[pypy-commit] cffi release-0.9: hg merge default
arigo
noreply at buildbot.pypy.org
Sun May 17 09:59:34 CEST 2015
Author: Armin Rigo <arigo at tunes.org>
Branch: release-0.9
Changeset: r2021:fcb2195c0347
Date: 2015-05-17 10:00 +0200
http://bitbucket.org/cffi/cffi/changeset/fcb2195c0347/
Log: hg merge default
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -3761,9 +3761,10 @@
for (i=0; i<nb_fields; i++) {
PyObject *fname;
CTypeDescrObject *ftype;
- int fbitsize = -1, falign, do_align, foffset = -1;
-
- if (!PyArg_ParseTuple(PyList_GET_ITEM(fields, i), "O!O!|ii:list item",
+ int fbitsize = -1, falign, do_align;
+ Py_ssize_t foffset = -1;
+
+ if (!PyArg_ParseTuple(PyList_GET_ITEM(fields, i), "O!O!|in:list item",
&PyText_Type, &fname,
&CTypeDescr_Type, &ftype,
&fbitsize, &foffset))
@@ -5581,7 +5582,7 @@
static RETURNTYPE _cffi_to_c_i##SIZE(PyObject *obj) { \
PY_LONG_LONG tmp = _my_PyLong_AsLongLong(obj); \
if ((tmp > (PY_LONG_LONG)((1ULL<<(SIZE-1)) - 1)) || \
- (tmp < (PY_LONG_LONG)(-(1ULL<<(SIZE-1))))) \
+ (tmp < (PY_LONG_LONG)(0ULL-(1ULL<<(SIZE-1))))) \
if (!PyErr_Occurred()) \
return (RETURNTYPE)_convert_overflow(obj, #SIZE "-bit int"); \
return (RETURNTYPE)tmp; \
diff --git a/c/libffi_msvc/ffi.c b/c/libffi_msvc/ffi.c
--- a/c/libffi_msvc/ffi.c
+++ b/c/libffi_msvc/ffi.c
@@ -119,7 +119,7 @@
argp += z;
}
- if (argp - stack > ecif->cif->bytes)
+ if (argp - stack > (long)ecif->cif->bytes)
{
Py_FatalError("FFI BUG: not enough stack space for arguments");
}
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -266,7 +266,10 @@
#
if decl.name:
tp = self._get_type(node, partial_length_ok=True)
- if self._is_constant_globalvar(node):
+ if tp.is_raw_function:
+ tp = self._get_type_pointer(tp)
+ self._declare('function ' + decl.name, tp)
+ elif self._is_constant_globalvar(node):
self._declare('constant ' + decl.name, tp)
else:
self._declare('variable ' + decl.name, tp)
@@ -290,9 +293,13 @@
assert '__dotdotdot__' not in name.split()
self._declarations[name] = obj
- def _get_type_pointer(self, type, const=False):
+ def _get_type_pointer(self, type, const=False, declname=None):
if isinstance(type, model.RawFunctionType):
return type.as_function_pointer()
+ if (isinstance(type, model.StructOrUnionOrEnum) and
+ type.name.startswith('$') and type.name[1:].isdigit() and
+ type.forcename is None and declname is not None):
+ return model.NamedPointerType(type, declname)
if const:
return model.ConstPointerType(type)
return model.PointerType(type)
@@ -319,7 +326,8 @@
# pointer type
const = (isinstance(typenode.type, pycparser.c_ast.TypeDecl)
and 'const' in typenode.type.quals)
- return self._get_type_pointer(self._get_type(typenode.type), const)
+ return self._get_type_pointer(self._get_type(typenode.type), const,
+ declname=name)
#
if isinstance(typenode, pycparser.c_ast.TypeDecl):
type = typenode.type
diff --git a/cffi/ffiplatform.py b/cffi/ffiplatform.py
--- a/cffi/ffiplatform.py
+++ b/cffi/ffiplatform.py
@@ -1,4 +1,4 @@
-import os
+import sys, os
class VerificationError(Exception):
@@ -14,7 +14,17 @@
LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs',
'extra_objects', 'depends']
+def _hack_at_distutils():
+ # Windows-only workaround for some configurations: see
+ # https://bugs.python.org/issue23246 (Python 2.7.9)
+ if sys.platform == "win32":
+ try:
+ import setuptools # for side-effects, patches distutils
+ except ImportError:
+ pass
+
def get_extension(srcfilename, modname, sources=(), **kwds):
+ _hack_at_distutils() # *before* the following import
from distutils.core import Extension
allsources = [srcfilename]
allsources.extend(sources)
@@ -37,6 +47,7 @@
def _build(tmpdir, ext):
# XXX compact but horrible :-(
+ _hack_at_distutils()
from distutils.core import Distribution
import distutils.errors
#
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -102,8 +102,26 @@
'uint32_t': 'i',
'int64_t': 'i',
'uint64_t': 'i',
+ 'int_least8_t': 'i',
+ 'uint_least8_t': 'i',
+ 'int_least16_t': 'i',
+ 'uint_least16_t': 'i',
+ 'int_least32_t': 'i',
+ 'uint_least32_t': 'i',
+ 'int_least64_t': 'i',
+ 'uint_least64_t': 'i',
+ 'int_fast8_t': 'i',
+ 'uint_fast8_t': 'i',
+ 'int_fast16_t': 'i',
+ 'uint_fast16_t': 'i',
+ 'int_fast32_t': 'i',
+ 'uint_fast32_t': 'i',
+ 'int_fast64_t': 'i',
+ 'uint_fast64_t': 'i',
'intptr_t': 'i',
'uintptr_t': 'i',
+ 'intmax_t': 'i',
+ 'uintmax_t': 'i',
'ptrdiff_t': 'i',
'size_t': 'i',
'ssize_t': 'i',
diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py
--- a/cffi/vengine_cpy.py
+++ b/cffi/vengine_cpy.py
@@ -141,19 +141,23 @@
def load_library(self, flags=None):
# XXX review all usages of 'self' here!
# import it as a new extension module
- if hasattr(sys, "getdlopenflags"):
- previous_flags = sys.getdlopenflags()
+ imp.acquire_lock()
try:
- if hasattr(sys, "setdlopenflags") and flags is not None:
- sys.setdlopenflags(flags)
- module = imp.load_dynamic(self.verifier.get_module_name(),
- self.verifier.modulefilename)
- except ImportError as e:
- error = "importing %r: %s" % (self.verifier.modulefilename, e)
- raise ffiplatform.VerificationError(error)
+ if hasattr(sys, "getdlopenflags"):
+ previous_flags = sys.getdlopenflags()
+ try:
+ if hasattr(sys, "setdlopenflags") and flags is not None:
+ sys.setdlopenflags(flags)
+ module = imp.load_dynamic(self.verifier.get_module_name(),
+ self.verifier.modulefilename)
+ except ImportError as e:
+ error = "importing %r: %s" % (self.verifier.modulefilename, e)
+ raise ffiplatform.VerificationError(error)
+ finally:
+ if hasattr(sys, "setdlopenflags"):
+ sys.setdlopenflags(previous_flags)
finally:
- if hasattr(sys, "setdlopenflags"):
- sys.setdlopenflags(previous_flags)
+ imp.release_lock()
#
# call loading_cpy_struct() to get the struct layout inferred by
# the C compiler
diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py
--- a/cffi/vengine_gen.py
+++ b/cffi/vengine_gen.py
@@ -149,15 +149,21 @@
context = 'argument of %s' % name
arglist = [type.get_c_name(' %s' % arg, context)
for type, arg in zip(tp.args, argnames)]
+ tpresult = tp.result
+ if isinstance(tpresult, model.StructOrUnion):
+ arglist.insert(0, tpresult.get_c_name(' *r', context))
+ tpresult = model.void_type
arglist = ', '.join(arglist) or 'void'
wrappername = '_cffi_f_%s' % name
self.export_symbols.append(wrappername)
funcdecl = ' %s(%s)' % (wrappername, arglist)
context = 'result of %s' % name
- prnt(tp.result.get_c_name(funcdecl, context))
+ prnt(tpresult.get_c_name(funcdecl, context))
prnt('{')
#
- if not isinstance(tp.result, model.VoidType):
+ if isinstance(tp.result, model.StructOrUnion):
+ result_code = '*r = '
+ elif not isinstance(tp.result, model.VoidType):
result_code = 'return '
else:
result_code = ''
@@ -174,15 +180,26 @@
else:
indirections = []
base_tp = tp
- if any(isinstance(typ, model.StructOrUnion) for typ in tp.args):
+ if (any(isinstance(typ, model.StructOrUnion) for typ in tp.args)
+ or isinstance(tp.result, model.StructOrUnion)):
indirect_args = []
for i, typ in enumerate(tp.args):
if isinstance(typ, model.StructOrUnion):
typ = model.PointerType(typ)
indirections.append((i, typ))
indirect_args.append(typ)
+ indirect_result = tp.result
+ if isinstance(indirect_result, model.StructOrUnion):
+ if indirect_result.fldtypes is None:
+ raise TypeError("'%s' is used as result type, "
+ "but is opaque" % (
+ indirect_result._get_c_name(),))
+ indirect_result = model.PointerType(indirect_result)
+ indirect_args.insert(0, indirect_result)
+ indirections.insert(0, ("result", indirect_result))
+ indirect_result = model.void_type
tp = model.FunctionPtrType(tuple(indirect_args),
- tp.result, tp.ellipsis)
+ indirect_result, tp.ellipsis)
BFunc = self.ffi._get_cached_btype(tp)
wrappername = '_cffi_f_%s' % name
newfunction = module.load_function(BFunc, wrappername)
@@ -195,9 +212,16 @@
def _make_struct_wrapper(self, oldfunc, i, tp, base_tp):
backend = self.ffi._backend
BType = self.ffi._get_cached_btype(tp)
- def newfunc(*args):
- args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:]
- return oldfunc(*args)
+ if i == "result":
+ ffi = self.ffi
+ def newfunc(*args):
+ res = ffi.new(BType)
+ oldfunc(res, *args)
+ return res[0]
+ else:
+ def newfunc(*args):
+ args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:]
+ return oldfunc(*args)
newfunc._cffi_base_type = base_tp
return newfunc
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -437,9 +437,9 @@
types ``TBYTE TCHAR LPCTSTR PCTSTR LPTSTR PTSTR PTBYTE PTCHAR`` are no
longer automatically defined; see ``ffi.set_unicode()`` below.
-* *New in version 0.9:* the other standard integer types from stdint.h,
+* *New in version 0.9.3:* the other standard integer types from stdint.h,
as long as they map to integers of 1, 2, 4 or 8 bytes. Larger integers
- are not supported.
+ are not supported. (Actually added in version 0.9 but this was buggy.)
.. _`common Windows types`: http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx
@@ -1078,6 +1078,23 @@
C.printf("hello, %f\n", ffi.cast("double", 42))
C.printf("hello, %s\n", ffi.new("char[]", "world"))
+Note that if you are using ``dlopen()``, the function declaration in the
+``cdef()`` must match the original one in C exactly, as usual --- in
+particular, if this function is variadic in C, then its ``cdef()``
+declaration must also be variadic. You cannot declare it in the
+``cdef()`` with fixed arguments instead, even if you plan to only call
+it with these argument types. The reason is that some architectures
+have a different calling convention depending on whether the function
+signature is fixed or not. (On x86-64, the difference can sometimes be
+seen in PyPy's JIT-generated code if some arguments are ``double``.)
+
+Note that the function signature ``int foo();`` is interpreted by CFFI
+as equivalent to ``int foo(void);``. This differs from the C standard,
+in which ``int foo();`` is really like ``int foo(...);`` and can be
+called with any arguments. (This feature of C is a pre-C89 relic: the
+arguments cannot be accessed at all in the body of ``foo()`` without
+relying on compiler-specific extensions.)
+
Callbacks
---------
@@ -1260,7 +1277,8 @@
buffer interface. This is the opposite of ``ffi.buffer()``. It gives
a (read-write) reference to the existing data, not a copy; for this
reason, and for PyPy compatibility, it does not work with the built-in
-types str or unicode or bytearray. It is meant to be used on objects
+types str or unicode or bytearray (or buffers/memoryviews on them).
+It is meant to be used on objects
containing large quantities of raw data, like ``array.array`` or numpy
arrays. It supports both the old buffer API (in Python 2.x) and the
new memoryview API. The original object is kept alive (and, in case
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -19,7 +19,7 @@
p = subprocess.Popen([pkg_config, option, 'libffi'],
stdout=subprocess.PIPE)
except OSError as e:
- if e.errno != errno.ENOENT:
+ if e.errno not in [errno.ENOENT, errno.EACCES]:
raise
else:
t = p.stdout.read().decode().strip()
@@ -109,11 +109,14 @@
use_pkg_config()
ask_supports_thread()
+if 'freebsd' in sys.platform:
+ include_dirs.append('/usr/local/include')
+
if __name__ == '__main__':
from setuptools import setup, Extension
ext_modules = []
- if '__pypy__' not in sys.modules:
+ if '__pypy__' not in sys.builtin_module_names:
ext_modules.append(Extension(
name='_cffi_backend',
include_dirs=include_dirs,
@@ -162,5 +165,8 @@
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
+ 'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: Implementation :: CPython',
+ 'Programming Language :: Python :: Implementation :: PyPy',
],
)
diff --git a/setup_base.py b/setup_base.py
--- a/setup_base.py
+++ b/setup_base.py
@@ -8,7 +8,7 @@
if __name__ == '__main__':
from distutils.core import setup
from distutils.extension import Extension
- standard = '__pypy__' not in sys.modules
+ standard = '__pypy__' not in sys.builtin_module_names
setup(packages=['cffi'],
requires=['pycparser'],
ext_modules=[Extension(name = '_cffi_backend',
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -1388,6 +1388,17 @@
assert p.c == 14
assert p.d == 14
+ def test_nested_field_offset_align(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ struct foo_s {
+ struct { int a; char b; };
+ union { char c; };
+ };
+ """)
+ assert ffi.offsetof("struct foo_s", "c") == 2 * SIZE_OF_INT
+ assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT
+
def test_nested_anonymous_union(self):
ffi = FFI(backend=self.Backend())
ffi.cdef("""
@@ -1692,5 +1703,3 @@
assert lib.DOT_HEX == 0x100
assert lib.DOT_HEX2 == 0x10
assert lib.DOT_UL == 1000
-
-
diff --git a/testing/test_ctypes.py b/testing/test_ctypes.py
--- a/testing/test_ctypes.py
+++ b/testing/test_ctypes.py
@@ -28,6 +28,9 @@
def test_nested_anonymous_struct(self):
py.test.skip("ctypes backend: not supported: nested anonymous struct")
+ def test_nested_field_offset_align(self):
+ py.test.skip("ctypes backend: not supported: nested anonymous struct")
+
def test_nested_anonymous_union(self):
py.test.skip("ctypes backend: not supported: nested anonymous union")
diff --git a/testing/test_ffi_backend.py b/testing/test_ffi_backend.py
--- a/testing/test_ffi_backend.py
+++ b/testing/test_ffi_backend.py
@@ -222,3 +222,57 @@
assert ffi.typeof(c) is ffi.typeof("char[]")
ffi.cast("unsigned short *", c)[1] += 500
assert list(a) == [10000, 20500, 30000]
+
+ def test_all_primitives(self):
+ ffi = FFI()
+ for name in [
+ "char",
+ "short",
+ "int",
+ "long",
+ "long long",
+ "signed char",
+ "unsigned char",
+ "unsigned short",
+ "unsigned int",
+ "unsigned long",
+ "unsigned long long",
+ "float",
+ "double",
+ "long double",
+ "wchar_t",
+ "_Bool",
+ "int8_t",
+ "uint8_t",
+ "int16_t",
+ "uint16_t",
+ "int32_t",
+ "uint32_t",
+ "int64_t",
+ "uint64_t",
+ "int_least8_t",
+ "uint_least8_t",
+ "int_least16_t",
+ "uint_least16_t",
+ "int_least32_t",
+ "uint_least32_t",
+ "int_least64_t",
+ "uint_least64_t",
+ "int_fast8_t",
+ "uint_fast8_t",
+ "int_fast16_t",
+ "uint_fast16_t",
+ "int_fast32_t",
+ "uint_fast32_t",
+ "int_fast64_t",
+ "uint_fast64_t",
+ "intptr_t",
+ "uintptr_t",
+ "intmax_t",
+ "uintmax_t",
+ "ptrdiff_t",
+ "size_t",
+ "ssize_t",
+ ]:
+ x = ffi.sizeof(name)
+ assert 1 <= x <= 16
diff --git a/testing/test_function.py b/testing/test_function.py
--- a/testing/test_function.py
+++ b/testing/test_function.py
@@ -292,7 +292,6 @@
assert ffi.string(a) == b'4.4.4.4'
def test_function_typedef(self):
- py.test.skip("using really obscure C syntax")
ffi = FFI(backend=self.Backend())
ffi.cdef("""
typedef double func_t(double);
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -657,9 +657,9 @@
# case the 'static' is completely ignored.
ffi.cdef("static const int AA, BB, CC, DD;")
lib = ffi.verify("#define AA 42\n"
- "#define BB (-43)\n"
- "#define CC (22*2)\n"
- "#define DD ((unsigned int)142)\n")
+ "#define BB (-43) // blah\n"
+ "#define CC (22*2) /* foobar */\n"
+ "#define DD ((unsigned int)142) /* foo\nbar */\n")
assert lib.AA == 42
assert lib.BB == -43
assert lib.CC == 44
@@ -1197,6 +1197,15 @@
""")
assert lib.foo_func(lib.BB) == lib.BB == 2
+def test_function_typedef():
+ ffi = FFI()
+ ffi.cdef("""
+ typedef double func_t(double);
+ func_t sin;
+ """)
+ lib = ffi.verify('#include <math.h>', libraries=lib_m)
+ assert lib.sin(1.23) == math.sin(1.23)
+
def test_callback_calling_convention():
py.test.skip("later")
if sys.platform != 'win32':
@@ -1217,11 +1226,11 @@
xxx
def test_opaque_integer_as_function_result():
- import platform
- if platform.machine().startswith('sparc'):
- py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)')
- elif platform.machine() == 'mips64' and sys.maxsize > 2**32:
- py.test.skip('Segfaults on mips64el')
+ #import platform
+ #if platform.machine().startswith('sparc'):
+ # py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)')
+ #elif platform.machine() == 'mips64' and sys.maxsize > 2**32:
+ # py.test.skip('Segfaults on mips64el')
# XXX bad abuse of "struct { ...; }". It only works a bit by chance
# anyway. XXX think about something better :-(
ffi = FFI()
@@ -1236,11 +1245,45 @@
h = lib.foo()
assert ffi.sizeof(h) == ffi.sizeof("short")
+def test_return_partial_struct():
+ ffi = FFI()
+ ffi.cdef("""
+ typedef struct { int x; ...; } foo_t;
+ foo_t foo(void);
+ """)
+ lib = ffi.verify("""
+ typedef struct { int y, x; } foo_t;
+ foo_t foo(void) { foo_t r = { 45, 81 }; return r; }
+ """)
+ h = lib.foo()
+ assert ffi.sizeof(h) == 2 * ffi.sizeof("int")
+ assert h.x == 81
+
+def test_take_and_return_partial_structs():
+ ffi = FFI()
+ ffi.cdef("""
+ typedef struct { int x; ...; } foo_t;
+ foo_t foo(foo_t, foo_t);
+ """)
+ lib = ffi.verify("""
+ typedef struct { int y, x; } foo_t;
+ foo_t foo(foo_t a, foo_t b) {
+ foo_t r = { 100, a.x * 5 + b.x * 7 };
+ return r;
+ }
+ """)
+ args = ffi.new("foo_t[3]")
+ args[0].x = 1000
+ args[2].x = -498
+ h = lib.foo(args[0], args[2])
+ assert ffi.sizeof(h) == 2 * ffi.sizeof("int")
+ assert h.x == 1000 * 5 - 498 * 7
+
def test_cannot_name_struct_type():
ffi = FFI()
- ffi.cdef("typedef struct { int x; } *sp; void foo(sp);")
+ ffi.cdef("typedef struct { int x; } **sp; void foo(sp);")
e = py.test.raises(VerificationError, ffi.verify,
- "typedef struct { int x; } *sp; void foo(sp);")
+ "typedef struct { int x; } **sp; void foo(sp x) { }")
assert 'in argument of foo: unknown type name' in str(e.value)
def test_dont_check_unnamable_fields():
@@ -1637,9 +1680,8 @@
e = py.test.raises(TypeError, ffi.verify,
"typedef struct { int x; } foo_t; "
"foo_t myfunc(void) { foo_t x = { 42 }; return x; }")
- assert str(e.value) in [
- "function myfunc: 'foo_t' is used as result type, but is opaque",
- "function myfunc: result type 'foo_t' is opaque"]
+ assert str(e.value) == (
+ "function myfunc: 'foo_t' is used as result type, but is opaque")
def test_include():
ffi1 = FFI()
@@ -1667,6 +1709,17 @@
res = lib2.myfunc(lib2.AA)
assert res == 2
+def test_named_pointer_as_argument():
+ ffi = FFI()
+ ffi.cdef("typedef struct { int x; } *mystruct_p;\n"
+ "mystruct_p ff5a(mystruct_p);")
+ lib = ffi.verify("typedef struct { int x; } *mystruct_p;\n"
+ "mystruct_p ff5a(mystruct_p p) { p->x += 40; return p; }")
+ p = ffi.new("mystruct_p", [-2])
+ q = lib.ff5a(p)
+ assert q == p
+ assert p.x == 38
+
def test_enum_size():
cases = [('123', 4, 4294967295),
('4294967295U', 4, 4294967295),
More information about the pypy-commit
mailing list