Author: Armin Rigo <[email protected]>
Branch: cffi-1.0
Changeset: r77256:6c7eb0e3b230
Date: 2015-05-09 17:07 +0200
http://bitbucket.org/pypy/pypy/changeset/6c7eb0e3b230/
Log: Test and fix fix fix in progress
diff --git a/pypy/module/_cffi_backend/ctypefunc.py
b/pypy/module/_cffi_backend/ctypefunc.py
--- a/pypy/module/_cffi_backend/ctypefunc.py
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -294,29 +294,33 @@
space = self.space
ctype.force_lazy_struct()
if ctype._custom_field_pos:
- raise OperationError(space.w_TypeError,
- space.wrap(
- "cannot pass as an argument a struct that was completed "
- "with verify() (see pypy/module/_cffi_backend/ctypefunc.py "
- "for details)"))
+ # these NotImplementedErrors may be caught and ignored until
+ # a real call is made to a function of this type
+ place = "return value" if is_result_type else "argument"
+ raise oefmt(space.w_NotImplementedError,
+ "ctype '%s' not supported as %s (it is a struct declared "
+ "with \"...;\", but the C calling convention may depend "
+ "on the missing fields)", ctype.name, place)
# walk the fields, expanding arrays into repetitions; first,
# only count how many flattened fields there are
nflat = 0
for i, cf in enumerate(ctype._fields_list):
if cf.is_bitfield():
+ place = "return value" if is_result_type else "argument"
raise oefmt(space.w_NotImplementedError,
- "ctype '%s' not supported as argument or return value"
- " (it is a struct with bit fields)", ctype.name)
+ "ctype '%s' not supported as %s"
+ " (it is a struct with bit fields)", ctype.name, place)
flat = 1
ct = cf.ctype
while isinstance(ct, ctypearray.W_CTypeArray):
flat *= ct.length
ct = ct.ctitem
if flat <= 0:
+ place = "return value" if is_result_type else "argument"
raise oefmt(space.w_NotImplementedError,
- "ctype '%s' not supported as argument or return value"
- " (it is a struct with a zero-length array)", ctype.name)
+ "ctype '%s' not supported as %s (it is a struct"
+ " with a zero-length array)", ctype.name, place)
nflat += flat
if USE_C_LIBFFI_MSVC and is_result_type:
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
@@ -9,7 +9,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
+from pypy.module._cffi_backend import cbuffer, func, cgc, structwrapper
from pypy.module._cffi_backend.ctypeobj import W_CType
from pypy.module._cffi_backend.cdataobj import W_CData
@@ -55,10 +55,12 @@
x = self.types_dict[string] # KeyError if not found
if isinstance(x, W_CType):
return x
- elif consider_fn_as_fnptr:
- return realize_c_type.unwrap_fn_as_fnptr(x)
else:
- return realize_c_type.unexpected_fn_type(self, x)
+ assert isinstance(x, realize_c_type.W_RawFuncType)
+ if consider_fn_as_fnptr:
+ return x.unwrap_as_fnptr(self)
+ else:
+ return x.unexpected_fn_type(self)
@jit.dont_look_inside
def parse_string_to_type(self, string, consider_fn_as_fnptr):
@@ -78,6 +80,7 @@
" " * num_spaces)
x = realize_c_type.realize_c_type_or_func(
self, self.ctxobj.info.c_output, index)
+ assert x is not None
self.types_dict[string] = x
return self.get_string_to_type(string, consider_fn_as_fnptr)
@@ -402,6 +405,8 @@
corresponding <ctype> object.
It can also be used on 'cdata' instance to get its C type."""
#
+ if isinstance(w_arg, structwrapper.W_StructWrapper):
+ 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
@@ -10,6 +10,7 @@
from pypy.module._cffi_backend import cffi_opcode, cglob
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.structwrapper import W_StructWrapper
class W_LibObject(W_Root):
@@ -38,6 +39,25 @@
num += 1
self.ffi.included_libs = includes[:]
+ def _build_cpython_func(self, g):
+ # 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
+ # unions are replaced with pointers, and a return value that
+ # would be struct or union is instead handled by passing
+ # inside the function a hidden first pointer argument.
+ rawfunctype = realize_c_type.realize_c_type_or_func(
+ self.ffi, self.ctx.c_types, getarg(g.c_type_op))
+ assert isinstance(rawfunctype, realize_c_type.W_RawFuncType)
+ #
+ w_ct, locs = rawfunctype.unwrap_as_nostruct_fnptr(self.ffi)
+ #
+ ptr = rffi.cast(rffi.CCHARP, g.c_address)
+ 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
+
@jit.elidable_promote()
def _get_attr_elidable(self, attr):
return self.dict_w[attr] # KeyError if not found
@@ -63,14 +83,8 @@
if (op == cffi_opcode.OP_CPYTHON_BLTN_V or
op == cffi_opcode.OP_CPYTHON_BLTN_N or
op == cffi_opcode.OP_CPYTHON_BLTN_O):
- # 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
- w_ct = realize_c_type.realize_c_type_or_func(
- self.ffi, self.ctx.c_types, getarg(g.c_type_op))
- w_ct = realize_c_type.unwrap_fn_as_fnptr(w_ct)
- ptr = rffi.cast(rffi.CCHARP, g.c_address)
- w_result = W_CData(space, ptr, w_ct)
+ # A function
+ w_result = self._build_cpython_func(g)
#
elif op == cffi_opcode.OP_GLOBAL_VAR:
# A global variable of the exact type specified here
diff --git a/pypy/module/_cffi_backend/realize_c_type.py
b/pypy/module/_cffi_backend/realize_c_type.py
--- a/pypy/module/_cffi_backend/realize_c_type.py
+++ b/pypy/module/_cffi_backend/realize_c_type.py
@@ -124,23 +124,64 @@
class W_RawFuncType(W_Root):
"""Temporary: represents a C function type (not a function pointer)"""
- def __init__(self, w_ctfuncptr):
- self.w_ctfuncptr = w_ctfuncptr
+ _ctfuncptr = None
+ _nostruct_ctfuncptr = (None, None)
-def unwrap_fn_as_fnptr(x):
- assert isinstance(x, W_RawFuncType)
- return x.w_ctfuncptr
+ def __init__(self, opcodes, base_index):
+ self.opcodes = opcodes
+ self.base_index = base_index
-def unexpected_fn_type(ffi, x):
- x = unwrap_fn_as_fnptr(x)
- # here, x.name is for example 'int(*)(int)'
- # ^
- j = x.name_position - 2
- assert j >= 0
- text1 = x.name[:j]
- text2 = x.name[x.name_position+1:]
- raise oefmt(ffi.w_FFIError, "the type '%s%s' is a function type, not a "
- "pointer-to-function type", text1, text2)
+ def _unpack(self, ffi):
+ opcodes = self.opcodes
+ base_index = self.base_index
+ assert getop(opcodes[base_index]) == cffi_opcode.OP_FUNCTION
+ fret = realize_c_type(ffi, opcodes, getarg(opcodes[base_index]))
+ base_index += 1
+ num_args = 0
+ OP_FUNCTION_END = cffi_opcode.OP_FUNCTION_END
+ while getop(opcodes[base_index + num_args]) != OP_FUNCTION_END:
+ num_args += 1
+ ellipsis = (getarg(opcodes[base_index + num_args]) & 1) != 0
+ fargs = [realize_c_type(ffi, opcodes, base_index + i)
+ for i in range(num_args)]
+ return fargs, fret, ellipsis
+
+ def unwrap_as_fnptr(self, ffi):
+ if self._ctfuncptr is None:
+ fargs, fret, ellipsis = self._unpack(ffi)
+ self._ctfuncptr = newtype._new_function_type(
+ ffi.space, fargs, fret, ellipsis)
+ return self._ctfuncptr
+
+ def unwrap_as_nostruct_fnptr(self, ffi):
+ if self._nostruct_ctfuncptr[0] is None:
+ fargs, fret, ellipsis = self._unpack(ffi)
+ locs = []
+ for i in range(len(fargs)):
+ farg = fargs[i]
+ if isinstance(farg, ctypestruct.W_CTypeStructOrUnion):
+ farg = newtype.new_pointer_type(ffi.space, farg)
+ fargs[i] = farg
+ locs.append(i)
+ if isinstance(fret, ctypestruct.W_CTypeStructOrUnion):
+ xxx
+ ctfuncptr = newtype._new_function_type(
+ ffi.space, fargs, fret, ellipsis)
+ if not locs:
+ locs = None
+ else:
+ locs = locs[:]
+ self._nostruct_ctfuncptr = (ctfuncptr, locs)
+ return self._nostruct_ctfuncptr
+
+ def unexpected_fn_type(self, ffi):
+ fargs, fret, ellipsis = self._unpack(ffi)
+ sargs = ', '.join([farg.name for farg in fargs])
+ sret1 = fret.name[:fret.name_position]
+ sret2 = fret.name[fret.name_position:]
+ raise oefmt(ffi.w_FFIError,
+ "the type '%s(%s)%s' is a function type, not a "
+ "pointer-to-function type", sret1, sargs, sret2)
def realize_c_type(ffi, opcodes, index):
@@ -149,7 +190,8 @@
"""
x = realize_c_type_or_func(ffi, opcodes, index)
if not isinstance(x, W_CType):
- unexpected_fn_type(ffi, x)
+ assert isinstance(x, W_RawFuncType)
+ raise x.unexpected_fn_type(ffi)
return x
@@ -270,7 +312,7 @@
if isinstance(y, W_CType):
x = newtype.new_pointer_type(ffi.space, y)
elif isinstance(y, W_RawFuncType):
- x = y.w_ctfuncptr
+ x = y.unwrap_as_fnptr(ffi)
else:
raise NotImplementedError
@@ -288,17 +330,7 @@
x = _realize_c_enum(ffi, getarg(op))
elif case == cffi_opcode.OP_FUNCTION:
- y = realize_c_type(ffi, opcodes, getarg(op))
- base_index = index + 1
- num_args = 0
- OP_FUNCTION_END = cffi_opcode.OP_FUNCTION_END
- while getop(opcodes[base_index + num_args]) != OP_FUNCTION_END:
- num_args += 1
- ellipsis = (getarg(opcodes[base_index + num_args]) & 1) != 0
- fargs = [realize_c_type(ffi, opcodes, base_index + i)
- for i in range(num_args)]
- w_ctfuncptr = newtype._new_function_type(ffi.space, fargs, y, ellipsis)
- x = W_RawFuncType(w_ctfuncptr)
+ x = W_RawFuncType(opcodes, index)
elif case == cffi_opcode.OP_NOOP:
x = realize_c_type_or_func(ffi, opcodes, getarg(op))
diff --git a/pypy/module/_cffi_backend/structwrapper.py
b/pypy/module/_cffi_backend/structwrapper.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/structwrapper.py
@@ -0,0 +1,41 @@
+from pypy.interpreter.baseobjspace import W_Root
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import interp2app
+
+from pypy.module._cffi_backend.cdataobj import W_CData
+from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
+from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
+
+
+class W_StructWrapper(W_Root):
+ def __init__(self, w_cdata, locs, rawfunctype):
+ self.w_cdata = w_cdata
+ self.locs = locs
+ self.rawfunctype = rawfunctype
+
+ def typeof(self, ffi):
+ return self.rawfunctype.unwrap_as_fnptr(ffi)
+
+ def descr_call(self, args_w):
+ ctype = self.w_cdata.ctype
+ assert isinstance(ctype, W_CTypeFunc)
+ args_w = args_w[:]
+ for loc in self.locs:
+ if loc >= len(args_w):
+ continue # the real call will complain
+ w_arg = args_w[loc]
+ if not isinstance(w_arg, W_CData):
+ continue # the real call will complain
+ if not isinstance(w_arg.ctype, W_CTypeStructOrUnion):
+ continue # the real call will complain
+ w_arg = W_CData(w_arg.space, w_arg.unsafe_escaping_ptr(),
+ ctype.fargs[loc])
+ args_w[loc] = w_arg
+ return self.w_cdata.call(args_w)
+
+
+W_StructWrapper.typedef = TypeDef(
+ 'FFIStructWrapper',
+ __call__ = interp2app(W_StructWrapper.descr_call),
+ )
+W_StructWrapper.typedef.acceptable_as_base_class = False
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
@@ -607,3 +607,26 @@
s.a = -512
raises(OverflowError, "s.a = -513")
assert s.a == -512
+
+ def test_incomplete_struct_as_arg(self):
+ ffi, lib = self.prepare(
+ "struct foo_s { int x; ...; }; int f(int, struct foo_s);",
+ "test_incomplete_struct_as_arg",
+ "struct foo_s { int a, x, z; };\n"
+ "int f(int b, struct foo_s s) { return s.x * b; }")
+ s = ffi.new("struct foo_s *", [21])
+ assert s.x == 21
+ assert ffi.sizeof(s[0]) == 12
+ assert ffi.offsetof(ffi.typeof(s), 'x') == 4
+ assert lib.f(2, s[0]) == 42
+ assert ffi.typeof(lib.f) == ffi.typeof("int(*)(int, struct foo_s)")
+
+ def test_incomplete_struct_as_result(self):
+ ffi, lib = self.prepare(
+ "struct foo_s { int x; ...; }; struct foo_s f(int);",
+ "test_incomplete_struct_as_result",
+ "struct foo_s { int a, x, z; };\n"
+ "struct foo_s f(int x) { struct foo_s r; r.x = x * 2; return r; }")
+ s = lib.f(21)
+ assert s.x == 42
+ assert ffi.typeof(lib.f) == ffi.typeof("struct foo_s(*)(int)")
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit