diff --git a/PKG-INFO b/PKG-INFO
index 25717f1..656298e 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,23 +1,11 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: cffi
-Version: 1.14.0
+Version: 1.14.6
 Summary: Foreign Function Interface for Python calling C code.
 Home-page: http://cffi.readthedocs.org
 Author: Armin Rigo, Maciej Fijalkowski
 Author-email: python-cffi@googlegroups.com
 License: MIT
-Description: 
-        CFFI
-        ====
-        
-        Foreign Function Interface for Python calling C code.
-        Please see the `Documentation <http://cffi.readthedocs.org/>`_.
-        
-        Contact
-        -------
-        
-        `Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_
-        
 Platform: UNKNOWN
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
@@ -32,3 +20,18 @@ Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: License :: OSI Approved :: MIT License
+License-File: LICENSE
+
+
+CFFI
+====
+
+Foreign Function Interface for Python calling C code.
+Please see the `Documentation <http://cffi.readthedocs.org/>`_.
+
+Contact
+-------
+
+`Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_
+
+
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
index 7c9cd26..3e0351b 100644
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -2,7 +2,7 @@
 #include <Python.h>
 #include "structmember.h"
 
-#define CFFI_VERSION  "1.14.0"
+#define CFFI_VERSION  "1.14.6"
 
 #ifdef MS_WIN32
 #include <windows.h>
@@ -80,18 +80,46 @@
  * That sounds like a horribly bad idea to me, and is the reason for why
  * I prefer CFFI crashing cleanly.
  *
- * Currently, we use libffi's ffi_closure_alloc() only on NetBSD.  It is
+ * Currently, we use libffi's ffi_closure_alloc() on NetBSD.  It is
  * known that on the NetBSD kernel, a different strategy is used which
  * should not be open to the fork() bug.
+ *
+ * This is also used on macOS, provided we are executing on macOS 10.15 or
+ * above.  It's a mess because it needs runtime checks in that case.
  */
 #ifdef __NetBSD__
-# define CFFI_TRUST_LIBFFI
-#endif
 
-#ifndef CFFI_TRUST_LIBFFI
-# include "malloc_closure.h"
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC 1
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE 1
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC 1
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE 1
+# define CFFI_CHECK_FFI_PREP_CIF_VAR 0
+# define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 0
+
+#elif defined(__APPLE__) && defined(FFI_AVAILABLE_APPLE)
+
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE 1
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE 1
+# define CFFI_CHECK_FFI_PREP_CIF_VAR __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
+# define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 1
+
+#else
+
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC 0
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE 0
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC 0
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE 0
+# define CFFI_CHECK_FFI_PREP_CIF_VAR 0
+# define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 0
+
 #endif
 
+/* always includes this, even if it turns out not to be used on NetBSD
+   because calls are behind "if (0)" */
+#include "malloc_closure.h"
+
 
 #if PY_MAJOR_VERSION >= 3
 # define STR_OR_BYTES "bytes"
@@ -148,6 +176,14 @@
     (PyCObject_FromVoidPtr(pointer, destructor))
 #endif
 
+#if PY_VERSION_HEX < 0x030900a4
+# define Py_SET_REFCNT(obj, val) (Py_REFCNT(obj) = (val))
+#endif
+
+#if PY_VERSION_HEX >= 0x03080000
+# define USE_WRITEUNRAISABLEMSG
+#endif
+
 /************************************************************/
 
 /* base type flag: exactly one of the following: */
@@ -404,10 +440,10 @@ ctypedescr_dealloc(CTypeDescrObject *ct)
 
     if (ct->ct_unique_key != NULL) {
         /* revive dead object temporarily for DelItem */
-        Py_REFCNT(ct) = 43;
+        Py_SET_REFCNT(ct, 43);
         PyDict_DelItem(unique_cache, ct->ct_unique_key);
         assert(Py_REFCNT(ct) == 42);
-        Py_REFCNT(ct) = 0;
+        Py_SET_REFCNT(ct, 0);
         Py_DECREF(ct->ct_unique_key);
     }
     Py_XDECREF(ct->ct_itemdescr);
@@ -653,7 +689,7 @@ static PyMethodDef ctypedescr_methods[] = {
 
 static PyTypeObject CTypeDescr_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "_cffi_backend.CTypeDescr",
+    "_cffi_backend.CType",
     offsetof(CTypeDescrObject, ct_name),
     sizeof(char),
     (destructor)ctypedescr_dealloc,             /* tp_dealloc */
@@ -1886,11 +1922,12 @@ static void cdataowninggc_dealloc(CDataObject *cd)
         ffi_closure *closure = ((CDataObject_closure *)cd)->closure;
         PyObject *args = (PyObject *)(closure->user_data);
         Py_XDECREF(args);
-#ifdef CFFI_TRUST_LIBFFI
-        ffi_closure_free(closure);
-#else
-        cffi_closure_free(closure);
+#if CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE
+        if (CFFI_CHECK_FFI_CLOSURE_ALLOC) {
+            ffi_closure_free(closure);
+        } else
 #endif
+            cffi_closure_free(closure);
     }
     else {
         Py_FatalError("cdata CDataOwningGC_Type with unexpected type flags");
@@ -2888,7 +2925,8 @@ static PyObject *
 convert_struct_to_owning_object(char *data, CTypeDescrObject *ct); /*forward*/
 
 static cif_description_t *
-fb_prepare_cif(PyObject *fargs, CTypeDescrObject *, ffi_abi);      /*forward*/
+fb_prepare_cif(PyObject *fargs, CTypeDescrObject *, Py_ssize_t, ffi_abi);
+                                                                   /*forward*/
 
 static PyObject *new_primitive_type(const char *name);             /*forward*/
 
@@ -3081,7 +3119,7 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds)
 #else
         fabi = PyLong_AS_LONG(PyTuple_GET_ITEM(signature, 0));
 #endif
-        cif_descr = fb_prepare_cif(fvarargs, fresult, fabi);
+        cif_descr = fb_prepare_cif(fvarargs, fresult, nargs_declared, fabi);
         if (cif_descr == NULL)
             goto error;
     }
@@ -3354,7 +3392,7 @@ static PyMethodDef cdata_methods[] = {
 
 static PyTypeObject CData_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "_cffi_backend.CData",
+    "_cffi_backend._CDataBase",
     sizeof(CDataObject),
     0,
     (destructor)cdata_dealloc,                  /* tp_dealloc */
@@ -3373,7 +3411,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 +3436,7 @@ static PyTypeObject CData_Type = {
 
 static PyTypeObject CDataOwning_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "_cffi_backend.CDataOwn",
+    "_cffi_backend.__CDataOwn",
     sizeof(CDataObject),
     0,
     (destructor)cdataowning_dealloc,            /* tp_dealloc */
@@ -3415,7 +3455,8 @@ static PyTypeObject CDataOwning_Type = {
     0,  /* inherited */                         /* tp_setattro */
     0,                                          /* tp_as_buffer */
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */
-    0,                                          /* tp_doc */
+    "This is an internal subtype of _CDataBase for performance only on "
+    "CPython.  Check with isinstance(x, ffi.CData).",   /* tp_doc */
     0,                                          /* tp_traverse */
     0,                                          /* tp_clear */
     0,  /* inherited */                         /* tp_richcompare */
@@ -3438,7 +3479,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 +3499,8 @@ static PyTypeObject CDataOwningGC_Type = {
     0,                                          /* tp_as_buffer */
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES  /* tp_flags */
                        | Py_TPFLAGS_HAVE_GC,
-    0,                                          /* tp_doc */
+    "This is an internal subtype of _CDataBase for performance only on "
+    "CPython.  Check with isinstance(x, ffi.CData).",   /* tp_doc */
     (traverseproc)cdataowninggc_traverse,       /* tp_traverse */
     (inquiry)cdataowninggc_clear,               /* tp_clear */
     0,  /* inherited */                         /* tp_richcompare */
@@ -3481,7 +3523,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 +3543,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 +3567,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 +3590,8 @@ static PyTypeObject CDataGCP_Type = {
                        | Py_TPFLAGS_HAVE_FINALIZE
 #endif
                        | Py_TPFLAGS_HAVE_GC,
-    0,                                          /* tp_doc */
+    "This is an internal subtype of _CDataBase for performance only on "
+    "CPython.  Check with isinstance(x, ffi.CData).",   /* tp_doc */
     (traverseproc)cdatagcp_traverse,            /* tp_traverse */
     0,                                          /* tp_clear */
     0,  /* inherited */                         /* tp_richcompare */
@@ -3608,7 +3652,7 @@ cdataiter_dealloc(CDataIterObject *it)
 
 static PyTypeObject CDataIter_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "_cffi_backend.CDataIter",              /* tp_name */
+    "_cffi_backend.__CData_iterator",       /* tp_name */
     sizeof(CDataIterObject),                /* tp_basicsize */
     0,                                      /* tp_itemsize */
     /* methods */
@@ -4363,7 +4407,7 @@ static PyMethodDef dl_methods[] = {
 
 static PyTypeObject dl_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "_cffi_backend.Library",            /* tp_name */
+    "_cffi_backend.CLibrary",           /* tp_name */
     sizeof(DynLibObject),               /* tp_basicsize */
     0,                                  /* tp_itemsize */
     /* methods */
@@ -4441,9 +4485,11 @@ 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))
         {
+            Py_ssize_t sz1;
+            wchar_t *w1;
 #if PY_MAJOR_VERSION < 3
             s = PyUnicode_AsUTF8String(s);
             if (s == NULL)
@@ -4454,7 +4500,15 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename,
             if (*p_printable_filename == NULL)
                 return NULL;
 
-            handle = dlopenW(filenameW);
+            sz1 = PyUnicode_GetSize(filename_unicode) + 1;
+            sz1 *= 2;   /* should not be needed, but you never know */
+            w1 = alloca(sizeof(wchar_t) * sz1);
+            sz1 = PyUnicode_AsWideChar((PyUnicodeObject *)filename_unicode,
+                                       w1, sz1 - 1);
+            if (sz1 < 0)
+                return NULL;
+            w1[sz1] = 0;
+            handle = dlopenW(w1);
             goto got_handle;
         }
         PyErr_Clear();
@@ -4466,19 +4520,31 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename,
         if (PyUnicode_Check(s))
         {
             s = PyUnicode_AsUTF8String(s);
-            if (s == NULL)
+            if (s == NULL) {
+                PyMem_Free(filename_or_null);
                 return NULL;
+            }
             *p_temp = s;
         }
 #endif
         *p_printable_filename = PyText_AsUTF8(s);
-        if (*p_printable_filename == NULL)
+        if (*p_printable_filename == NULL) {
+            PyMem_Free(filename_or_null);
             return NULL;
+        }
     }
     if ((flags & (RTLD_NOW | RTLD_LAZY)) == 0)
         flags |= RTLD_NOW;
 
+#ifdef MS_WIN32
+    if (filename_or_null == NULL) {
+        PyErr_SetString(PyExc_OSError, "dlopen(None) not supported on Windows");
+        return NULL;
+    }
+#endif
+
     handle = dlopen(filename_or_null, flags);
+    PyMem_Free(filename_or_null);
 
 #ifdef MS_WIN32
   got_handle:
@@ -4995,7 +5061,9 @@ static int complete_sflags(int sflags)
 #ifdef MS_WIN32
         sflags |= SF_MSVC_BITFIELDS;
 #else
-# if defined(__arm__) || defined(__aarch64__)
+# if defined(__APPLE__) && defined(__arm64__)
+        sflags |= SF_GCC_X86_BITFIELDS;
+# elif defined(__arm__) || defined(__aarch64__)
         sflags |= SF_GCC_ARM_BITFIELDS;
 # else
         sflags |= SF_GCC_X86_BITFIELDS;
@@ -5783,11 +5851,14 @@ static CTypeDescrObject *fb_prepare_ctype(struct funcbuilder_s *fb,
 
 static cif_description_t *fb_prepare_cif(PyObject *fargs,
                                          CTypeDescrObject *fresult,
+                                         Py_ssize_t variadic_nargs_declared,
                                          ffi_abi fabi)
+
 {
     char *buffer;
     cif_description_t *cif_descr;
     struct funcbuilder_s funcbuffer;
+    ffi_status status = (ffi_status)-1;
 
     funcbuffer.nb_bytes = 0;
     funcbuffer.bufferp = NULL;
@@ -5810,8 +5881,24 @@ static cif_description_t *fb_prepare_cif(PyObject *fargs,
     assert(funcbuffer.bufferp == buffer + funcbuffer.nb_bytes);
 
     cif_descr = (cif_description_t *)buffer;
-    if (ffi_prep_cif(&cif_descr->cif, fabi, funcbuffer.nargs,
-                     funcbuffer.rtype, funcbuffer.atypes) != FFI_OK) {
+
+    /* use `ffi_prep_cif_var` if necessary and available */
+#if CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE
+    if (variadic_nargs_declared >= 0) {
+        if (CFFI_CHECK_FFI_PREP_CIF_VAR) {
+            status = ffi_prep_cif_var(&cif_descr->cif, fabi,
+                                      variadic_nargs_declared, funcbuffer.nargs,
+                                      funcbuffer.rtype, funcbuffer.atypes);
+        }
+    }
+#endif
+
+    if (status == (ffi_status)-1) {
+        status = ffi_prep_cif(&cif_descr->cif, fabi, funcbuffer.nargs,
+                              funcbuffer.rtype, funcbuffer.atypes);
+    }
+
+    if (status != FFI_OK) {
         PyErr_SetString(PyExc_SystemError,
                         "libffi failed to build this function type");
         goto error;
@@ -5855,7 +5942,7 @@ static PyObject *new_function_type(PyObject *fargs,   /* tuple */
            is computed here. */
         cif_description_t *cif_descr;
 
-        cif_descr = fb_prepare_cif(fargs, fresult, fabi);
+        cif_descr = fb_prepare_cif(fargs, fresult, -1, fabi);
         if (cif_descr == NULL) {
             if (PyErr_ExceptionMatches(PyExc_NotImplementedError)) {
                 PyErr_Clear();   /* will get the exception if we see an
@@ -5983,6 +6070,43 @@ static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb,
                                       char *extra_error_line)
 {
     /* like PyErr_WriteUnraisable(), but write a full traceback */
+#ifdef USE_WRITEUNRAISABLEMSG
+
+    /* PyErr_WriteUnraisable actually writes the full traceback anyway
+       from Python 3.4, but we can't really get the formatting of the
+       custom text to be what we want.  We can do better from Python
+       3.8 by calling the new _PyErr_WriteUnraisableMsg().
+       Luckily it's also Python 3.8 that adds new functionality that
+       people might want: the new sys.unraisablehook().
+    */
+    PyObject *s;
+    int first_char;
+    assert(objdescr != NULL && objdescr[0] != 0);   /* non-empty */
+    first_char = objdescr[0];
+    if (first_char >= 'A' && first_char <= 'Z')
+        first_char += 'a' - 'A';    /* lower() the very first character */
+    if (extra_error_line == NULL)
+        extra_error_line = "";
+
+    if (obj != NULL)
+        s = PyUnicode_FromFormat("%c%s%R%s",
+            first_char, objdescr + 1, obj, extra_error_line);
+    else
+        s = PyUnicode_FromFormat("%c%s%s",
+            first_char, objdescr + 1, extra_error_line);
+
+    PyErr_Restore(t, v, tb);
+    if (s != NULL) {
+        _PyErr_WriteUnraisableMsg(PyText_AS_UTF8(s), NULL);
+        Py_DECREF(s);
+    }
+    else
+        PyErr_WriteUnraisable(obj);   /* best effort */
+    PyErr_Clear();
+
+#else
+
+    /* version for Python 2.7 and < 3.8 */
     PyObject *f;
 #if PY_MAJOR_VERSION >= 3
     /* jump through hoops to ensure the tb is attached to v, on Python 3 */
@@ -6007,6 +6131,8 @@ static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb,
     Py_XDECREF(t);
     Py_XDECREF(v);
     Py_XDECREF(tb);
+
+#endif
 }
 
 static void general_invoke_callback(int decode_args_from_libffi,
@@ -6056,7 +6182,11 @@ static void general_invoke_callback(int decode_args_from_libffi,
         goto error;
     if (convert_from_object_fficallback(result, SIGNATURE(1), py_res,
                                         decode_args_from_libffi) < 0) {
+#ifdef USE_WRITEUNRAISABLEMSG
+        extra_error_line = ", trying to convert the result back to C";
+#else
         extra_error_line = "Trying to convert the result back to C:\n";
+#endif
         goto error;
     }
  done:
@@ -6108,10 +6238,16 @@ static void general_invoke_callback(int decode_args_from_libffi,
             _my_PyErr_WriteUnraisable(exc1, val1, tb1,
                                       "From cffi callback ", py_ob,
                                       extra_error_line);
+#ifdef USE_WRITEUNRAISABLEMSG
+            _my_PyErr_WriteUnraisable(exc2, val2, tb2,
+                 "during handling of the above exception by 'onerror'",
+                 NULL, NULL);
+#else
             extra_error_line = ("\nDuring the call to 'onerror', "
                                 "another exception occurred:\n\n");
             _my_PyErr_WriteUnraisable(exc2, val2, tb2,
                                       NULL, NULL, extra_error_line);
+#endif
             _cffi_stop_error_capture(ecap);
         }
     }
@@ -6179,15 +6315,28 @@ static PyObject *prepare_callback_info_tuple(CTypeDescrObject *ct,
     infotuple = Py_BuildValue("OOOO", ct, ob, py_rawerr, onerror_ob);
     Py_DECREF(py_rawerr);
 
-#ifdef WITH_THREAD
+#if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000
     /* We must setup the GIL here, in case the callback is invoked in
-       some other non-Pythonic thread.  This is the same as ctypes. */
+       some other non-Pythonic thread.  This is the same as ctypes.
+       But PyEval_InitThreads() is always a no-op from CPython 3.7
+       (the call from ctypes was removed some time later I think). */
     PyEval_InitThreads();
 #endif
 
     return infotuple;
 }
 
+/* messily try to silence a gcc/clang deprecation warning for
+   ffi_prep_closure.  Don't miss the "pragma pop" after the function.
+   This is done around the whole function because very old GCCs don't
+   support it inside a function. */
+#if defined(__clang__)
+#  pragma clang diagnostic push
+#  pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(__GNUC__)
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
 static PyObject *b_callback(PyObject *self, PyObject *args)
 {
     CTypeDescrObject *ct;
@@ -6196,6 +6345,7 @@ static PyObject *b_callback(PyObject *self, PyObject *args)
     PyObject *infotuple;
     cif_description_t *cif_descr;
     ffi_closure *closure;
+    ffi_status status;
     void *closure_exec;
 
     if (!PyArg_ParseTuple(args, "O!O|OO:callback", &CTypeDescr_Type, &ct, &ob,
@@ -6206,12 +6356,16 @@ static PyObject *b_callback(PyObject *self, PyObject *args)
     if (infotuple == NULL)
         return NULL;
 
-#ifdef CFFI_TRUST_LIBFFI
-    closure = ffi_closure_alloc(sizeof(ffi_closure), &closure_exec);
-#else
-    closure = cffi_closure_alloc();
-    closure_exec = closure;
+#if CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE
+    if (CFFI_CHECK_FFI_CLOSURE_ALLOC) {
+        closure = ffi_closure_alloc(sizeof(ffi_closure), &closure_exec);
+    } else
 #endif
+    {
+        closure = cffi_closure_alloc();
+        closure_exec = closure;
+    }
+
     if (closure == NULL) {
         Py_DECREF(infotuple);
         PyErr_SetString(PyExc_MemoryError,
@@ -6228,8 +6382,8 @@ static PyObject *b_callback(PyObject *self, PyObject *args)
     cd->head.c_type = ct;
     cd->head.c_data = (char *)closure_exec;
     cd->head.c_weakreflist = NULL;
+    closure->user_data = NULL;
     cd->closure = closure;
-    PyObject_GC_Track(cd);
 
     cif_descr = (cif_description_t *)ct->ct_extra;
     if (cif_descr == NULL) {
@@ -6238,17 +6392,30 @@ static PyObject *b_callback(PyObject *self, PyObject *args)
                      "return type or with '...'", ct->ct_name);
         goto error;
     }
-#ifdef CFFI_TRUST_LIBFFI
-    if (ffi_prep_closure_loc(closure, &cif_descr->cif,
-                         invoke_callback, infotuple, closure_exec) != FFI_OK) {
+
+#if CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE
+    if (CFFI_CHECK_FFI_PREP_CLOSURE_LOC) {
+        status = ffi_prep_closure_loc(closure, &cif_descr->cif,
+                                      invoke_callback, infotuple, closure_exec);
+    }
+    else
+#endif
+    {
+#if defined(__APPLE__) && defined(FFI_AVAILABLE_APPLE) && !FFI_LEGACY_CLOSURE_API
+        PyErr_Format(PyExc_SystemError, "ffi_prep_closure_loc() is missing");
+        goto error;
 #else
-    if (ffi_prep_closure(closure, &cif_descr->cif,
-                         invoke_callback, infotuple) != FFI_OK) {
+        status = ffi_prep_closure(closure, &cif_descr->cif,
+                                  invoke_callback, infotuple);
 #endif
+    }
+
+    if (status != FFI_OK) {
         PyErr_SetString(PyExc_SystemError,
                         "libffi failed to build this callback");
         goto error;
     }
+
     if (closure->user_data != infotuple) {
         /* Issue #266.  Should not occur, but could, if we are using
            at runtime a version of libffi compiled with a different
@@ -6263,22 +6430,30 @@ static PyObject *b_callback(PyObject *self, PyObject *args)
             "different from the 'ffi.h' file seen at compile-time)");
         goto error;
     }
+    PyObject_GC_Track(cd);
     return (PyObject *)cd;
 
  error:
     closure->user_data = NULL;
     if (cd == NULL) {
-#ifdef CFFI_TRUST_LIBFFI
-        ffi_closure_free(closure);
-#else
-        cffi_closure_free(closure);
+#if CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE
+        if (CFFI_CHECK_FFI_CLOSURE_ALLOC) {
+            ffi_closure_free(closure);
+        }
+        else
 #endif
+            cffi_closure_free(closure);
     }
     else
         Py_DECREF(cd);
     Py_XDECREF(infotuple);
     return NULL;
 }
+#if defined(__clang__)
+#  pragma clang diagnostic pop
+#elif defined(__GNUC__)
+#  pragma GCC diagnostic pop
+#endif
 
 static PyObject *b_new_enum_type(PyObject *self, PyObject *args)
 {
@@ -7788,6 +7963,22 @@ init_cffi_backend(void)
     PyObject *m, *v;
     int i;
     static char init_done = 0;
+    static PyTypeObject *all_types[] = {
+        &dl_type,
+        &CTypeDescr_Type,
+        &CField_Type,
+        &CData_Type,
+        &CDataOwning_Type,
+        &CDataOwningGC_Type,
+        &CDataFromBuf_Type,
+        &CDataGCP_Type,
+        &CDataIter_Type,
+        &MiniBuffer_Type,
+        &FFI_Type,
+        &Lib_Type,
+        &GlobSupport_Type,
+        NULL
+    };
 
     v = PySys_GetObject("version");
     if (v == NULL || !PyText_Check(v) ||
@@ -7813,26 +8004,22 @@ init_cffi_backend(void)
             INITERROR;
     }
 
-    if (PyType_Ready(&dl_type) < 0)
-        INITERROR;
-    if (PyType_Ready(&CTypeDescr_Type) < 0)
-        INITERROR;
-    if (PyType_Ready(&CField_Type) < 0)
-        INITERROR;
-    if (PyType_Ready(&CData_Type) < 0)
-        INITERROR;
-    if (PyType_Ready(&CDataOwning_Type) < 0)
-        INITERROR;
-    if (PyType_Ready(&CDataOwningGC_Type) < 0)
-        INITERROR;
-    if (PyType_Ready(&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");
@@ -7878,10 +8065,6 @@ init_cffi_backend(void)
             INITERROR;
     }
 
-    Py_INCREF(&MiniBuffer_Type);
-    if (PyModule_AddObject(m, "buffer", (PyObject *)&MiniBuffer_Type) < 0)
-        INITERROR;
-
     init_cffi_tls();
     if (PyErr_Occurred())
         INITERROR;
diff --git a/c/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..f154146 100644
--- a/c/ffi_obj.c
+++ b/c/ffi_obj.c
@@ -1070,10 +1070,10 @@ static PyObject *ffi_init_once(FFIObject *self, PyObject *args, PyObject *kwds)
         if (res != NULL) {
             tup = PyTuple_Pack(2, Py_True, res);
             if (tup == NULL || PyDict_SetItem(cache, tag, tup) < 0) {
-                Py_XDECREF(tup);
                 Py_DECREF(res);
                 res = NULL;
             }
+            Py_XDECREF(tup);
         }
     }
 
@@ -1137,7 +1137,7 @@ static PyGetSetDef ffi_getsets[] = {
 
 static PyTypeObject FFI_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "CompiledFFI",
+    "_cffi_backend.FFI",
     sizeof(FFIObject),
     0,
     (destructor)ffi_dealloc,                    /* tp_dealloc */
diff --git a/c/lib_obj.c b/c/lib_obj.c
index 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/libffi_msvc/ffi.c b/c/libffi_msvc/ffi.c
index 836f171..b9e324f 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;
@@ -287,16 +289,12 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp)
 	    _asm fld DWORD PTR [eax] ;
 //      asm ("flds (%0)" : : "r" (resp) : "st" );
     }
-  else if (rtype == FFI_TYPE_DOUBLE)
+  else if (rtype == FFI_TYPE_DOUBLE || rtype == FFI_TYPE_LONGDOUBLE)
     {
 	    _asm mov eax, resp ;
 	    _asm fld QWORD PTR [eax] ;
 //      asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" );
     }
-  else if (rtype == FFI_TYPE_LONGDOUBLE)
-    {
-//      asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" );
-    }
   else if (rtype == FFI_TYPE_SINT64)
     {
 	    _asm mov edx, resp ;
@@ -307,6 +305,10 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp)
 //	   : : "r"(resp)
 //	   : "eax", "edx");
     }
+  else if (rtype == FFI_TYPE_STRUCT)
+    {
+	    _asm mov eax, resp ;
+    }
 #else
   /* now, do a generic return based on the value of rtype */
   if (rtype == FFI_TYPE_INT)
@@ -317,14 +319,10 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp)
     {
       asm ("flds (%0)" : : "r" (resp) : "st" );
     }
-  else if (rtype == FFI_TYPE_DOUBLE)
+  else if (rtype == FFI_TYPE_DOUBLE || rtype == FFI_TYPE_LONGDOUBLE)
     {
       asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" );
     }
-  else if (rtype == FFI_TYPE_LONGDOUBLE)
-    {
-      asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" );
-    }
   else if (rtype == FFI_TYPE_SINT64)
     {
       asm ("movl 0(%0),%%eax;"
@@ -332,6 +330,10 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp)
 	   : : "r"(resp)
 	   : "eax", "edx");
     }
+  else if (rtype == FFI_TYPE_STRUCT)
+    {
+      asm ("movl %0,%%eax" : : "r" (resp) : "eax");
+    }
 #endif
 #endif
 
@@ -340,6 +342,8 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp)
      result types except for floats; we have to 'mov xmm0, rax' in the
      caller to correct this.
   */
+  if (rtype == FFI_TYPE_STRUCT)
+      return resp;
   return *(void **)resp;
 #endif
 }
@@ -378,7 +382,7 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
       /* because we're little endian, this is what it turns into.   */
 
 #ifdef _WIN64
-      if (z > 8)
+      if (z != 1 && z != 2 && z != 4 && z != 8)
         {
           /* On Win64, if a single argument takes more than 8 bytes,
              then it is always passed by reference. */
@@ -447,6 +451,11 @@ ffi_prep_closure_loc (ffi_closure* closure,
        || cif->arg_types[3]->type == FFI_TYPE_DOUBLE))
     mask |= 8;
 
+  /* if we return a non-small struct, then the first argument is a pointer
+   * to the return area, and all real arguments are shifted by one */
+  if (cif->flags == FFI_TYPE_STRUCT)
+    mask = (mask & ~8) << 1;
+
   /* 41 BB ----         mov         r11d,mask */
   BYTES("\x41\xBB"); INT(mask);
 
diff --git a/c/libffi_msvc/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)
diff --git a/c/test_c.py b/c/test_c.py
index 3b34999..cd8def7 100644
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -3,18 +3,21 @@ import pytest
 
 def _setup_path():
     import os, sys
-    if '__pypy__' in sys.builtin_module_names:
-        py.test.skip("_cffi_backend.c: not tested on top of pypy, "
-                     "use pypy/module/_cffi_backend/test/ instead.")
     sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
 _setup_path()
 from _cffi_backend import *
-from _cffi_backend import _testfunc, _get_types, _get_common_types, __version__
+from _cffi_backend import _get_types, _get_common_types
+try:
+    from _cffi_backend import _testfunc
+except ImportError:
+    def _testfunc(num):
+        pytest.skip("_testunc() not available")
+from _cffi_backend import __version__
 
 # ____________________________________________________________
 
 import sys
-assert __version__ == "1.14.0", ("This test_c.py file is for testing a version"
+assert __version__ == "1.14.6", ("This test_c.py file is for testing a version"
                                  " of cffi that differs from the one that we"
                                  " get from 'import _cffi_backend'")
 if sys.version_info < (3,):
@@ -65,8 +68,10 @@ def find_and_load_library(name, flags=RTLD_NOW):
         path = ctypes.util.find_library(name)
         if path is None and name == 'c':
             assert sys.platform == 'win32'
-            assert sys.version_info >= (3,)
-            py.test.skip("dlopen(None) cannot work on Windows with Python 3")
+            assert (sys.version_info >= (3,) or
+                    '__pypy__' in sys.builtin_module_names)
+            py.test.skip("dlopen(None) cannot work on Windows "
+                         "with PyPy or Python 3")
     return load_library(path, flags)
 
 def test_load_library():
@@ -109,7 +114,7 @@ def test_cast_to_signed_char():
     p = new_primitive_type("signed char")
     x = cast(p, -65 + 17*256)
     assert repr(x) == "<cdata 'signed char' -65>"
-    assert repr(type(x)) == "<%s '_cffi_backend.CData'>" % type_or_class
+    assert repr(type(x)) == "<%s '_cffi_backend._CDataBase'>" % type_or_class
     assert int(x) == -65
     x = cast(p, -66 + (1<<199)*256)
     assert repr(x) == "<cdata 'signed char' -66>"
@@ -1326,7 +1331,9 @@ def test_callback_exception():
     except ImportError:
         import io as cStringIO    # Python 3
     import linecache
-    def matches(istr, ipattern):
+    def matches(istr, ipattern, ipattern38):
+        if sys.version_info >= (3, 8):
+            ipattern = ipattern38
         str, pattern = istr, ipattern
         while '$' in pattern:
             i = pattern.index('$')
@@ -1359,6 +1366,8 @@ def test_callback_exception():
     try:
         linecache.getline = lambda *args: 'LINE'    # hack: speed up PyPy tests
         sys.stderr = cStringIO.StringIO()
+        if hasattr(sys, '__unraisablehook__'):          # work around pytest
+            sys.unraisablehook = sys.__unraisablehook__ # on recent CPythons
         assert f(100) == 300
         assert sys.stderr.getvalue() == ''
         assert f(10000) == -42
@@ -1370,6 +1379,14 @@ Traceback (most recent call last):
   File "$", line $, in check_value
     $
 ValueError: 42
+""", """\
+Exception ignored from cffi callback <function$Zcb1 at 0x$>:
+Traceback (most recent call last):
+  File "$", line $, in Zcb1
+    $
+  File "$", line $, in check_value
+    $
+ValueError: 42
 """)
         sys.stderr = cStringIO.StringIO()
         bigvalue = 20000
@@ -1378,6 +1395,12 @@ ValueError: 42
 From cffi callback <function$Zcb1 at 0x$>:
 Trying to convert the result back to C:
 OverflowError: integer 60000 does not fit 'short'
+""", """\
+Exception ignored from cffi callback <function$Zcb1 at 0x$>, trying to convert the result back to C:
+Traceback (most recent call last):
+  File "$", line $, in test_callback_exception
+    $
+OverflowError: integer 60000 does not fit 'short'
 """)
         sys.stderr = cStringIO.StringIO()
         bigvalue = 20000
@@ -1414,12 +1437,25 @@ OverflowError: integer 60000 does not fit 'short'
 
 During the call to 'onerror', another exception occurred:
 
+TypeError: $integer$
+""", """\
+Exception ignored from cffi callback <function$Zcb1 at 0x$>, trying to convert the result back to C:
+Traceback (most recent call last):
+  File "$", line $, in test_callback_exception
+    $
+OverflowError: integer 60000 does not fit 'short'
+Exception ignored during handling of the above exception by 'onerror':
+Traceback (most recent call last):
+  File "$", line $, in test_callback_exception
+    $
 TypeError: $integer$
 """)
         #
         sys.stderr = cStringIO.StringIO()
         seen = "not a list"    # this makes the oops() function crash
         assert ff(bigvalue) == -42
+        # the $ after the AttributeError message are for the suggestions that
+        # will be added in Python 3.10
         assert matches(sys.stderr.getvalue(), """\
 From cffi callback <function$Zcb1 at 0x$>:
 Trying to convert the result back to C:
@@ -1430,7 +1466,18 @@ During the call to 'onerror', another exception occurred:
 Traceback (most recent call last):
   File "$", line $, in oops
     $
-AttributeError: 'str' object has no attribute 'append'
+AttributeError: 'str' object has no attribute 'append$
+""", """\
+Exception ignored from cffi callback <function$Zcb1 at 0x$>, trying to convert the result back to C:
+Traceback (most recent call last):
+  File "$", line $, in test_callback_exception
+    $
+OverflowError: integer 60000 does not fit 'short'
+Exception ignored during handling of the above exception by 'onerror':
+Traceback (most recent call last):
+  File "$", line $, in oops
+    $
+AttributeError: 'str' object has no attribute 'append$
 """)
     finally:
         sys.stderr = orig_stderr
@@ -1465,7 +1512,7 @@ def test_a_lot_of_callbacks():
     def make_callback(m):
         def cb(n):
             return n + m
-        return callback(BFunc, cb, 42)    # 'cb' and 'BFunc' go out of scope
+        return callback(BFunc, cb, 42)    # 'cb' goes out of scope
     #
     flist = [make_callback(i) for i in range(BIGNUM)]
     for i, f in enumerate(flist):
@@ -2548,8 +2595,8 @@ def test_errno():
     assert get_errno() == 95
 
 def test_errno_callback():
-    if globals().get('PY_DOT_PY') == '2.5':
-        py.test.skip("cannot run this test on py.py with Python 2.5")
+    if globals().get('PY_DOT_PY'):
+        py.test.skip("cannot run this test on py.py (e.g. fails on Windows)")
     set_errno(95)
     def cb():
         e = get_errno()
@@ -3969,6 +4016,20 @@ def test_from_buffer_types():
     with pytest.raises(ValueError):
         release(pv[0])
 
+def test_issue483():
+    BInt = new_primitive_type("int")
+    BIntP = new_pointer_type(BInt)
+    BIntA = new_array_type(BIntP, None)
+    lst = list(range(25))
+    bytestring = bytearray(buffer(newp(BIntA, lst))[:] + b'XYZ')
+    p1 = from_buffer(BIntA, bytestring)      # int[]
+    assert len(buffer(p1)) == 25 * size_of_int()
+    assert sizeof(p1) == 25 * size_of_int()
+    #
+    p2 = from_buffer(BIntP, bytestring)
+    assert sizeof(p2) == size_of_ptr()
+    assert len(buffer(p2)) == size_of_int()  # first element only, by default
+
 def test_memmove():
     Short = new_primitive_type("short")
     ShortA = new_array_type(new_pointer_type(Short), None)
@@ -4453,3 +4514,62 @@ 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_get_types():
+    import _cffi_backend
+    CData, CType = _get_types()
+    assert CData is _cffi_backend._CDataBase
+    assert CType is _cffi_backend.CType
+
+def test_type_available_with_correct_names():
+    import _cffi_backend
+    check_names = [
+        'CType',
+        'CField',
+        'CLibrary',
+        '_CDataBase',
+        'FFI',
+        'Lib',
+        'buffer',
+    ]
+    if '__pypy__' in sys.builtin_module_names:
+        check_names += [
+            '__CData_iterator',
+            '__FFIGlobSupport',
+            '__FFIAllocator',
+            '__FFIFunctionWrapper',
+        ]
+    else:
+        check_names += [
+            '__CDataOwn',
+            '__CDataOwnGC',
+            '__CDataFromBuf',
+            '__CDataGCP',
+            '__CData_iterator',
+            '__FFIGlobSupport',
+        ]
+    for name in check_names:
+        tp = getattr(_cffi_backend, name)
+        assert isinstance(tp, type)
+        assert (tp.__module__, tp.__name__) == ('_cffi_backend', name)
+
+def test_unaligned_types():
+    BByteArray = new_array_type(
+        new_pointer_type(new_primitive_type("unsigned char")), None)
+    pbuf = newp(BByteArray, 40)
+    buf = buffer(pbuf)
+    #
+    for name in ['short', 'int', 'long', 'long long', 'float', 'double',
+                 'float _Complex', 'double _Complex']:
+        p = new_primitive_type(name)
+        if name.endswith(' _Complex'):
+            num = cast(p, 1.23 - 4.56j)
+        else:
+            num = cast(p, 0x0123456789abcdef)
+        size = sizeof(p)
+        buf[0:40] = b"\x00" * 40
+        pbuf1 = cast(new_pointer_type(p), pbuf + 1)
+        pbuf1[0] = num
+        assert pbuf1[0] == num
+        assert buf[0] == b'\x00'
+        assert buf[1 + size] == b'\x00'
diff --git a/cffi.egg-info/PKG-INFO b/cffi.egg-info/PKG-INFO
index 25717f1..656298e 100644
--- a/cffi.egg-info/PKG-INFO
+++ b/cffi.egg-info/PKG-INFO
@@ -1,23 +1,11 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: cffi
-Version: 1.14.0
+Version: 1.14.6
 Summary: Foreign Function Interface for Python calling C code.
 Home-page: http://cffi.readthedocs.org
 Author: Armin Rigo, Maciej Fijalkowski
 Author-email: python-cffi@googlegroups.com
 License: MIT
-Description: 
-        CFFI
-        ====
-        
-        Foreign Function Interface for Python calling C code.
-        Please see the `Documentation <http://cffi.readthedocs.org/>`_.
-        
-        Contact
-        -------
-        
-        `Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_
-        
 Platform: UNKNOWN
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
@@ -32,3 +20,18 @@ Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: License :: OSI Approved :: MIT License
+License-File: LICENSE
+
+
+CFFI
+====
+
+Foreign Function Interface for Python calling C code.
+Please see the `Documentation <http://cffi.readthedocs.org/>`_.
+
+Contact
+-------
+
+`Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_
+
+
diff --git a/cffi.egg-info/SOURCES.txt b/cffi.egg-info/SOURCES.txt
index 6920aa6..dce3999 100644
--- a/cffi.egg-info/SOURCES.txt
+++ b/cffi.egg-info/SOURCES.txt
@@ -154,6 +154,7 @@ testing/cffi1/test_commontypes.py
 testing/cffi1/test_dlopen.py
 testing/cffi1/test_dlopen_unicode_literals.py
 testing/cffi1/test_ffi_obj.py
+testing/cffi1/test_function_args.py
 testing/cffi1/test_new_ffi_1.py
 testing/cffi1/test_parse_c_type.py
 testing/cffi1/test_pkgconfig.py
@@ -185,4 +186,5 @@ testing/embedding/thread1-test.c
 testing/embedding/thread2-test.c
 testing/embedding/thread3-test.c
 testing/embedding/tlocal-test.c
-testing/embedding/tlocal.py
\ No newline at end of file
+testing/embedding/tlocal.py
+testing/embedding/withunicode.py
\ No newline at end of file
diff --git a/cffi/__init__.py b/cffi/__init__.py
index 7969abf..cdc26f0 100644
--- a/cffi/__init__.py
+++ b/cffi/__init__.py
@@ -5,8 +5,8 @@ from .api import FFI
 from .error import CDefError, FFIError, VerificationError, VerificationMissing
 from .error import PkgConfigError
 
-__version__ = "1.14.0"
-__version_info__ = (1, 14, 0)
+__version__ = "1.14.6"
+__version_info__ = (1, 14, 6)
 
 # The verifier module file names are based on the CRC32 of a string that
 # contains the following version number.  It may be older than __version__
diff --git a/cffi/_cffi_errors.h b/cffi/_cffi_errors.h
index 83cdad0..158e059 100644
--- a/cffi/_cffi_errors.h
+++ b/cffi/_cffi_errors.h
@@ -54,6 +54,8 @@ static PyObject *_cffi_start_error_capture(void)
         "      of.write(x)\n"
         "    except: pass\n"
         "    self.buf += x\n"
+        "  def flush(self):\n"
+        "    pass\n"
         "fl = FileLike()\n"
         "fl.buf = ''\n"
         "of = sys.stderr\n"
diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h
index 3129150..e4c0a67 100644
--- a/cffi/_cffi_include.h
+++ b/cffi/_cffi_include.h
@@ -8,20 +8,49 @@
    the same works for the other two macros.  Py_DEBUG implies them,
    but not the other way around.
 
-   Issue #350 is still open: on Windows, the code here causes it to link
-   with PYTHON36.DLL (for example) instead of PYTHON3.DLL.  A fix was
-   attempted in 164e526a5515 and 14ce6985e1c3, but reverted: virtualenv
-   does not make PYTHON3.DLL available, and so the "correctly" compiled
-   version would not run inside a virtualenv.  We will re-apply the fix
-   after virtualenv has been fixed for some time.  For explanation, see
-   issue #355.  For a workaround if you want PYTHON3.DLL and don't worry
-   about virtualenv, see issue #350.  See also 'py_limited_api' in
-   setuptools_ext.py.
+   The implementation is messy (issue #350): on Windows, with _MSC_VER,
+   we have to define Py_LIMITED_API even before including pyconfig.h.
+   In that case, we guess what pyconfig.h will do to the macros above,
+   and check our guess after the #include.
+
+   Note that on Windows, with CPython 3.x, you need >= 3.5 and virtualenv
+   version >= 16.0.0.  With older versions of either, you don't get a
+   copy of PYTHON3.DLL in the virtualenv.  We can't check the version of
+   CPython *before* we even include pyconfig.h.  ffi.set_source() puts
+   a ``#define _CFFI_NO_LIMITED_API'' at the start of this file if it is
+   running on Windows < 3.5, as an attempt at fixing it, but that's
+   arguably wrong because it may not be the target version of Python.
+   Still better than nothing I guess.  As another workaround, you can
+   remove the definition of Py_LIMITED_API here.
+
+   See also 'py_limited_api' in cffi/setuptools_ext.py.
 */
 #if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API)
-#  include <pyconfig.h>
-#  if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG)
-#    define Py_LIMITED_API
+#  ifdef _MSC_VER
+#    if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API)
+#      define Py_LIMITED_API
+#    endif
+#    include <pyconfig.h>
+     /* sanity-check: Py_LIMITED_API will cause crashes if any of these
+        are also defined.  Normally, the Python file PC/pyconfig.h does not
+        cause any of these to be defined, with the exception that _DEBUG
+        causes Py_DEBUG.  Double-check that. */
+#    ifdef Py_LIMITED_API
+#      if defined(Py_DEBUG)
+#        error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set"
+#      endif
+#      if defined(Py_TRACE_REFS)
+#        error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set"
+#      endif
+#      if defined(Py_REF_DEBUG)
+#        error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set"
+#      endif
+#    endif
+#  else
+#    include <pyconfig.h>
+#    if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API)
+#      define Py_LIMITED_API
+#    endif
 #  endif
 #endif
 
diff --git a/cffi/_embedding.h b/cffi/_embedding.h
index 34a4a66..7410231 100644
--- a/cffi/_embedding.h
+++ b/cffi/_embedding.h
@@ -224,7 +224,7 @@ static int _cffi_initialize_python(void)
 
         if (f != NULL && f != Py_None) {
             PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME
-                               "\ncompiled with cffi version: 1.14.0"
+                               "\ncompiled with cffi version: 1.14.6"
                                "\n_cffi_backend module: ", f);
             modules = PyImport_GetModuleDict();
             mod = PyDict_GetItemString(modules, "_cffi_backend");
@@ -246,7 +246,9 @@ static int _cffi_initialize_python(void)
     goto done;
 }
 
+#if PY_VERSION_HEX < 0x03080000
 PyAPI_DATA(char *) _PyParser_TokenNames[];  /* from CPython */
+#endif
 
 static int _cffi_carefully_make_gil(void)
 {
@@ -329,15 +331,20 @@ static int _cffi_carefully_make_gil(void)
     /* call Py_InitializeEx() */
     if (!Py_IsInitialized()) {
         _cffi_py_initialize();
+#if PY_VERSION_HEX < 0x03070000
         PyEval_InitThreads();
+#endif
         PyEval_SaveThread();  /* release the GIL */
         /* the returned tstate must be the one that has been stored into the
            autoTLSkey by _PyGILState_Init() called from Py_Initialize(). */
     }
     else {
+#if PY_VERSION_HEX < 0x03070000
+        /* PyEval_InitThreads() is always a no-op from CPython 3.7 */
         PyGILState_STATE state = PyGILState_Ensure();
         PyEval_InitThreads();
         PyGILState_Release(state);
+#endif
     }
 
 #ifdef WITH_THREAD
@@ -361,11 +368,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,
 };
 
diff --git a/cffi/cparser.py b/cffi/cparser.py
index ea27c48..74830e9 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|\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*\]")
@@ -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, or '#NUMBER'.  This
+    # function stores them away and replaces them with exactly the string
+    # '#line@N', where N is the index in the list 'line_directives'.
+    line_directives = []
+    def replace(m):
+        i = len(line_directives)
+        line_directives.append(m.group())
+        return '#line@%d' % i
+    csource = _r_line_directive.sub(replace, csource)
+    return csource, line_directives
+
+def _put_back_line_directives(csource, line_directives):
+    def replace(m):
+        s = m.group()
+        if not s.startswith('#line@'):
+            raise AssertionError("unexpected #line directive "
+                                 "(should have been processed and removed")
+        return line_directives[int(s[6:])]
+    return _r_line_directive.sub(replace, csource)
+
 def _preprocess(csource):
+    # First, remove the lines of the form '#line N "filename"' because
+    # the "filename" part could confuse the rest
+    csource, line_directives = _remove_line_directives(csource)
     # Remove comments.  NOTE: this only work because the cdef() section
-    # should not contain any string 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
@@ -395,7 +426,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 +594,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 +612,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/recompiler.py b/cffi/recompiler.py
index d66ff7f..86b37d7 100644
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -7,6 +7,9 @@ VERSION_BASE = 0x2601
 VERSION_EMBEDDED = 0x2701
 VERSION_CHAR16CHAR32 = 0x2801
 
+USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or
+                   sys.version_info >= (3, 5))
+
 
 class GlobalExpr:
     def __init__(self, name, address, type_op, size=0, check_value=0):
@@ -190,6 +193,17 @@ class Recompiler:
             assert isinstance(op, CffiOp)
         self.cffi_types = tuple(self.cffi_types)    # don't change any more
 
+    def _enum_fields(self, tp):
+        # When producing C, expand all anonymous struct/union fields.
+        # That's necessary to have C code checking the offsets of the
+        # individual fields contained in them.  When producing Python,
+        # don't do it and instead write it like it is, with the
+        # corresponding fields having an empty name.  Empty names are
+        # recognized at runtime when we import the generated Python
+        # file.
+        expand_anonymous_struct_union = not self.target_is_python
+        return tp.enumfields(expand_anonymous_struct_union)
+
     def _do_collect_type(self, tp):
         if not isinstance(tp, model.BaseTypeByIdentity):
             if isinstance(tp, tuple):
@@ -203,7 +217,7 @@ class Recompiler:
             elif isinstance(tp, model.StructOrUnion):
                 if tp.fldtypes is not None and (
                         tp not in self.ffi._parser._included_declarations):
-                    for name1, tp1, _, _ in tp.enumfields():
+                    for name1, tp1, _, _ in self._enum_fields(tp):
                         self._do_collect_type(self._field_type(tp, name1, tp1))
             else:
                 for _, x in tp._get_items():
@@ -283,6 +297,8 @@ class Recompiler:
         prnt = self._prnt
         if self.ffi._embedding is not None:
             prnt('#define _CFFI_USE_EMBEDDING')
+        if not USE_LIMITED_API:
+            prnt('#define _CFFI_NO_LIMITED_API')
         #
         # first the '#include' (actually done by inlining the file's content)
         lines = self._rel_readlines('_cffi_include.h')
@@ -859,7 +875,7 @@ class Recompiler:
         prnt('{')
         prnt('  /* only to generate compile-time warnings or errors */')
         prnt('  (void)p;')
-        for fname, ftype, fbitsize, fqual in tp.enumfields():
+        for fname, ftype, fbitsize, fqual in self._enum_fields(tp):
             try:
                 if ftype.is_integer_type() or fbitsize >= 0:
                     # accept all integers, but complain on float or double
@@ -915,8 +931,7 @@ class Recompiler:
         flags = '|'.join(flags) or '0'
         c_fields = []
         if reason_for_not_expanding is None:
-            expand_anonymous_struct_union = not self.target_is_python
-            enumfields = list(tp.enumfields(expand_anonymous_struct_union))
+            enumfields = list(self._enum_fields(tp))
             for fldname, fldtype, fbitsize, fqual in enumfields:
                 fldtype = self._field_type(tp, fldname, fldtype)
                 self._check_not_opaque(fldtype,
@@ -1296,14 +1311,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/cffi/setuptools_ext.py b/cffi/setuptools_ext.py
index df5a518..8fe3614 100644
--- a/cffi/setuptools_ext.py
+++ b/cffi/setuptools_ext.py
@@ -84,11 +84,13 @@ def _set_py_limited_api(Extension, kwds):
 
     On Windows, with CPython <= 3.4, it's better not to use py_limited_api
     because virtualenv *still* doesn't copy PYTHON3.DLL on these versions.
-    For now we'll skip py_limited_api on all Windows versions to avoid an
-    inconsistent mess.
+    Recently (2020) we started shipping only >= 3.5 wheels, though.  So
+    we'll give it another try and set py_limited_api on Windows >= 3.5.
     """
+    from cffi import recompiler
+
     if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount')
-            and sys.platform != 'win32'):
+            and recompiler.USE_LIMITED_API):
         import setuptools
         try:
             setuptools_major_version = int(setuptools.__version__.partition('.')[0])
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/cffi/verifier.py b/cffi/verifier.py
index 59b78c2..a500c78 100644
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -50,7 +50,8 @@ class Verifier(object):
             if tag:
                 raise TypeError("can't specify both 'modulename' and 'tag'")
         else:
-            key = '\x00'.join([sys.version[:3], __version_verifier_modules__,
+            key = '\x00'.join(['%d.%d' % sys.version_info[:2],
+                               __version_verifier_modules__,
                                preamble, flattened_kwds] +
                               ffi._cdefsources)
             if sys.version_info >= (3,):
diff --git a/debian/changelog b/debian/changelog
index ee471c4..280ea8c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+python-cffi-py2 (1.14.6-0kali1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Kali Janitor <janitor@kali.org>  Sun, 19 Sep 2021 22:02:23 -0000
+
 python-cffi-py2 (1.14.0-2kali2) kali-dev; urgency=medium
 
   * Complete debian/copyright
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
index dd56a7e..b1bbb8d 100644
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -590,7 +590,7 @@ above`_ as primitive (so ``extern long long a[5];`` and ``extern int64_t a[5]``
 different declarations).  The reason for that is detailed in `a comment
 about an issue.`__
 
-.. __: https://bitbucket.org/cffi/cffi/issues/265/cffi-doesnt-allow-creating-pointers-to#comment-28406958
+.. __: https://foss.heptapod.net/pypy/cffi/-/issues/265#note_50393
 
 
 ffibuilder.compile() etc.: compiling out-of-line modules
diff --git a/doc/source/conf.py b/doc/source/conf.py
index d5c768b..0b13937 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -47,7 +47,7 @@ copyright = u'2012-2018, Armin Rigo, Maciej Fijalkowski'
 # The short X.Y version.
 version = '1.14'
 # The full version, including alpha/beta/rc tags.
-release = '1.14.0'
+release = '1.14.6'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/doc/source/embedding.rst b/doc/source/embedding.rst
index 181249c..f26a0f2 100644
--- a/doc/source/embedding.rst
+++ b/doc/source/embedding.rst
@@ -380,7 +380,7 @@ inaccuracies in this paragraph or better ways to do things.)
   ``dlopen("libpythonX.Y.so", RTLD_LAZY|RTLD_GLOBAL)``, which will
   force ``libpythonX.Y.so`` to be loaded first.
 
-.. __: https://bitbucket.org/cffi/cffi/issues/264/
+.. __: https://foss.heptapod.net/pypy/cffi/-/issues/264
 
 
 Using multiple CFFI-made DLLs
diff --git a/doc/source/goals.rst b/doc/source/goals.rst
index 0fda659..df4877c 100644
--- a/doc/source/goals.rst
+++ b/doc/source/goals.rst
@@ -55,8 +55,8 @@ Get started by reading `the overview`__.
 Comments and bugs
 -----------------
 
-The best way to contact us is on the IRC ``#pypy`` channel of
-``irc.freenode.net``.  Feel free to discuss matters either there or in
+The best way to contact us is on the IRC ``#cffi`` or ``#pypy`` channels of
+``irc.libera.chat``.  Feel free to discuss matters either there or in
 the `mailing list`_.  Please report to the `issue tracker`_ any bugs.
 
 As a general rule, when there is a design issue to resolve, we pick the
@@ -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
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
index e6b715a..c06b31a 100644
--- a/doc/source/installation.rst
+++ b/doc/source/installation.rst
@@ -52,26 +52,26 @@ Download and Installation:
 
 * https://pypi.python.org/pypi/cffi
 
-* Checksums of the "source" package version 1.14.0:
+* Checksums of the "source" package version 1.14.6:
 
    - MD5: ...
 
-   - SHA: ...
+   - SHA1: ...
 
    - SHA256: ...
 
-* 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
-  `MacOS X`_ or `Windows 64`_.)
+  `MacOS X`_.)
 
 * running the tests: ``py.test  c/  testing/`` (if you didn't
   install cffi yet, you need first ``python setup_base.py build_ext -f
   -i``)
 
-.. _`Bitbucket page`: https://bitbucket.org/cffi/cffi
+.. _`Heptapod page`: https://foss.heptapod.net/pypy/cffi
 
 Demos:
 
@@ -82,9 +82,9 @@ Demos:
   ultimate reference is given by the tests, notably
   `testing/cffi1/test_verify1.py`_ and `testing/cffi0/backend_tests.py`_.
 
-.. _`demo`: https://bitbucket.org/cffi/cffi/src/default/demo
-.. _`testing/cffi1/test_verify1.py`: https://bitbucket.org/cffi/cffi/src/default/testing/cffi1/test_verify1.py
-.. _`testing/cffi0/backend_tests.py`: https://bitbucket.org/cffi/cffi/src/default/testing/cffi0/backend_tests.py
+.. _`demo`: https://foss.heptapod.net/pypy/cffi/-/tree/branch/default/demo
+.. _`testing/cffi1/test_verify1.py`: https://foss.heptapod.net/pypy/cffi/-/blob/branch/default/testing/cffi1/test_verify1.py
+.. _`testing/cffi0/backend_tests.py`: https://foss.heptapod.net/pypy/cffi/-/blob/branch/default/testing/cffi0/backend_tests.py
 
 
 Platform-specific instructions
@@ -133,10 +133,10 @@ as described here_.
 .. _here: http://superuser.com/questions/259278/python-2-6-1-pycrypto-2-3-pypi-package-broken-pipe-during-build
 
 
-Windows (regular 32-bit)
-++++++++++++++++++++++++
+Windows (32/64-bit)
++++++++++++++++++++
 
-Win32 works and is tested at least each official release.
+Win32 and Win64 work and are tested at least each official release.
 
 The recommended C compiler compatible with Python 2.7 is this one:
 http://www.microsoft.com/en-us/download/details.aspx?id=44266
@@ -150,22 +150,6 @@ For Python 3.4 and beyond:
 https://www.visualstudio.com/en-us/downloads/visual-studio-2015-ctp-vs
 
 
-Windows 64
-++++++++++
-
-Win64 received very basic testing and we applied a few essential
-fixes in cffi 0.7. The comment above applies for Python 2.7 on 
-Windows 64 as well. Please report any other issue.
-
-Note as usual that this is only about running the 64-bit version of
-Python on the 64-bit OS.  If you're running the 32-bit version (the
-common case apparently), then you're running Win32 as far as we're
-concerned.
-
-.. _`issue 9`: https://bitbucket.org/cffi/cffi/issue/9
-.. _`Python issue 7546`: http://bugs.python.org/issue7546
-
-
 Linux and OS/X: UCS2 versus UCS4
 ++++++++++++++++++++++++++++++++
 
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")  
diff --git a/doc/source/ref.rst b/doc/source/ref.rst
index 9aaed7a..1348ed6 100644
--- a/doc/source/ref.rst
+++ b/doc/source/ref.rst
@@ -459,7 +459,7 @@ destructors will be called in a random order.  If you need a particular
 order, see the discussion in `issue 340`__.
 
 .. __: http://bugs.python.org/issue31105
-.. __: https://bitbucket.org/cffi/cffi/issues/340/resources-release-issues
+.. __: https://foss.heptapod.net/pypy/cffi/-/issues/340
 
 
 .. _ffi-new-handle:
@@ -731,7 +731,7 @@ example::
            raise IndexError("index too large!")
        ...
 
-.. __: https://bitbucket.org/cffi/cffi/issues/233/
+.. __: https://foss.heptapod.net/pypy/cffi/-/issues/233
 
 
 .. _ffi-getctype:
diff --git a/doc/source/using.rst b/doc/source/using.rst
index beb3f92..fc65a4f 100644
--- a/doc/source/using.rst
+++ b/doc/source/using.rst
@@ -787,7 +787,7 @@ The ``extern "Python"`` functions cannot be variadic for now.  This
 may be implemented in the future.  (`This demo`__ shows how to do it
 anyway, but it is a bit lengthy.)
 
-.. __: https://bitbucket.org/cffi/cffi/src/default/demo/extern_python_varargs.py
+.. __: https://foss.heptapod.net/pypy/cffi/-/blob/branch/default/demo/extern_python_varargs.py
 
 Each corresponding Python callback function is defined with the
 ``@ffi.def_extern()`` decorator.  Be careful when writing this
@@ -896,7 +896,7 @@ ffi.callback() and the result is the same.
     to refactor the involved code so that it no longer uses ``ffi.callback()``.
 
 .. __: https://github.com/pyca/pyopenssl/issues/596
-.. __: https://bitbucket.org/cffi/cffi/issues/391/
+.. __: https://foss.heptapod.net/pypy/cffi/-/issues/391
 .. __: https://bugzilla.redhat.com/show_bug.cgi?id=1249685
 
 Warning: like ffi.new(), ffi.callback() returns a cdata that has
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
index 660608a..ff09168 100644
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -2,6 +2,72 @@
 What's New
 ======================
 
+v1.14.6
+=======
+
+* Test fixes for CPython 3.10.0b3
+
+* Support for `sys.unraisablehook()` on Python >= 3.8
+
+* Fix two minor memory leaks (thanks Sebastian!)
+
+* Like many projects that had an IRC channel on freenode, we moved it to
+  ``irc.libera.chat``.
+
+v1.14.5
+=======
+
+* Source fix for old gcc versions
+
+* This and future releases should include wheels on more platforms,
+  thanks to our new release managers Matt and Matt!
+
+v1.14.4
+=======
+
+Release done for pip reasons.
+
+v1.14.3
+=======
+
+Release done for pip reasons.
+
+v1.14.2
+=======
+
+* CPython 3 on Windows: we again try to compile with ``Py_LIMITED_API``
+  by default.  This flag is not added if you run the compilation with
+  CPython 3.4, as it only works with CPython >= 3.5, but by now this
+  version of Python is quite old (and we no longer distribute cffi
+  wheels for it).
+
+  This may require that you upgrade ``virtualenv`` (requires version 16
+  or newer) or at least copy manually ``python3.dll`` into your existing
+  virtualenvs.  For distributing wheels with your cffi modules, you may
+  also need to upgrade ``wheel`` to the just-released version 0.35.
+
+  You can manually disable ``Py_LIMITED_API`` by calling
+  ``ffi.set_source(..., py_limited_api=False)``.
+
+
+v1.14.1
+=======
+
+* CFFI source code is now `hosted on Heptapod`_.
+
+* Improved support for ``typedef int my_array_t[...];`` with an explicit
+  dot-dot-dot in API mode (`issue #453`_)
+
+* Windows (32 and 64 bits): multiple fixes for ABI-mode call to functions
+  that return a structure.
+
+* Experimental support for MacOS 11 on aarch64.
+
+* and a few other minor changes and bug fixes.
+
+.. _`hosted on Heptapod`: https://foss.heptapod.net/pypy/cffi/
+.. _`issue #453`: https://foss.heptapod.net/pypy/cffi/issues/453
+
 
 v1.14
 =====
@@ -36,7 +102,7 @@ v1.13.2
 * re-release because the Linux wheels came with an attached version of libffi
   that was very old and buggy (`issue #432`_).
 
-.. _`issue #432`: https://bitbucket.org/cffi/cffi/issues/432/
+.. _`issue #432`: https://foss.heptapod.net/pypy/cffi/-/issues/432
 
 
 
@@ -55,8 +121,8 @@ v1.13.1
 * fixed `issue #427`_ where a multithreading mistake in the embedding logic
   initialization code would cause deadlocks on CPython 3.7.
 
-.. _`issue #429`: https://bitbucket.org/cffi/cffi/issues/429/
-.. _`issue #427`: https://bitbucket.org/cffi/cffi/issues/427/
+.. _`issue #429`: https://foss.heptapod.net/pypy/cffi/-/issues/429
+.. _`issue #427`: https://foss.heptapod.net/pypy/cffi/-/issues/427
 
 
 v1.13
@@ -82,8 +148,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 +163,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 +185,7 @@ v1.12.1
 
 
 v1.12
-=====
+-----
 
 * `Direct support for pkg-config`__.
 
@@ -151,11 +220,8 @@ v1.12
   to 1 byte instead of 4).
 
 .. __: cdef.html#pkgconfig
-.. _`issue #362`: https://bitbucket.org/cffi/cffi/issues/362/
-
+.. _`issue #362`: https://foss.heptapod.net/pypy/cffi/-/issues/362
 
-Older Versions
-==============
 
 v1.11.5
 -------
@@ -186,10 +252,10 @@ v1.11.5
   concerned about virtualenv: pass ``define_macros=[("Py_LIMITED_API",
   None)]`` as a keyword to the ``ffibuilder.set_source()`` call.
 
-.. _`Issue #345`: https://bitbucket.org/cffi/cffi/issues/345/
-.. _`Issue #350`: https://bitbucket.org/cffi/cffi/issues/350/
-.. _`Issue #358`: https://bitbucket.org/cffi/cffi/issues/358/
-.. _`Issue #357`: https://bitbucket.org/cffi/cffi/issues/357/
+.. _`Issue #345`: https://foss.heptapod.net/pypy/cffi/-/issues/345
+.. _`Issue #350`: https://foss.heptapod.net/pypy/cffi/-/issues/350
+.. _`Issue #358`: https://foss.heptapod.net/pypy/cffi/-/issues/358
+.. _`Issue #357`: https://foss.heptapod.net/pypy/cffi/-/issues/357
 
 
 v1.11.4
@@ -202,7 +268,7 @@ v1.11.4
   ``foo.cp36-win32.pyd``, to make it clear that they are regular
   CPython modules depending on ``python36.dll``.
 
-.. _`Issue #355`: https://bitbucket.org/cffi/cffi/issues/355/
+.. _`Issue #355`: https://foss.heptapod.net/pypy/cffi/-/issues/355
 
 
 v1.11.3
@@ -291,9 +357,9 @@ v1.11
   that are *slower* to call than the API mode does.  For some reason it
   is often thought to be faster.  It is not!
 
-.. __: https://bitbucket.org/cffi/cffi/issues/321/cffi-191-segmentation-fault-during-self
+.. __: https://foss.heptapod.net/pypy/cffi/-/issues/321
 .. __: ref.html#ffi-gc
-.. __: https://bitbucket.org/cffi/cffi/issues/320/improve-memory_pressure-management
+.. __: https://foss.heptapod.net/pypy/cffi/-/issues/320
 .. __: http://bugs.python.org/issue31105
 
 
diff --git a/setup.py b/setup.py
index e1dd39d..7fd6388 100644
--- a/setup.py
+++ b/setup.py
@@ -56,7 +56,7 @@ def no_working_compiler_found():
     tries to compile C code.  (Hints: on OS/X 10.8, for errors about
     -mno-fused-madd see http://stackoverflow.com/questions/22313407/
     Otherwise, see https://wiki.python.org/moin/CompLangPython or
-    the IRC channel #python on irc.freenode.net.)
+    the IRC channel #python on irc.libera.chat.)
 
     Trying to continue anyway.  If you are trying to install CFFI from
     a build done in a different context, you can ignore this warning.
@@ -149,25 +149,14 @@ else:
     ask_supports_thread()
     ask_supports_sync_synchronize()
 
+if 'darwin' in sys.platform:
+    # priority is given to `pkg_config`, but always fall back on SDK's libffi.
+    extra_compile_args += ['-iwithsysroot/usr/include/ffi']
+
 if 'freebsd' in sys.platform:
     include_dirs.append('/usr/local/include')
     library_dirs.append('/usr/local/lib')
 
-if 'darwin' in sys.platform:
-    try:
-        p = subprocess.Popen(['xcrun', '--show-sdk-path'],
-                             stdout=subprocess.PIPE)
-    except OSError as e:
-        if e.errno not in [errno.ENOENT, errno.EACCES]:
-            raise
-    else:
-        t = p.stdout.read().decode().strip()
-        p.stdout.close()
-        if p.wait() == 0:
-            include_dirs.append(t + '/usr/include/ffi')
-
-
-
 if __name__ == '__main__':
     from setuptools import setup, Distribution, Extension
 
@@ -198,7 +187,7 @@ Contact
 
 `Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_
 """,
-        version='1.14.0',
+        version='1.14.6',
         packages=['cffi'] if cpython else [],
         package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', 
                                '_embedding.h', '_cffi_errors.h']}
diff --git a/setup_base.py b/setup_base.py
index 667c7d5..4cf6ea5 100644
--- a/setup_base.py
+++ b/setup_base.py
@@ -8,6 +8,7 @@ from setup import library_dirs, extra_compile_args, extra_link_args
 if __name__ == '__main__':
     from distutils.core import setup
     from distutils.extension import Extension
+
     standard = '__pypy__' not in sys.builtin_module_names
     setup(packages=['cffi'],
           requires=['pycparser'],
diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py
index 063e9f2..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 *
 
@@ -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:
diff --git a/testing/cffi0/test_ffi_backend.py b/testing/cffi0/test_ffi_backend.py
index 6937a79..8e29bc4 100644
--- a/testing/cffi0/test_ffi_backend.py
+++ b/testing/cffi0/test_ffi_backend.py
@@ -179,6 +179,7 @@ class TestBitfield:
         setters = ['case %d: s.%s = value; break;' % iname
                    for iname in enumerate(fnames)]
         lib = ffi1.verify("""
+            #include <string.h>
             struct s1 { %s };
             struct sa { char a; struct s1 b; };
             #define Gofs_y  offsetof(struct s1, y)
@@ -246,7 +247,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 +264,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 +279,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 +294,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")
diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py
index 6312707..b4bb23d 100644
--- a/testing/cffi0/test_function.py
+++ b/testing/cffi0/test_function.py
@@ -5,7 +5,7 @@ import math, os, sys
 import ctypes.util
 from cffi.backend_ctypes import CTypesBackend
 from testing.udir import udir
-from testing.support import FdWriteCapture
+from testing.support import FdWriteCapture, StdErrCapture
 from .backend_tests import needs_dlopen_none
 
 try:
@@ -227,19 +227,32 @@ class TestFunction(object):
             def cb():
                 return returnvalue
             fptr = ffi.callback("void(*)(void)", cb)
-            old_stderr = sys.stderr
-            try:
-                sys.stderr = StringIO()
+            with StdErrCapture() as f:
                 returned = fptr()
-                printed = sys.stderr.getvalue()
-            finally:
-                sys.stderr = old_stderr
+            printed = f.getvalue()
             assert returned is None
             if returnvalue is None:
                 assert printed == ''
             else:
                 assert "None" in printed
 
+    def test_callback_returning_struct_three_bytes(self):
+        if self.Backend is CTypesBackend:
+            py.test.skip("not supported with the ctypes backend")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            typedef struct {
+                unsigned char a, b, c;
+            } THREEBYTES;
+        """)
+        def cb():
+            return (12, 34, 56)
+        fptr = ffi.callback("THREEBYTES(*)(void)", cb)
+        tb = fptr()
+        assert tb.a == 12
+        assert tb.b == 34
+        assert tb.c == 56
+
     def test_passing_array(self):
         ffi = FFI(backend=self.Backend())
         ffi.cdef("""
diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py
index 990f259..ffad879 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,22 @@ 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")
+        if self.__class__.Backend is CTypesBackend:
+            py.test.skip("not working on win32 on the ctypes backend")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            typedef struct {
+                unsigned char a, b, c;
+            } THREEBYTES;
+
+            THREEBYTES return_three_bytes(void);
+        """)
+        lib = ffi.dlopen(self.module)
+        tb = lib.return_three_bytes()
+        assert tb.a == 12
+        assert tb.b == 34
+        assert tb.c == 56
diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py
index 3fc3783..a5e4587 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,93 @@ 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"
+    ffi = FFI(backend=FakeBackend())
+    e = py.test.raises(CDefError, ffi.cdef, """
+        \t # \t 8 \t "baz.c" \t
+
+        some syntax error here
+    """)
+    assert str(e.value) == "parse error\nbaz.c:9:14: before: syntax"
+    #
+    e = py.test.raises(CDefError, ffi.cdef, """
+        # 7 "foo//bar.c"
+
+        some syntax error here
+    """)
+    assert str(e.value) == "parse error\nfoo//bar.c:8:14: before: syntax"
+
+def test_multiple_line_directives():
+    ffi = FFI(backend=FakeBackend())
+    e = py.test.raises(CDefError, ffi.cdef,
+    """ #line 5 "foo.c"
+        extern int xx;
+        #line 6 "bar.c"
+        extern int yy;
+        #line 7 "baz.c"
+        some syntax error here
+        #line 8 "yadda.c"
+        extern int zz;
+    """)
+    assert str(e.value) == "parse error\nbaz.c:7:14: before: syntax"
+    #
+    e = py.test.raises(CDefError, ffi.cdef,
+    """ # 5 "foo.c"
+        extern int xx;
+        # 6 "bar.c"
+        extern int yy;
+        # 7 "baz.c"
+        some syntax error here
+        # 8 "yadda.c"
+        extern int zz;
+    """)
+    assert str(e.value) == "parse error\nbaz.c:7:14: before: syntax"
+
+def test_commented_line_directive():
+    ffi = FFI(backend=FakeBackend())
+    e = py.test.raises(CDefError, ffi.cdef, """
+        /*
+        #line 5 "foo.c"
+        */
+        void xx(void);
+
+        #line 6 "bar.c"
+        /*
+        #line 35 "foo.c"
+        */
+        some syntax error
+    """)
+    #
+    assert str(e.value) == "parse error\nbar.c:9:14: before: syntax"
+    e = py.test.raises(CDefError, ffi.cdef, """
+        /*
+        # 5 "foo.c"
+        */
+        void xx(void);
+
+        # 6 "bar.c"
+        /*
+        # 35 "foo.c"
+        */
+        some syntax error
+    """)
+    assert str(e.value) == "parse error\nbar.c:9:14: before: syntax"
+
 def test_line_continuation_in_defines():
     ffi = FFI(backend=FakeBackend())
     ffi.cdef("""
diff --git a/testing/cffi0/test_version.py b/testing/cffi0/test_version.py
index 9325685..d380b98 100644
--- a/testing/cffi0/test_version.py
+++ b/testing/cffi0/test_version.py
@@ -36,7 +36,12 @@ def test_doc_version_file():
     v = cffi.__version__.replace('+', '')
     p = os.path.join(parent, 'doc', 'source', 'installation.rst')
     content = open(p).read()
-    assert (" package version %s:" % v) in content
+    if " package version %s:" % v not in content:
+        for i in range(5):
+            if " package version %s-%d:" % (v, i) in content:
+                break
+        else:
+            assert 0, "doc/source/installation.rst needs updating"
 
 def test_setup_version():
     parent = os.path.dirname(os.path.dirname(cffi.__file__))
diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py
new file mode 100644
index 0000000..30c6fed
--- /dev/null
+++ b/testing/cffi1/test_function_args.py
@@ -0,0 +1,208 @@
+import pytest, sys
+try:
+    # comment out the following line to run this test.
+    # the latest on x86-64 linux: https://github.com/libffi/libffi/issues/574
+    if sys.platform != 'win32':
+        raise ImportError("this test is skipped because it keeps finding "
+                          "failures in libffi, instead of cffi")
+
+    from hypothesis import given, settings, example
+    from hypothesis import strategies as st
+except ImportError as e:
+    e1 = e
+    def test_types():
+        pytest.skip(str(e1))
+else:
+
+    from cffi import FFI
+    import sys, random
+    from .test_recompiler import verify
+
+    ALL_PRIMITIVES = [
+        'unsigned char',
+        'short',
+        'int',
+        'long',
+        'long long',
+        'float',
+        'double',
+        #'long double',   --- on x86 it can give libffi crashes
+    ]
+    def _make_struct(s):
+        return st.lists(s, min_size=1)
+    types = st.one_of(st.sampled_from(ALL_PRIMITIVES),
+                      st.lists(st.sampled_from(ALL_PRIMITIVES), min_size=1))
+    # NB. 'types' could be st.recursive instead, but it doesn't
+    # really seem useful
+
+    def draw_primitive(ffi, typename):
+        value = random.random() * 2**40
+        if typename != 'long double':
+            return ffi.cast(typename, value)
+        else:
+            return value
+
+    TEST_RUN_COUNTER = 0
+
+
+    @given(st.lists(types), types)
+    @settings(max_examples=100, deadline=5000)   # 5000ms
+    def test_types(tp_args, tp_result):
+        global TEST_RUN_COUNTER
+        print(tp_args, tp_result)
+        cdefs = []
+        structs = {}
+
+        def build_type(tp):
+            if type(tp) is list:
+                field_types = [build_type(tp1) for tp1 in tp]
+                fields = ['%s f%d;' % (ftp, j)
+                          for (j, ftp) in enumerate(field_types)]
+                fields = '\n    '.join(fields)
+                name = 's%d' % len(cdefs)
+                cdefs.append("typedef struct {\n    %s\n} %s;" % (fields, name))
+                structs[name] = field_types
+                return name
+            else:
+                return tp
+
+        args = [build_type(tp) for tp in tp_args]
+        result = build_type(tp_result)
+
+        TEST_RUN_COUNTER += 1
+        signature = "%s testfargs(%s)" % (result,
+            ', '.join(['%s a%d' % (arg, i) for (i, arg) in enumerate(args)])
+            or 'void')
+
+        source = list(cdefs)
+
+        cdefs.append("%s;" % signature)
+        cdefs.append("extern %s testfargs_result;" % result)
+        for i, arg in enumerate(args):
+            cdefs.append("extern %s testfargs_arg%d;" % (arg, i))
+        source.append("%s testfargs_result;" % result)
+        for i, arg in enumerate(args):
+            source.append("%s testfargs_arg%d;" % (arg, i))
+        source.append(signature)
+        source.append("{")
+        for i, arg in enumerate(args):
+            source.append("    testfargs_arg%d = a%d;" % (i, i))
+        source.append("    return testfargs_result;")
+        source.append("}")
+
+        typedef_line = "typedef %s;" % (signature.replace('testfargs',
+                                                          '(*mycallback_t)'),)
+        assert signature.endswith(')')
+        sig_callback = "%s testfcallback(mycallback_t callback)" % result
+        cdefs.append(typedef_line)
+        cdefs.append("%s;" % sig_callback)
+        source.append(typedef_line)
+        source.append(sig_callback)
+        source.append("{")
+        source.append("    return callback(%s);" %
+                ', '.join(["testfargs_arg%d" % i for i in range(len(args))]))
+        source.append("}")
+
+        ffi = FFI()
+        ffi.cdef("\n".join(cdefs))
+        lib = verify(ffi, 'test_function_args_%d' % TEST_RUN_COUNTER,
+                     "\n".join(source), no_cpp=True)
+
+        # when getting segfaults, enable this:
+        if False:
+            from testing.udir import udir
+            import subprocess
+            f = open(str(udir.join('run1.py')), 'w')
+            f.write('import sys; sys.path = %r\n' % (sys.path,))
+            f.write('from _CFFI_test_function_args_%d import ffi, lib\n' %
+                    TEST_RUN_COUNTER)
+            for i in range(len(args)):
+                f.write('a%d = ffi.new("%s *")\n' % (i, args[i]))
+            aliststr = ', '.join(['a%d[0]' % i for i in range(len(args))])
+            f.write('lib.testfargs(%s)\n' % aliststr)
+            f.write('ffi.addressof(lib, "testfargs")(%s)\n' % aliststr)
+            f.close()
+            print("checking for segfault for direct call...")
+            rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir))
+            assert rc == 0, rc
+
+        def make_arg(tp):
+            if tp in structs:
+                return [make_arg(tp1) for tp1 in structs[tp]]
+            else:
+                return draw_primitive(ffi, tp)
+
+        passed_args = [make_arg(arg) for arg in args]
+        returned_value = make_arg(result)
+
+        def write(p, v):
+            if type(v) is list:
+                for i, v1 in enumerate(v):
+                    write(ffi.addressof(p, 'f%d' % i), v1)
+            else:
+                p[0] = v
+
+        write(ffi.addressof(lib, 'testfargs_result'), returned_value)
+
+        ## CALL forcing libffi
+        print("CALL forcing libffi")
+        received_return = ffi.addressof(lib, 'testfargs')(*passed_args)
+        ##
+
+        _tp_long_double = ffi.typeof("long double")
+        def check(p, v):
+            if type(v) is list:
+                for i, v1 in enumerate(v):
+                    check(ffi.addressof(p, 'f%d' % i), v1)
+            else:
+                if ffi.typeof(p).item is _tp_long_double:
+                    assert ffi.cast("double", p[0]) == v
+                else:
+                    assert p[0] == v
+
+        for i, arg in enumerate(passed_args):
+            check(ffi.addressof(lib, 'testfargs_arg%d' % i), arg)
+        ret = ffi.new(result + "*", received_return)
+        check(ret, returned_value)
+
+        ## CALLBACK
+        def expand(value):
+            if isinstance(value, ffi.CData):
+                t = ffi.typeof(value)
+                if t is _tp_long_double:
+                    return float(ffi.cast("double", value))
+                return [expand(getattr(value, 'f%d' % i))
+                        for i in range(len(t.fields))]
+            else:
+                return value
+
+        # when getting segfaults, enable this:
+        if False:
+            from testing.udir import udir
+            import subprocess
+            f = open(str(udir.join('run1.py')), 'w')
+            f.write('import sys; sys.path = %r\n' % (sys.path,))
+            f.write('from _CFFI_test_function_args_%d import ffi, lib\n' %
+                    TEST_RUN_COUNTER)
+            f.write('def callback(*args): return ffi.new("%s *")[0]\n' % result)
+            f.write('fptr = ffi.callback("%s(%s)", callback)\n' % (result,
+                                                                ','.join(args)))
+            f.write('print(lib.testfcallback(fptr))\n')
+            f.close()
+            print("checking for segfault for callback...")
+            rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir))
+            assert rc == 0, rc
+
+        seen_args = []
+        def callback(*args):
+            seen_args.append([expand(arg) for arg in args])
+            return returned_value
+
+        fptr = ffi.callback("%s(%s)" % (result, ','.join(args)), callback)
+        print("CALL with callback")
+        received_return = lib.testfcallback(fptr)
+
+        assert len(seen_args) == 1
+        assert passed_args == seen_args[0]
+        ret = ffi.new(result + "*", received_return)
+        check(ret, returned_value)
diff --git a/testing/cffi1/test_re_python.py b/testing/cffi1/test_re_python.py
index dce4f40..2ae0dd1 100644
--- a/testing/cffi1/test_re_python.py
+++ b/testing/cffi1/test_re_python.py
@@ -74,6 +74,7 @@ def setup_module(mod):
     int strlen(const char *);
     struct with_union { union { int a; char b; }; };
     union with_struct { struct { int a; char b; }; };
+    struct with_struct_with_union { struct { union { int x; }; } cp; };
     struct NVGcolor { union { float rgba[4]; struct { float r,g,b,a; }; }; };
     typedef struct selfref { struct selfref *next; } *selfref_ptr_t;
     """)
@@ -248,6 +249,10 @@ def test_anonymous_union_inside_struct():
     assert ffi.offsetof("union with_struct", "b") == INT
     assert ffi.sizeof("union with_struct") >= INT + 1
     #
+    assert ffi.sizeof("struct with_struct_with_union") == INT
+    p = ffi.new("struct with_struct_with_union *")
+    assert p.cp.x == 0
+    #
     FLOAT = ffi.sizeof("float")
     assert ffi.sizeof("struct NVGcolor") == FLOAT * 4
     assert ffi.offsetof("struct NVGcolor", "rgba") == 0
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
index 78abaa0..fdb4d5a 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():
@@ -886,8 +889,8 @@ def test_unpack_args():
     e7 = py.test.raises(TypeError, lib.foo2, 45, 46, 47)
     def st1(s):
         s = str(s)
-        if s.startswith("_CFFI_test_unpack_args.CompiledLib."):
-            s = s[len("_CFFI_test_unpack_args.CompiledLib."):]
+        if s.startswith("_CFFI_test_unpack_args.Lib."):
+            s = s[len("_CFFI_test_unpack_args.Lib."):]
         return s
     assert st1(e1.value) == "foo0() takes no arguments (1 given)"
     assert st1(e2.value) == "foo0() takes no arguments (2 given)"
@@ -1666,9 +1669,10 @@ def test_extern_python_bogus_result_type():
     with StdErrCapture() as f:
         res = lib.bar(321)
     assert res is None
-    assert f.getvalue() == (
-        "From cffi callback %r:\n" % (bar,) +
-        "Trying to convert the result back to C:\n"
+    msg = f.getvalue()
+    assert "rom cffi callback %r" % (bar,) in msg
+    assert "rying to convert the result back to C:\n" in msg
+    assert msg.endswith(
         "TypeError: callback with the return type 'void' must return None\n")
 
 def test_extern_python_redefine():
@@ -2119,6 +2123,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("""
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"
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,))
diff --git a/testing/support.py b/testing/support.py
index de8131c..6339a94 100644
--- a/testing/support.py
+++ b/testing/support.py
@@ -33,9 +33,14 @@ class StdErrCapture(object):
             from io import StringIO
         self.old_stderr = sys.stderr
         sys.stderr = f = StringIO()
+        if hasattr(sys, '__unraisablehook__'):           # work around pytest
+            self.old_unraisablebook = sys.unraisablehook # on recent CPythons
+            sys.unraisablehook = sys.__unraisablehook__
         return f
     def __exit__(self, *args):
         sys.stderr = self.old_stderr
+        if hasattr(self, 'old_unraisablebook'):
+            sys.unraisablehook = self.old_unraisablebook
 
 
 class FdWriteCapture(object):
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