[pypy-commit] pypy default: merged upstream

alex_gaynor noreply at buildbot.pypy.org
Tue May 26 20:05:38 CEST 2015


Author: Alex Gaynor <alex.gaynor at gmail.com>
Branch: 
Changeset: r77594:d93ad0d33f48
Date: 2015-05-26 14:05 -0400
http://bitbucket.org/pypy/pypy/changeset/d93ad0d33f48/

Log:	merged upstream

diff --git a/lib_pypy/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO
--- a/lib_pypy/cffi.egg-info/PKG-INFO
+++ b/lib_pypy/cffi.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: cffi
-Version: 1.0.3
+Version: 1.0.4
 Summary: Foreign Function Interface for Python calling C code.
 Home-page: http://cffi.readthedocs.org
 Author: Armin Rigo, Maciej Fijalkowski
diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py
--- a/lib_pypy/cffi/__init__.py
+++ b/lib_pypy/cffi/__init__.py
@@ -4,8 +4,8 @@
 from .api import FFI, CDefError, FFIError
 from .ffiplatform import VerificationError, VerificationMissing
 
-__version__ = "1.0.3"
-__version_info__ = (1, 0, 3)
+__version__ = "1.0.4"
+__version_info__ = (1, 0, 4)
 
 # 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/lib_pypy/cffi/_cffi_include.h b/lib_pypy/cffi/_cffi_include.h
--- a/lib_pypy/cffi/_cffi_include.h
+++ b/lib_pypy/cffi/_cffi_include.h
@@ -51,6 +51,11 @@
 # endif
 #endif
 
+#ifdef __GNUC__
+# define _CFFI_UNUSED_FN  __attribute__((unused))
+#else
+# define _CFFI_UNUSED_FN  /* nothing */
+#endif
 
 /**********  CPython-specific section  **********/
 #ifndef PYPY_VERSION
@@ -82,7 +87,8 @@
             PyLong_FromLongLong((long long)x)))
 
 #define _cffi_to_c_int(o, type)                                          \
-    (sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
+    ((type)(                                                             \
+     sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
                                          : (type)_cffi_to_c_i8(o)) :     \
      sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o)       \
                                          : (type)_cffi_to_c_i16(o)) :    \
@@ -90,7 +96,7 @@
                                          : (type)_cffi_to_c_i32(o)) :    \
      sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o)       \
                                          : (type)_cffi_to_c_i64(o)) :    \
-     (Py_FatalError("unsupported size for type " #type), (type)0))
+     (Py_FatalError("unsupported size for type " #type), (type)0)))
 
 #define _cffi_to_c_i8                                                    \
                  ((int(*)(PyObject *))_cffi_exports[1])
@@ -181,6 +187,20 @@
     return NULL;
 }
 
+_CFFI_UNUSED_FN
+static PyObject **_cffi_unpack_args(PyObject *args_tuple, Py_ssize_t expected,
+                                    const char *fnname)
+{
+    if (PyTuple_GET_SIZE(args_tuple) != expected) {
+        PyErr_Format(PyExc_TypeError,
+                     "%.150s() takes exactly %zd arguments (%zd given)",
+                     fnname, expected, PyTuple_GET_SIZE(args_tuple));
+        return NULL;
+    }
+    return &PyTuple_GET_ITEM(args_tuple, 0);   /* pointer to the first item,
+                                                  the others follow */
+}
+
 #endif
 /**********  end CPython-specific section  **********/
 
@@ -200,12 +220,6 @@
     ((got_nonpos) == (expected <= 0) &&                 \
      (got) == (unsigned long long)expected)
 
-#ifdef __GNUC__
-# define _CFFI_UNUSED_FN  __attribute__((unused))
-#else
-# define _CFFI_UNUSED_FN  /* nothing */
-#endif
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib_pypy/cffi/parse_c_type.h b/lib_pypy/cffi/parse_c_type.h
--- a/lib_pypy/cffi/parse_c_type.h
+++ b/lib_pypy/cffi/parse_c_type.h
@@ -83,7 +83,8 @@
     const char *name;
     void *address;
     _cffi_opcode_t type_op;
-    size_t size;             // 0 if unknown
+    void *size_or_direct_fn;  // OP_GLOBAL_VAR: size, or 0 if unknown
+                              // OP_CPYTHON_BLTN_*: addr of direct function
 };
 
 struct _cffi_getconst_s {
diff --git a/lib_pypy/cffi/recompiler.py b/lib_pypy/cffi/recompiler.py
--- a/lib_pypy/cffi/recompiler.py
+++ b/lib_pypy/cffi/recompiler.py
@@ -19,7 +19,7 @@
         self.check_value = check_value
 
     def as_c_expr(self):
-        return '  { "%s", (void *)%s, %s, %s },' % (
+        return '  { "%s", (void *)%s, %s, (void *)%s },' % (
             self.name, self.address, self.type_op.as_c_expr(), self.size)
 
     def as_python_expr(self):
@@ -386,7 +386,7 @@
         prnt('#  ifdef _MSC_VER')
         prnt('     PyMODINIT_FUNC')
         prnt('#  if PY_MAJOR_VERSION >= 3')
-        prnt('     PyInit_%s(void) { return -1; }' % (base_module_name,))
+        prnt('     PyInit_%s(void) { return NULL; }' % (base_module_name,))
         prnt('#  else')
         prnt('     init%s(void) { }' % (base_module_name,))
         prnt('#  endif')
@@ -602,6 +602,26 @@
         else:
             argname = 'args'
         #
+        # ------------------------------
+        # the 'd' version of the function, only for addressof(lib, 'func')
+        arguments = []
+        call_arguments = []
+        context = 'argument of %s' % name
+        for i, type in enumerate(tp.args):
+            arguments.append(type.get_c_name(' x%d' % i, context))
+            call_arguments.append('x%d' % i)
+        repr_arguments = ', '.join(arguments)
+        repr_arguments = repr_arguments or 'void'
+        name_and_arguments = '_cffi_d_%s(%s)' % (name, repr_arguments)
+        prnt('static %s' % (tp.result.get_c_name(name_and_arguments),))
+        prnt('{')
+        call_arguments = ', '.join(call_arguments)
+        result_code = 'return '
+        if isinstance(tp.result, model.VoidType):
+            result_code = ''
+        prnt('  %s%s(%s);' % (result_code, name, call_arguments))
+        prnt('}')
+        #
         prnt('#ifndef PYPY_VERSION')        # ------------------------------
         #
         prnt('static PyObject *')
@@ -632,10 +652,13 @@
             rng = range(len(tp.args))
             for i in rng:
                 prnt('  PyObject *arg%d;' % i)
+            prnt('  PyObject **aa;')
             prnt()
-            prnt('  if (!PyArg_ParseTuple(args, "%s:%s", %s))' % (
-                'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng])))
+            prnt('  aa = _cffi_unpack_args(args, %d, "%s");' % (len(rng), name))
+            prnt('  if (aa == NULL)')
             prnt('    return NULL;')
+            for i in rng:
+                prnt('  arg%d = aa[%d];' % (i, i))
         prnt()
         #
         for i, type in enumerate(tp.args):
@@ -668,6 +691,7 @@
         # the PyPy version: need to replace struct/union arguments with
         # pointers, and if the result is a struct/union, insert a first
         # arg that is a pointer to the result.
+        difference = False
         arguments = []
         call_arguments = []
         context = 'argument of %s' % name
@@ -675,6 +699,7 @@
             indirection = ''
             if isinstance(type, model.StructOrUnion):
                 indirection = '*'
+                difference = True
             arg = type.get_c_name(' %sx%d' % (indirection, i), context)
             arguments.append(arg)
             call_arguments.append('%sx%d' % (indirection, i))
@@ -686,18 +711,22 @@
             tp_result = model.void_type
             result_decl = None
             result_code = '*result = '
-        repr_arguments = ', '.join(arguments)
-        repr_arguments = repr_arguments or 'void'
-        name_and_arguments = '_cffi_f_%s(%s)' % (name, repr_arguments)
-        prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))
-        prnt('{')
-        if result_decl:
-            prnt(result_decl)
-        call_arguments = ', '.join(call_arguments)
-        prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))
-        if result_decl:
-            prnt('  return result;')
-        prnt('}')
+            difference = True
+        if difference:
+            repr_arguments = ', '.join(arguments)
+            repr_arguments = repr_arguments or 'void'
+            name_and_arguments = '_cffi_f_%s(%s)' % (name, repr_arguments)
+            prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))
+            prnt('{')
+            if result_decl:
+                prnt(result_decl)
+            call_arguments = ', '.join(call_arguments)
+            prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))
+            if result_decl:
+                prnt('  return result;')
+            prnt('}')
+        else:
+            prnt('#  define _cffi_f_%s _cffi_d_%s' % (name, name))
         #
         prnt('#endif')        # ------------------------------
         prnt()
@@ -718,7 +747,8 @@
             meth_kind = OP_CPYTHON_BLTN_V   # 'METH_VARARGS'
         self._lsts["global"].append(
             GlobalExpr(name, '_cffi_f_%s' % name,
-                       CffiOp(meth_kind, type_index), check_value=0))
+                       CffiOp(meth_kind, type_index), check_value=0,
+                       size='_cffi_d_%s' % name))
 
     # ----------
     # named structs or unions
diff --git a/lib_pypy/cffi/vengine_cpy.py b/lib_pypy/cffi/vengine_cpy.py
--- a/lib_pypy/cffi/vengine_cpy.py
+++ b/lib_pypy/cffi/vengine_cpy.py
@@ -886,7 +886,8 @@
             PyLong_FromLongLong((long long)x)))
 
 #define _cffi_to_c_int(o, type)                                          \
-    (sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
+    ((type)(                                                             \
+     sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
                                          : (type)_cffi_to_c_i8(o)) :     \
      sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o)       \
                                          : (type)_cffi_to_c_i16(o)) :    \
@@ -894,7 +895,7 @@
                                          : (type)_cffi_to_c_i32(o)) :    \
      sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o)       \
                                          : (type)_cffi_to_c_i64(o)) :    \
-     (Py_FatalError("unsupported size for type " #type), (type)0))
+     (Py_FatalError("unsupported size for type " #type), (type)0)))
 
 #define _cffi_to_c_i8                                                    \
                  ((int(*)(PyObject *))_cffi_exports[1])
diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py
--- a/pypy/module/_cffi_backend/__init__.py
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -2,7 +2,7 @@
 from pypy.interpreter.mixedmodule import MixedModule
 from rpython.rlib import rdynload
 
-VERSION = "1.0.3"
+VERSION = "1.0.4"
 
 
 class Module(MixedModule):
diff --git a/pypy/module/_cffi_backend/ffi_obj.py b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -10,7 +10,7 @@
 from pypy.module._cffi_backend import parse_c_type, realize_c_type
 from pypy.module._cffi_backend import newtype, cerrno, ccallback, ctypearray
 from pypy.module._cffi_backend import ctypestruct, ctypeptr, handle
-from pypy.module._cffi_backend import cbuffer, func, cgc, structwrapper
+from pypy.module._cffi_backend import cbuffer, func, cgc, wrapper
 from pypy.module._cffi_backend import cffi_opcode
 from pypy.module._cffi_backend.ctypeobj import W_CType
 from pypy.module._cffi_backend.cdataobj import W_CData
@@ -201,13 +201,13 @@
 in case of nested structures or arrays.
 
 3. ffi.addressof(<library>, "name") returns the address of the named
-global variable."""
+function or global variable."""
         #
         from pypy.module._cffi_backend.lib_obj import W_LibObject
         space = self.space
         if isinstance(w_arg, W_LibObject) and len(args_w) == 1:
             # case 3 in the docstring
-            return w_arg.address_of_global_var(space.str_w(args_w[0]))
+            return w_arg.address_of_func_or_global_var(space.str_w(args_w[0]))
         #
         w_ctype = self.ffi_type(w_arg, ACCEPT_CDATA)
         if len(args_w) == 0:
@@ -478,7 +478,7 @@
 corresponding <ctype> object.
 It can also be used on 'cdata' instance to get its C type."""
         #
-        if isinstance(w_arg, structwrapper.W_StructWrapper):
+        if isinstance(w_arg, wrapper.W_FunctionWrapper):
             return w_arg.typeof(self)
         return self.ffi_type(w_arg, ACCEPT_STRING | ACCEPT_CDATA)
 
diff --git a/pypy/module/_cffi_backend/lib_obj.py b/pypy/module/_cffi_backend/lib_obj.py
--- a/pypy/module/_cffi_backend/lib_obj.py
+++ b/pypy/module/_cffi_backend/lib_obj.py
@@ -12,7 +12,7 @@
 from pypy.module._cffi_backend.realize_c_type import getop, getarg
 from pypy.module._cffi_backend.cdataobj import W_CData
 from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
-from pypy.module._cffi_backend.structwrapper import W_StructWrapper
+from pypy.module._cffi_backend.wrapper import W_FunctionWrapper
 
 
 class W_LibObject(W_Root):
@@ -49,7 +49,7 @@
             num += 1
         self.ffi.included_ffis_libs = includes[:]
 
-    def _build_cpython_func(self, g):
+    def _build_cpython_func(self, g, fnname):
         # Build a function: in the PyPy version, these are all equivalent
         # and 'g->address' is a pointer to a function of exactly the
         # C type specified --- almost: arguments that are structs or
@@ -64,10 +64,8 @@
         #
         ptr = rffi.cast(rffi.CCHARP, g.c_address)
         assert ptr
-        w_cdata = W_CData(self.space, ptr, w_ct)
-        if locs is not None:
-            w_cdata = W_StructWrapper(w_cdata, locs, rawfunctype)
-        return w_cdata
+        return W_FunctionWrapper(self.space, ptr, g.c_size_or_direct_fn, w_ct,
+                                 locs, rawfunctype, fnname)
 
     @jit.elidable_promote()
     def _get_attr_elidable(self, attr):
@@ -100,13 +98,13 @@
                 op == cffi_opcode.OP_CPYTHON_BLTN_N or
                 op == cffi_opcode.OP_CPYTHON_BLTN_O):
                 # A function
-                w_result = self._build_cpython_func(g)
+                w_result = self._build_cpython_func(g, attr)
                 #
             elif op == cffi_opcode.OP_GLOBAL_VAR:
                 # A global variable of the exact type specified here
                 w_ct = realize_c_type.realize_c_type(
                     self.ffi, self.ctx.c_types, getarg(g.c_type_op))
-                g_size = rffi.getintfield(g, 'c_size')
+                g_size = rffi.cast(lltype.Signed, g.c_size_or_direct_fn)
                 if g_size != w_ct.size and g_size != 0 and w_ct.size > 0:
                     raise oefmt(self.ffi.w_FFIError,
                             "global variable '%s' should be %d bytes "
@@ -199,7 +197,7 @@
                    for i in range(total)]
         return space.newlist(names_w)
 
-    def address_of_global_var(self, varname):
+    def address_of_func_or_global_var(self, varname):
         # rebuild a string object from 'varname', to do typechecks and
         # to force a unicode back to a plain string
         space = self.space
@@ -208,9 +206,15 @@
             # regular case: a global variable
             return w_value.address()
         #
-        if ((isinstance(w_value, W_CData) and
-                isinstance(w_value.ctype, W_CTypeFunc))
-            or isinstance(w_value, W_StructWrapper)):
+        if isinstance(w_value, W_FunctionWrapper):
+            # '&func' returns a regular cdata pointer-to-function
+            if w_value.directfnptr:
+                return W_CData(space, w_value.directfnptr, w_value.ctype)
+            else:
+                return w_value    # backward compatibility
+        #
+        if (isinstance(w_value, W_CData) and
+                isinstance(w_value.ctype, W_CTypeFunc)):
             # '&func' is 'func' in C, for a constant function 'func'
             return w_value
         #
diff --git a/pypy/module/_cffi_backend/parse_c_type.py b/pypy/module/_cffi_backend/parse_c_type.py
--- a/pypy/module/_cffi_backend/parse_c_type.py
+++ b/pypy/module/_cffi_backend/parse_c_type.py
@@ -23,7 +23,7 @@
                        ('name', rffi.CCHARP),
                        ('address', rffi.VOIDP),
                        ('type_op', _CFFI_OPCODE_T),
-                       ('size', rffi.SIZE_T))
+                       ('size_or_direct_fn', rffi.CCHARP))
 CDL_INTCONST_S = lltype.Struct('cdl_intconst_s',
                        ('value', rffi.ULONGLONG),
                        ('neg', rffi.INT))
diff --git a/pypy/module/_cffi_backend/src/parse_c_type.h b/pypy/module/_cffi_backend/src/parse_c_type.h
--- a/pypy/module/_cffi_backend/src/parse_c_type.h
+++ b/pypy/module/_cffi_backend/src/parse_c_type.h
@@ -1,5 +1,5 @@
 
-/* See doc/parse_c_type.rst in the source of CFFI for more information */
+/* See doc/misc/parse_c_type.rst in the source of CFFI for more information */
 
 typedef void *_cffi_opcode_t;
 
@@ -83,7 +83,8 @@
     const char *name;
     void *address;
     _cffi_opcode_t type_op;
-    size_t size;             // 0 if unknown
+    void *size_or_direct_fn;  // OP_GLOBAL_VAR: size, or 0 if unknown
+                              // OP_CPYTHON_BLTN_*: addr of direct function
 };
 
 struct _cffi_getconst_s {
diff --git a/pypy/module/_cffi_backend/structwrapper.py b/pypy/module/_cffi_backend/structwrapper.py
deleted file mode 100644
--- a/pypy/module/_cffi_backend/structwrapper.py
+++ /dev/null
@@ -1,86 +0,0 @@
-from pypy.interpreter.baseobjspace import W_Root
-from pypy.interpreter.typedef import TypeDef
-from pypy.interpreter.gateway import interp2app
-from rpython.rlib import jit
-
-from pypy.module._cffi_backend.cdataobj import W_CData
-from pypy.module._cffi_backend.cdataobj import W_CDataPtrToStructOrUnion
-from pypy.module._cffi_backend.ctypeptr import W_CTypePtrOrArray
-from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
-from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
-
-
-class W_StructWrapper(W_Root):
-    """A wrapper around a real W_CData which points to a function
-    generated in the C code.  The real W_CData has got no struct/union
-    argument (only pointers to it), and no struct/union return type
-    (it is replaced by a hidden pointer as first argument).  This
-    wrapper is callable, and the arguments it expects and returns
-    are directly the struct/union.  Calling ffi.typeof(wrapper)
-    also returns the original struct/union signature.
-    """
-    _immutable_ = True
-
-    def __init__(self, w_cdata, locs, rawfunctype):
-        space = w_cdata.space
-        ctype = w_cdata.ctype
-        assert isinstance(ctype, W_CTypeFunc)
-        assert len(ctype.fargs) == len(locs)
-        #
-        self.space = space
-        self.w_cdata = w_cdata
-        self.locs = locs
-        self.fargs = ctype.fargs
-        self.rawfunctype = rawfunctype
-
-    def typeof(self, ffi):
-        return self.rawfunctype.unwrap_as_fnptr(ffi)
-
-    @jit.unroll_safe
-    def _prepare(self, args_w, start_index):
-        # replaces struct/union arguments with ptr-to-struct/union arguments
-        space = self.space
-        locs = self.locs
-        result_w = args_w[:]
-        for i in range(start_index, min(len(args_w), len(locs))):
-            if locs[i] != 'A':
-                continue
-            w_arg = args_w[i]
-            farg = self.fargs[i]      # <ptr to struct/union>
-            assert isinstance(farg, W_CTypePtrOrArray)
-            if isinstance(w_arg, W_CData) and w_arg.ctype is farg.ctitem:
-                # fast way: we are given a W_CData "struct", so just make
-                # a new W_CData "ptr-to-struct" which points to the same
-                # raw memory.  We use unsafe_escaping_ptr(), so we have to
-                # make sure the original 'w_arg' stays alive; the easiest
-                # is to build an instance of W_CDataPtrToStructOrUnion.
-                w_arg = W_CDataPtrToStructOrUnion(
-                    space, w_arg.unsafe_escaping_ptr(), farg, w_arg)
-            else:
-                # slow way: build a new "ptr to struct" W_CData by calling
-                # the equivalent of ffi.new()
-                if space.is_w(w_arg, space.w_None):
-                    continue
-                w_arg = farg.newp(w_arg)
-            result_w[i] = w_arg
-        return result_w
-
-    def descr_call(self, args_w):
-        # If the result we want to present to the user is "returns struct",
-        # then internally allocate the struct and pass a pointer to it as
-        # a first argument.
-        if self.locs[0] == 'R':
-            w_result_cdata = self.fargs[0].newp(self.space.w_None)
-            args_w = [w_result_cdata] + args_w
-            self.w_cdata.call(self._prepare(args_w, 1))
-            assert isinstance(w_result_cdata, W_CDataPtrToStructOrUnion)
-            return w_result_cdata.structobj
-        else:
-            return self.w_cdata.call(self._prepare(args_w, 0))
-
-
-W_StructWrapper.typedef = TypeDef(
-        'FFIFuncStructWrapper',
-        __call__ = interp2app(W_StructWrapper.descr_call),
-        )
-W_StructWrapper.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -3335,4 +3335,4 @@
 
 def test_version():
     # this test is here mostly for PyPy
-    assert __version__ == "1.0.3"
+    assert __version__ == "1.0.4"
diff --git a/pypy/module/_cffi_backend/test/test_recompiler.py b/pypy/module/_cffi_backend/test/test_recompiler.py
--- a/pypy/module/_cffi_backend/test/test_recompiler.py
+++ b/pypy/module/_cffi_backend/test/test_recompiler.py
@@ -9,11 +9,14 @@
 @unwrap_spec(cdef=str, module_name=str, source=str)
 def prepare(space, cdef, module_name, source, w_includes=None):
     try:
+        import cffi
         from cffi import FFI            # <== the system one, which
-        from cffi import recompiler     # needs to be at least cffi 1.0.0
+        from cffi import recompiler     # needs to be at least cffi 1.0.4
         from cffi import ffiplatform
     except ImportError:
         py.test.skip("system cffi module not found or older than 1.0.0")
+    if cffi.__version_info__ < (1, 0, 4):
+        py.test.skip("system cffi module needs to be at least 1.0.4")
     space.appexec([], """():
         import _cffi_backend     # force it to be initialized
     """)
@@ -739,4 +742,60 @@
         #
         raises(AttributeError, ffi.addressof, lib, 'unknown_var')
         raises(AttributeError, ffi.addressof, lib, "FOOBAR")
-        assert ffi.addressof(lib, 'FetchRectBottom') == lib.FetchRectBottom
+
+    def test_defines__CFFI_(self):
+        # Check that we define the macro _CFFI_ automatically.
+        # It should be done before including Python.h, so that PyPy's Python.h
+        # can check for it.
+        ffi, lib = self.prepare("""
+            #define CORRECT 1
+        """, "test_defines__CFFI_", """
+            #ifdef _CFFI_
+            #    define CORRECT 1
+            #endif
+        """)
+        assert lib.CORRECT == 1
+
+    def test_unpack_args(self):
+        ffi, lib = self.prepare(
+            "void foo0(void); void foo1(int); void foo2(int, int);",
+            "test_unpack_args", """
+                void foo0(void) { }
+                void foo1(int x) { }
+                void foo2(int x, int y) { }
+            """)
+        assert 'foo0' in repr(lib.foo0)
+        assert 'foo1' in repr(lib.foo1)
+        assert 'foo2' in repr(lib.foo2)
+        lib.foo0()
+        lib.foo1(42)
+        lib.foo2(43, 44)
+        e1 = raises(TypeError, lib.foo0, 42)
+        e2 = raises(TypeError, lib.foo0, 43, 44)
+        e3 = raises(TypeError, lib.foo1)
+        e4 = raises(TypeError, lib.foo1, 43, 44)
+        e5 = raises(TypeError, lib.foo2)
+        e6 = raises(TypeError, lib.foo2, 42)
+        e7 = raises(TypeError, lib.foo2, 45, 46, 47)
+        assert str(e1.value) == "foo0() takes no arguments (1 given)"
+        assert str(e2.value) == "foo0() takes no arguments (2 given)"
+        assert str(e3.value) == "foo1() takes exactly one argument (0 given)"
+        assert str(e4.value) == "foo1() takes exactly one argument (2 given)"
+        assert str(e5.value) == "foo2() takes exactly 2 arguments (0 given)"
+        assert str(e6.value) == "foo2() takes exactly 2 arguments (1 given)"
+        assert str(e7.value) == "foo2() takes exactly 2 arguments (3 given)"
+
+    def test_address_of_function(self):
+        ffi, lib = self.prepare(
+            "long myfunc(long x);",
+            "test_addressof_function",
+            "char myfunc(char x) { return (char)(x + 42); }")
+        assert lib.myfunc(5) == 47
+        assert lib.myfunc(0xABC05) == 47
+        assert not isinstance(lib.myfunc, ffi.CData)
+        assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(long)")
+        addr = ffi.addressof(lib, 'myfunc')
+        assert addr(5) == 47
+        assert addr(0xABC05) == 47
+        assert isinstance(addr, ffi.CData)
+        assert ffi.typeof(addr) == ffi.typeof("long(*)(long)")
diff --git a/pypy/module/_cffi_backend/wrapper.py b/pypy/module/_cffi_backend/wrapper.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/wrapper.py
@@ -0,0 +1,115 @@
+from pypy.interpreter.error import oefmt
+from pypy.interpreter.baseobjspace import W_Root
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import interp2app
+from rpython.rlib import jit
+
+from pypy.module._cffi_backend.cdataobj import W_CData
+from pypy.module._cffi_backend.cdataobj import W_CDataPtrToStructOrUnion
+from pypy.module._cffi_backend.ctypeptr import W_CTypePtrOrArray
+from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
+from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
+
+
+class W_FunctionWrapper(W_Root):
+    """A wrapper around a real W_CData which points to a function
+    generated in the C code.  The real W_CData has got no struct/union
+    argument (only pointers to it), and no struct/union return type
+    (it is replaced by a hidden pointer as first argument).  This
+    wrapper is callable, and the arguments it expects and returns
+    are directly the struct/union.  Calling ffi.typeof(wrapper)
+    also returns the original struct/union signature.
+    """
+    _immutable_ = True
+
+    def __init__(self, space, fnptr, directfnptr, ctype,
+                 locs, rawfunctype, fnname):
+        assert isinstance(ctype, W_CTypeFunc)
+        assert ctype.cif_descr is not None     # not for '...' functions
+        assert locs is None or len(ctype.fargs) == len(locs)
+        #
+        self.space = space
+        self.fnptr = fnptr
+        self.directfnptr = directfnptr
+        self.ctype = ctype
+        self.locs = locs
+        self.rawfunctype = rawfunctype
+        self.fnname = fnname
+        self.nargs_expected = len(ctype.fargs) - (locs is not None and
+                                                  locs[0] == 'R')
+
+    def typeof(self, ffi):
+        return self.rawfunctype.unwrap_as_fnptr(ffi)
+
+    @jit.unroll_safe
+    def _prepare(self, args_w, start_index):
+        # replaces struct/union arguments with ptr-to-struct/union arguments
+        space = self.space
+        locs = self.locs
+        fargs = self.ctype.fargs
+        for i in range(start_index, len(locs)):
+            if locs[i] != 'A':
+                continue
+            w_arg = args_w[i]
+            farg = fargs[i]      # <ptr to struct/union>
+            assert isinstance(farg, W_CTypePtrOrArray)
+            if isinstance(w_arg, W_CData) and w_arg.ctype is farg.ctitem:
+                # fast way: we are given a W_CData "struct", so just make
+                # a new W_CData "ptr-to-struct" which points to the same
+                # raw memory.  We use unsafe_escaping_ptr(), so we have to
+                # make sure the original 'w_arg' stays alive; the easiest
+                # is to build an instance of W_CDataPtrToStructOrUnion.
+                w_arg = W_CDataPtrToStructOrUnion(
+                    space, w_arg.unsafe_escaping_ptr(), farg, w_arg)
+            else:
+                # slow way: build a new "ptr to struct" W_CData by calling
+                # the equivalent of ffi.new()
+                if space.is_w(w_arg, space.w_None):
+                    continue
+                w_arg = farg.newp(w_arg)
+            args_w[i] = w_arg
+
+    def descr_call(self, args_w):
+        if len(args_w) != self.nargs_expected:
+            space = self.space
+            if self.nargs_expected == 0:
+                raise oefmt(space.w_TypeError,
+                            "%s() takes no arguments (%d given)",
+                            self.fnname, len(args_w))
+            elif self.nargs_expected == 1:
+                raise oefmt(space.w_TypeError,
+                            "%s() takes exactly one argument (%d given)",
+                            self.fnname, len(args_w))
+            else:
+                raise oefmt(space.w_TypeError,
+                            "%s() takes exactly %d arguments (%d given)",
+                            self.fnname, self.nargs_expected, len(args_w))
+        #
+        if self.locs is not None:
+            # This case is if there are structs as arguments or return values.
+            # If the result we want to present to the user is "returns struct",
+            # then internally allocate the struct and pass a pointer to it as
+            # a first argument.
+            if self.locs[0] == 'R':
+                w_result_cdata = self.ctype.fargs[0].newp(self.space.w_None)
+                args_w = [w_result_cdata] + args_w
+                self._prepare(args_w, 1)
+                self.ctype._call(self.fnptr, args_w)    # returns w_None
+                assert isinstance(w_result_cdata, W_CDataPtrToStructOrUnion)
+                return w_result_cdata.structobj
+            else:
+                args_w = args_w[:]
+                self._prepare(args_w, 0)
+        #
+        return self.ctype._call(self.fnptr, args_w)
+
+    def descr_repr(self, space):
+        return space.wrap("<FFIFunctionWrapper for %s()>" % (self.fnname,))
+
+
+W_FunctionWrapper.typedef = TypeDef(
+        'FFIFunctionWrapper',
+        __repr__ = interp2app(W_FunctionWrapper.descr_repr),
+        __call__ = interp2app(W_FunctionWrapper.descr_call),
+        )
+W_FunctionWrapper.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_file/test/test_file.py b/pypy/module/_file/test/test_file.py
--- a/pypy/module/_file/test/test_file.py
+++ b/pypy/module/_file/test/test_file.py
@@ -280,6 +280,7 @@
         # well as of _io.FileIO at least in CPython 3.3.  This is
         # *not* the behavior of _io.FileIO in CPython 3.4 or 3.5;
         # see CPython's issue #21090.
+        import sys
         try:
             from posix import openpty, fdopen, write, close
         except ImportError:
@@ -288,9 +289,18 @@
         write(write_fd, 'Abc\n')
         close(write_fd)
         f = fdopen(read_fd)
-        s = f.read()
-        assert s == 'Abc\r\n'
-        raises(IOError, f.read)
+        # behavior on Linux: f.read() returns 'Abc\r\n', then the next time
+        # it raises IOError.  Behavior on OS/X (Python 2.7.5): the close()
+        # above threw away the buffer, and f.read() always returns ''.
+        if sys.platform.startswith('linux'):
+            s = f.read()
+            assert s == 'Abc\r\n'
+            raises(IOError, f.read)
+        else:
+            s = f.read()
+            assert s == ''
+            s = f.read()
+            assert s == ''
         f.close()
 
 
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -427,6 +427,7 @@
     'PyThread_ReInitTLS',
 
     'PyStructSequence_InitType', 'PyStructSequence_New',
+    'PyStructSequence_UnnamedField',
 
     'PyFunction_Type', 'PyMethod_Type', 'PyRange_Type', 'PyTraceBack_Type',
 
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
@@ -761,7 +761,6 @@
     #
     py.test.raises(AttributeError, ffi.addressof, lib, 'unknown_var')
     py.test.raises(AttributeError, ffi.addressof, lib, "FOOBAR")
-    assert ffi.addressof(lib, 'FetchRectBottom') == lib.FetchRectBottom
 
 def test_defines__CFFI_():
     # Check that we define the macro _CFFI_ automatically.
@@ -777,3 +776,48 @@
     #endif
     """)
     assert lib.CORRECT == 1
+
+def test_unpack_args():
+    ffi = FFI()
+    ffi.cdef("void foo0(void); void foo1(int); void foo2(int, int);")
+    lib = verify(ffi, "test_unpack_args", """
+    void foo0(void) { }
+    void foo1(int x) { }
+    void foo2(int x, int y) { }
+    """)
+    assert 'foo0' in repr(lib.foo0)
+    assert 'foo1' in repr(lib.foo1)
+    assert 'foo2' in repr(lib.foo2)
+    lib.foo0()
+    lib.foo1(42)
+    lib.foo2(43, 44)
+    e1 = py.test.raises(TypeError, lib.foo0, 42)
+    e2 = py.test.raises(TypeError, lib.foo0, 43, 44)
+    e3 = py.test.raises(TypeError, lib.foo1)
+    e4 = py.test.raises(TypeError, lib.foo1, 43, 44)
+    e5 = py.test.raises(TypeError, lib.foo2)
+    e6 = py.test.raises(TypeError, lib.foo2, 42)
+    e7 = py.test.raises(TypeError, lib.foo2, 45, 46, 47)
+    assert str(e1.value) == "foo0() takes no arguments (1 given)"
+    assert str(e2.value) == "foo0() takes no arguments (2 given)"
+    assert str(e3.value) == "foo1() takes exactly one argument (0 given)"
+    assert str(e4.value) == "foo1() takes exactly one argument (2 given)"
+    assert str(e5.value) == "foo2() takes exactly 2 arguments (0 given)"
+    assert str(e6.value) == "foo2() takes exactly 2 arguments (1 given)"
+    assert str(e7.value) == "foo2() takes exactly 2 arguments (3 given)"
+
+def test_address_of_function():
+    ffi = FFI()
+    ffi.cdef("long myfunc(long x);")
+    lib = verify(ffi, "test_addressof_function", """
+        char myfunc(char x) { return (char)(x + 42); }
+    """)
+    assert lib.myfunc(5) == 47
+    assert lib.myfunc(0xABC05) == 47
+    assert not isinstance(lib.myfunc, ffi.CData)
+    assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(long)")
+    addr = ffi.addressof(lib, 'myfunc')
+    assert addr(5) == 47
+    assert addr(0xABC05) == 47
+    assert isinstance(addr, ffi.CData)
+    assert ffi.typeof(addr) == ffi.typeof("long(*)(long)")
diff --git a/rpython/jit/backend/test/test_ll_random.py b/rpython/jit/backend/test/test_ll_random.py
--- a/rpython/jit/backend/test/test_ll_random.py
+++ b/rpython/jit/backend/test/test_ll_random.py
@@ -17,6 +17,10 @@
     def __init__(self, *args, **kw):
         test_random.OperationBuilder.__init__(self, *args, **kw)
         self.vtable_counter = 0
+        # note: rstrs and runicodes contain either new local strings, or
+        # constants.  In other words, all BoxPtrs here were created earlier
+        # by the trace before, and so it should be kind of fine to mutate
+        # them with strsetitem/unicodesetitem.
         self.rstrs = []
         self.runicodes = []
         self.structure_types = []
@@ -484,6 +488,8 @@
 class AbstractSetItemOperation(AbstractStringOperation):
     def produce_into(self, builder, r):
         v_string = self.get_string(builder, r)
+        if not isinstance(v_string, BoxPtr):
+            raise test_random.CannotProduceOperation  # setitem(Const, ...)
         v_index = builder.get_index(len(v_string.getref(self.ptr).chars), r)
         v_target = ConstInt(r.random_integer() % self.max)
         builder.do(self.opnum, [v_string, v_index, v_target])
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -4614,6 +4614,58 @@
         """
         self.optimize_strunicode_loop_extradescrs(ops, expected)
 
+    def test_str_equal_none3(self):
+        ops = """
+        []
+        p5 = newstr(0)
+        i0 = call(0, NULL, p5, descr=strequaldescr)
+        escape(i0)
+        jump()
+        """
+        expected = """
+        []
+        escape(0)
+        jump()
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
+
+    def test_str_equal_none4(self):
+        ops = """
+        [p1]
+        p5 = newstr(0)
+        i0 = call(0, p5, p1, descr=strequaldescr)
+        escape(i0)
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        # can't optimize more: p1 may be NULL!
+        i0 = call(0, s"", p1, descr=strequaldescr)
+        escape(i0)
+        jump(p1)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
+
+    def test_str_equal_none5(self):
+        ops = """
+        [p1]
+        guard_nonnull(p1) []
+        p5 = newstr(0)
+        i0 = call(0, p5, p1, descr=strequaldescr)
+        escape(i0)
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        guard_nonnull(p1) []
+        # p1 is not NULL, so the string comparison (p1=="") becomes:
+        i6 = strlen(p1)
+        i0 = int_eq(i6, 0)
+        escape(i0)
+        jump(p1)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
+
     def test_str_equal_nonnull1(self):
         ops = """
         [p1]
diff --git a/rpython/jit/metainterp/optimizeopt/vstring.py b/rpython/jit/metainterp/optimizeopt/vstring.py
--- a/rpython/jit/metainterp/optimizeopt/vstring.py
+++ b/rpython/jit/metainterp/optimizeopt/vstring.py
@@ -667,10 +667,15 @@
         l2box = v2.getstrlen(None, mode, None)
         if isinstance(l2box, ConstInt):
             if l2box.value == 0:
-                lengthbox = v1.getstrlen(self, mode, None)
-                seo = self.optimizer.send_extra_operation
-                seo(ResOperation(rop.INT_EQ, [lengthbox, CONST_0], resultbox))
-                return True
+                if v1.is_nonnull():
+                    lengthbox = v1.getstrlen(self, mode, None)
+                else:
+                    lengthbox = v1.getstrlen(None, mode, None)
+                if lengthbox is not None:
+                    seo = self.optimizer.send_extra_operation
+                    seo(ResOperation(rop.INT_EQ, [lengthbox, CONST_0],
+                                     resultbox))
+                    return True
             if l2box.value == 1:
                 l1box = v1.getstrlen(None, mode, None)
                 if isinstance(l1box, ConstInt) and l1box.value == 1:


More information about the pypy-commit mailing list