[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