[pypy-commit] pypy default: merge the ffistruct branch: it adds a very low level way to express C structures with _ffi in a very JIT-friendly way
antocuni
noreply at buildbot.pypy.org
Thu May 17 16:14:04 CEST 2012
Author: Antonio Cuni <anto.cuni at gmail.com>
Branch:
Changeset: r55127:9c77e2bd53fe
Date: 2012-05-17 16:13 +0200
http://bitbucket.org/pypy/pypy/changeset/9c77e2bd53fe/
Log: merge the ffistruct branch: it adds a very low level way to express
C structures with _ffi in a very JIT-friendly way
diff --git a/pypy/annotation/builtin.py b/pypy/annotation/builtin.py
--- a/pypy/annotation/builtin.py
+++ b/pypy/annotation/builtin.py
@@ -298,6 +298,9 @@
def rarith_intmask(s_obj):
return SomeInteger()
+def rarith_longlongmask(s_obj):
+ return SomeInteger(knowntype=pypy.rlib.rarithmetic.r_longlong)
+
def robjmodel_instantiate(s_clspbc):
assert isinstance(s_clspbc, SomePBC)
clsdef = None
@@ -376,6 +379,7 @@
BUILTIN_ANALYZERS[original] = value
BUILTIN_ANALYZERS[pypy.rlib.rarithmetic.intmask] = rarith_intmask
+BUILTIN_ANALYZERS[pypy.rlib.rarithmetic.longlongmask] = rarith_longlongmask
BUILTIN_ANALYZERS[pypy.rlib.objectmodel.instantiate] = robjmodel_instantiate
BUILTIN_ANALYZERS[pypy.rlib.objectmodel.r_dict] = robjmodel_r_dict
BUILTIN_ANALYZERS[pypy.rlib.objectmodel.hlinvoke] = robjmodel_hlinvoke
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1450,7 +1450,7 @@
self.wrap("expected a 32-bit integer"))
return value
- def truncatedint(self, w_obj):
+ def truncatedint_w(self, w_obj):
# Like space.gateway_int_w(), but return the integer truncated
# instead of raising OverflowError. For obscure cases only.
try:
@@ -1461,6 +1461,17 @@
from pypy.rlib.rarithmetic import intmask
return intmask(self.bigint_w(w_obj).uintmask())
+ def truncatedlonglong_w(self, w_obj):
+ # Like space.gateway_r_longlong_w(), but return the integer truncated
+ # instead of raising OverflowError.
+ try:
+ return self.r_longlong_w(w_obj)
+ except OperationError, e:
+ if not e.match(self, self.w_OverflowError):
+ raise
+ from pypy.rlib.rarithmetic import longlongmask
+ return longlongmask(self.bigint_w(w_obj).ulonglongmask())
+
def c_filedescriptor_w(self, w_fd):
# This is only used sometimes in CPython, e.g. for os.fsync() but
# not os.close(). It's likely designed for 'select'. It's irregular
diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py
--- a/pypy/interpreter/gateway.py
+++ b/pypy/interpreter/gateway.py
@@ -145,7 +145,7 @@
def visit_c_nonnegint(self, el, app_sig):
self.checked_space_method(el, app_sig)
- def visit_truncatedint(self, el, app_sig):
+ def visit_truncatedint_w(self, el, app_sig):
self.checked_space_method(el, app_sig)
def visit__Wrappable(self, el, app_sig):
@@ -268,8 +268,8 @@
def visit_c_nonnegint(self, typ):
self.run_args.append("space.c_nonnegint_w(%s)" % (self.scopenext(),))
- def visit_truncatedint(self, typ):
- self.run_args.append("space.truncatedint(%s)" % (self.scopenext(),))
+ def visit_truncatedint_w(self, typ):
+ self.run_args.append("space.truncatedint_w(%s)" % (self.scopenext(),))
def _make_unwrap_activation_class(self, unwrap_spec, cache={}):
try:
@@ -404,8 +404,8 @@
def visit_c_nonnegint(self, typ):
self.unwrap.append("space.c_nonnegint_w(%s)" % (self.nextarg(),))
- def visit_truncatedint(self, typ):
- self.unwrap.append("space.truncatedint(%s)" % (self.nextarg(),))
+ def visit_truncatedint_w(self, typ):
+ self.unwrap.append("space.truncatedint_w(%s)" % (self.nextarg(),))
def make_fastfunc(unwrap_spec, func):
unwrap_info = UnwrapSpec_FastFunc_Unwrap()
diff --git a/pypy/interpreter/test/test_objspace.py b/pypy/interpreter/test/test_objspace.py
--- a/pypy/interpreter/test/test_objspace.py
+++ b/pypy/interpreter/test/test_objspace.py
@@ -241,6 +241,37 @@
w_obj = space.wrap(-12)
space.raises_w(space.w_ValueError, space.r_ulonglong_w, w_obj)
+ def test_truncatedint_w(self):
+ space = self.space
+ assert space.truncatedint_w(space.wrap(42)) == 42
+ assert space.truncatedint_w(space.wrap(sys.maxint)) == sys.maxint
+ assert space.truncatedint_w(space.wrap(sys.maxint+1)) == -sys.maxint-1
+ assert space.truncatedint_w(space.wrap(-1)) == -1
+ assert space.truncatedint_w(space.wrap(-sys.maxint-2)) == sys.maxint
+
+ def test_truncatedlonglong_w(self):
+ space = self.space
+ w_value = space.wrap(12)
+ res = space.truncatedlonglong_w(w_value)
+ assert res == 12
+ assert type(res) is r_longlong
+ #
+ w_value = space.wrap(r_ulonglong(9223372036854775808))
+ res = space.truncatedlonglong_w(w_value)
+ assert res == -9223372036854775808
+ assert type(res) is r_longlong
+ #
+ w_value = space.wrap(r_ulonglong(18446744073709551615))
+ res = space.truncatedlonglong_w(w_value)
+ assert res == -1
+ assert type(res) is r_longlong
+ #
+ w_value = space.wrap(r_ulonglong(18446744073709551616))
+ res = space.truncatedlonglong_w(w_value)
+ assert res == 0
+ assert type(res) is r_longlong
+
+
def test_call_obj_args(self):
from pypy.interpreter.argument import Arguments
diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py
--- a/pypy/jit/backend/llgraph/llimpl.py
+++ b/pypy/jit/backend/llgraph/llimpl.py
@@ -823,7 +823,9 @@
op_getfield_gc_pure = op_getfield_gc
def op_getfield_raw(self, fielddescr, struct):
- if fielddescr.typeinfo == REF:
+ if fielddescr.arg_types == 'dynamic': # abuse of .arg_types
+ return do_getfield_raw_dynamic(struct, fielddescr)
+ elif fielddescr.typeinfo == REF:
return do_getfield_raw_ptr(struct, fielddescr.ofs)
elif fielddescr.typeinfo == INT:
return do_getfield_raw_int(struct, fielddescr.ofs)
@@ -919,7 +921,9 @@
raise NotImplementedError
def op_setfield_raw(self, fielddescr, struct, newvalue):
- if fielddescr.typeinfo == REF:
+ if fielddescr.arg_types == 'dynamic': # abuse of .arg_types
+ do_setfield_raw_dynamic(struct, fielddescr, newvalue)
+ elif fielddescr.typeinfo == REF:
do_setfield_raw_ptr(struct, fielddescr.ofs, newvalue)
elif fielddescr.typeinfo == INT:
do_setfield_raw_int(struct, fielddescr.ofs, newvalue)
@@ -1500,6 +1504,17 @@
def do_getfield_raw_ptr(struct, fieldnum):
return cast_to_ptr(_getfield_raw(struct, fieldnum))
+def do_getfield_raw_dynamic(struct, fielddescr):
+ from pypy.rlib import libffi
+ addr = cast_from_int(rffi.VOIDP, struct)
+ ofs = fielddescr.ofs
+ if fielddescr.is_pointer_field():
+ assert False, 'fixme'
+ elif fielddescr.is_float_field():
+ assert False, 'fixme'
+ else:
+ return libffi._struct_getfield(lltype.Signed, addr, ofs)
+
def do_new(size):
TYPE = symbolic.Size2Type[size]
x = lltype.malloc(TYPE, zero=True)
@@ -1597,6 +1612,17 @@
newvalue = cast_from_ptr(FIELDTYPE, newvalue)
setattr(ptr, fieldname, newvalue)
+def do_setfield_raw_dynamic(struct, fielddescr, newvalue):
+ from pypy.rlib import libffi
+ addr = cast_from_int(rffi.VOIDP, struct)
+ ofs = fielddescr.ofs
+ if fielddescr.is_pointer_field():
+ assert False, 'fixme'
+ elif fielddescr.is_float_field():
+ assert False, 'fixme'
+ else:
+ libffi._struct_setfield(lltype.Signed, addr, ofs, newvalue)
+
def do_newstr(length):
x = rstr.mallocstr(length)
return cast_to_ptr(x)
diff --git a/pypy/jit/backend/llgraph/runner.py b/pypy/jit/backend/llgraph/runner.py
--- a/pypy/jit/backend/llgraph/runner.py
+++ b/pypy/jit/backend/llgraph/runner.py
@@ -334,6 +334,16 @@
token = history.getkind(getattr(S, fieldname))
return self.getdescr(ofs, token[0], name=fieldname)
+ def fielddescrof_dynamic(self, offset, fieldsize, is_pointer, is_float, is_signed):
+ if is_pointer:
+ typeinfo = REF
+ elif is_float:
+ typeinfo = FLOAT
+ else:
+ typeinfo = INT
+ # we abuse the arg_types field to distinguish dynamic and static descrs
+ return self.getdescr(offset, typeinfo, arg_types='dynamic', name='<dynamic field>')
+
def interiorfielddescrof(self, A, fieldname):
S = A.OF
width = symbolic.get_size(A)
diff --git a/pypy/jit/backend/llsupport/descr.py b/pypy/jit/backend/llsupport/descr.py
--- a/pypy/jit/backend/llsupport/descr.py
+++ b/pypy/jit/backend/llsupport/descr.py
@@ -237,18 +237,25 @@
cache[(ARRAY, name)] = descr
return descr
+def compute_flag(is_pointer, is_float, is_signed):
+ if is_pointer:
+ assert not is_float
+ return FLAG_POINTER
+ elif is_float:
+ return FLAG_FLOAT
+ elif is_signed:
+ return FLAG_SIGNED
+ else:
+ return FLAG_UNSIGNED
+
+def get_dynamic_field_descr(offset, fieldsize, is_pointer, is_float, is_signed):
+ flag = compute_flag(is_pointer, is_float, is_signed)
+ return FieldDescr('dynamic', offset, fieldsize, flag)
+
def get_dynamic_interiorfield_descr(gc_ll_descr, offset, width, fieldsize,
is_pointer, is_float, is_signed):
arraydescr = ArrayDescr(0, width, None, FLAG_STRUCT)
- if is_pointer:
- assert not is_float
- flag = FLAG_POINTER
- elif is_float:
- flag = FLAG_FLOAT
- elif is_signed:
- flag = FLAG_SIGNED
- else:
- flag = FLAG_UNSIGNED
+ flag = compute_flag(is_pointer, is_float, is_signed)
fielddescr = FieldDescr('dynamic', offset, fieldsize, flag)
return InteriorFieldDescr(arraydescr, fielddescr)
diff --git a/pypy/jit/backend/llsupport/llmodel.py b/pypy/jit/backend/llsupport/llmodel.py
--- a/pypy/jit/backend/llsupport/llmodel.py
+++ b/pypy/jit/backend/llsupport/llmodel.py
@@ -11,7 +11,7 @@
from pypy.jit.backend.llsupport.descr import (
get_size_descr, get_field_descr, get_array_descr,
get_call_descr, get_interiorfield_descr, get_dynamic_interiorfield_descr,
- FieldDescr, ArrayDescr, CallDescr, InteriorFieldDescr)
+ FieldDescr, ArrayDescr, CallDescr, InteriorFieldDescr, get_dynamic_field_descr)
from pypy.jit.backend.llsupport.asmmemmgr import AsmMemoryManager
@@ -245,6 +245,9 @@
def fielddescrof(self, STRUCT, fieldname):
return get_field_descr(self.gc_ll_descr, STRUCT, fieldname)
+ def fielddescrof_dynamic(self, offset, fieldsize, is_pointer, is_float, is_signed):
+ return get_dynamic_field_descr(offset, fieldsize, is_pointer, is_float, is_signed)
+
def unpack_fielddescr(self, fielddescr):
assert isinstance(fielddescr, FieldDescr)
return fielddescr.offset
diff --git a/pypy/jit/backend/test/runner_test.py b/pypy/jit/backend/test/runner_test.py
--- a/pypy/jit/backend/test/runner_test.py
+++ b/pypy/jit/backend/test/runner_test.py
@@ -1660,20 +1660,37 @@
assert s.x == chr(190)
assert s.y == chr(150)
- def test_field_raw_pure(self):
- # This is really testing the same thing as test_field_basic but can't
- # hurt...
- S = lltype.Struct('S', ('x', lltype.Signed))
+ def test_fielddescrof_dynamic(self):
+ S = lltype.Struct('S',
+ ('x', lltype.Signed),
+ ('y', lltype.Signed),
+ )
+ longsize = rffi.sizeof(lltype.Signed)
+ y_ofs = longsize
s = lltype.malloc(S, flavor='raw')
sa = llmemory.cast_ptr_to_adr(s)
s_box = BoxInt(heaptracker.adr2int(sa))
+ #
+ field = self.cpu.fielddescrof(S, 'y')
+ field_dyn = self.cpu.fielddescrof_dynamic(offset=y_ofs,
+ fieldsize=longsize,
+ is_pointer=False,
+ is_float=False,
+ is_signed=True)
+ assert field.is_pointer_field() == field_dyn.is_pointer_field()
+ assert field.is_float_field() == field_dyn.is_float_field()
+ if 'llgraph' not in str(self.cpu):
+ assert field.is_field_signed() == field_dyn.is_field_signed()
+
+ #
for get_op, set_op in ((rop.GETFIELD_RAW, rop.SETFIELD_RAW),
(rop.GETFIELD_RAW_PURE, rop.SETFIELD_RAW)):
- fd = self.cpu.fielddescrof(S, 'x')
- self.execute_operation(set_op, [s_box, BoxInt(32)], 'void',
- descr=fd)
- res = self.execute_operation(get_op, [s_box], 'int', descr=fd)
- assert res.getint() == 32
+ for descr in (field, field_dyn):
+ self.execute_operation(set_op, [s_box, BoxInt(32)], 'void',
+ descr=descr)
+ res = self.execute_operation(get_op, [s_box], 'int', descr=descr)
+ assert res.getint() == 32
+
lltype.free(s, flavor='raw')
def test_new_with_vtable(self):
diff --git a/pypy/jit/codewriter/effectinfo.py b/pypy/jit/codewriter/effectinfo.py
--- a/pypy/jit/codewriter/effectinfo.py
+++ b/pypy/jit/codewriter/effectinfo.py
@@ -48,8 +48,10 @@
OS_LIBFFI_PREPARE = 60
OS_LIBFFI_PUSH_ARG = 61
OS_LIBFFI_CALL = 62
- OS_LIBFFI_GETARRAYITEM = 63
- OS_LIBFFI_SETARRAYITEM = 64
+ OS_LIBFFI_STRUCT_GETFIELD = 63
+ OS_LIBFFI_STRUCT_SETFIELD = 64
+ OS_LIBFFI_GETARRAYITEM = 65
+ OS_LIBFFI_SETARRAYITEM = 66
#
OS_LLONG_INVERT = 69
OS_LLONG_ADD = 70
diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -1675,6 +1675,12 @@
elif oopspec_name.startswith('libffi_call_'):
oopspecindex = EffectInfo.OS_LIBFFI_CALL
extraeffect = EffectInfo.EF_RANDOM_EFFECTS
+ elif oopspec_name == 'libffi_struct_getfield':
+ oopspecindex = EffectInfo.OS_LIBFFI_STRUCT_GETFIELD
+ extraeffect = EffectInfo.EF_CANNOT_RAISE
+ elif oopspec_name == 'libffi_struct_setfield':
+ oopspecindex = EffectInfo.OS_LIBFFI_STRUCT_SETFIELD
+ extraeffect = EffectInfo.EF_CANNOT_RAISE
elif oopspec_name == 'libffi_array_getitem':
oopspecindex = EffectInfo.OS_LIBFFI_GETARRAYITEM
extraeffect = EffectInfo.EF_CANNOT_RAISE
diff --git a/pypy/jit/codewriter/support.py b/pypy/jit/codewriter/support.py
--- a/pypy/jit/codewriter/support.py
+++ b/pypy/jit/codewriter/support.py
@@ -456,7 +456,6 @@
def _ll_3_libffi_call_void(llfunc, funcsym, ll_args):
return func(llfunc)._do_call(funcsym, ll_args, lltype.Void)
-
# in the following calls to builtins, the JIT is allowed to look inside:
inline_calls_to = [
('int_floordiv_ovf_zer', [lltype.Signed, lltype.Signed], lltype.Signed),
diff --git a/pypy/jit/metainterp/optimizeopt/fficall.py b/pypy/jit/metainterp/optimizeopt/fficall.py
--- a/pypy/jit/metainterp/optimizeopt/fficall.py
+++ b/pypy/jit/metainterp/optimizeopt/fficall.py
@@ -7,7 +7,9 @@
from pypy.rlib.libffi import Func
from pypy.rlib.objectmodel import we_are_translated
from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
-from pypy.rpython.lltypesystem import llmemory, rffi
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rlib.objectmodel import we_are_translated
+from pypy.rlib.rarithmetic import intmask
class FuncInfo(object):
@@ -118,6 +120,9 @@
ops = self.do_push_arg(op)
elif oopspec == EffectInfo.OS_LIBFFI_CALL:
ops = self.do_call(op)
+ elif (oopspec == EffectInfo.OS_LIBFFI_STRUCT_GETFIELD or
+ oopspec == EffectInfo.OS_LIBFFI_STRUCT_SETFIELD):
+ ops = self.do_struct_getsetfield(op, oopspec)
elif (oopspec == EffectInfo.OS_LIBFFI_GETARRAYITEM or
oopspec == EffectInfo.OS_LIBFFI_SETARRAYITEM):
ops = self.do_getsetarrayitem(op, oopspec)
@@ -195,6 +200,46 @@
ops.append(newop)
return ops
+ def do_struct_getsetfield(self, op, oopspec):
+ ffitypeval = self.getvalue(op.getarg(1))
+ addrval = self.getvalue(op.getarg(2))
+ offsetval = self.getvalue(op.getarg(3))
+ if not ffitypeval.is_constant() or not offsetval.is_constant():
+ return [op]
+ #
+ ffitypeaddr = ffitypeval.box.getaddr()
+ ffitype = llmemory.cast_adr_to_ptr(ffitypeaddr, clibffi.FFI_TYPE_P)
+ offset = offsetval.box.getint()
+ descr = self._get_field_descr(ffitype, offset)
+ #
+ arglist = [addrval.force_box(self.optimizer)]
+ if oopspec == EffectInfo.OS_LIBFFI_STRUCT_GETFIELD:
+ opnum = rop.GETFIELD_RAW
+ else:
+ opnum = rop.SETFIELD_RAW
+ newval = self.getvalue(op.getarg(4))
+ arglist.append(newval.force_box(self.optimizer))
+ #
+ newop = ResOperation(opnum, arglist, op.result, descr=descr)
+ return [newop]
+
+ def _get_field_descr(self, ffitype, offset):
+ kind = libffi.types.getkind(ffitype)
+ is_pointer = is_float = is_signed = False
+ if ffitype is libffi.types.pointer:
+ is_pointer = True
+ elif kind == 'i':
+ is_signed = True
+ elif kind == 'f' or kind == 'I' or kind == 'U':
+ # longlongs are treated as floats, see e.g. llsupport/descr.py:getDescrClass
+ is_float = True
+ else:
+ assert False, "unsupported ffitype or kind"
+ #
+ fieldsize = intmask(ffitype.c_size)
+ return self.optimizer.cpu.fielddescrof_dynamic(offset, fieldsize,
+ is_pointer, is_float, is_signed)
+
def do_getsetarrayitem(self, op, oopspec):
ffitypeval = self.getvalue(op.getarg(1))
widthval = self.getvalue(op.getarg(2))
@@ -245,6 +290,7 @@
offset, width, fieldsize, is_pointer, is_float, is_signed
)
+
def propagate_forward(self, op):
if self.logops is not None:
debug_print(self.logops.repr_of_resop(op))
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
@@ -56,6 +56,13 @@
restype=types.sint,
flags=43)
#
+ ffi_slong = types.slong
+ dyn_123_field = cpu.fielddescrof_dynamic(offset=123,
+ fieldsize=types.slong.c_size,
+ is_pointer=False,
+ is_float=False,
+ is_signed=True)
+ #
def calldescr(cpu, FUNC, oopspecindex, extraeffect=None):
if extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
f = None # means "can force all" really
@@ -69,6 +76,8 @@
libffi_push_arg = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_PUSH_ARG)
libffi_call = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_CALL,
EffectInfo.EF_RANDOM_EFFECTS)
+ libffi_struct_getfield = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_STRUCT_GETFIELD)
+ libffi_struct_setfield = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_STRUCT_SETFIELD)
namespace = namespace.__dict__
@@ -277,3 +286,30 @@
jump(i3, f1, p2)
"""
loop = self.optimize_loop(ops, expected)
+
+ def test_ffi_struct_fields(self):
+ ops = """
+ [i0]
+ i1 = call(0, ConstClass(ffi_slong), i0, 123, descr=libffi_struct_getfield)
+ i2 = int_add(i1, 1)
+ call(0, ConstClass(ffi_slong), i0, 123, i2, descr=libffi_struct_setfield)
+ jump(i1)
+ """
+ expected = """
+ [i0]
+ i1 = getfield_raw(i0, descr=dyn_123_field)
+ i2 = int_add(i1, 1)
+ setfield_raw(i0, i2, descr=dyn_123_field)
+ jump(i1)
+ """
+ loop = self.optimize_loop(ops, expected)
+
+ def test_ffi_struct_fields_nonconst(self):
+ ops = """
+ [i0, i1]
+ i2 = call(0, ConstClass(ffi_slong), i0, i1, descr=libffi_struct_getfield)
+ i3 = call(0, i1 , i0, 123, descr=libffi_struct_getfield)
+ jump(i1)
+ """
+ expected = ops
+ loop = self.optimize_loop(ops, expected)
diff --git a/pypy/jit/metainterp/test/test_fficall.py b/pypy/jit/metainterp/test/test_fficall.py
--- a/pypy/jit/metainterp/test/test_fficall.py
+++ b/pypy/jit/metainterp/test/test_fficall.py
@@ -4,7 +4,7 @@
from pypy.jit.metainterp.test.support import LLJitMixin
from pypy.rlib.jit import JitDriver, promote, dont_look_inside
from pypy.rlib.libffi import (ArgChain, IS_32_BIT, array_getitem, array_setitem,
- types)
+ types, struct_setfield_int, struct_getfield_int)
from pypy.rlib.objectmodel import specialize
from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
from pypy.rlib.test.test_libffi import TestLibffiCall as _TestLibffiCall
@@ -187,5 +187,24 @@
class TestFfiCallSupportAll(FfiCallTests, LLJitMixin):
supports_all = True # supports_{floats,longlong,singlefloats}
+ def test_struct_getfield(self):
+ myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'addr'])
+
+ def f(n):
+ i = 0
+ addr = lltype.malloc(rffi.VOIDP.TO, 10, flavor='raw')
+ while i < n:
+ myjitdriver.jit_merge_point(n=n, i=i, addr=addr)
+ struct_setfield_int(types.slong, addr, 0, 1)
+ i += struct_getfield_int(types.slong, addr, 0)
+ lltype.free(addr, flavor='raw')
+ return i
+ assert self.meta_interp(f, [20]) == f(20)
+ self.check_resops(
+ setfield_raw=2,
+ getfield_raw=2,
+ call=0)
+
+
class TestFfiLookup(FfiLookupTests, LLJitMixin):
pass
diff --git a/pypy/module/_ffi/__init__.py b/pypy/module/_ffi/__init__.py
--- a/pypy/module/_ffi/__init__.py
+++ b/pypy/module/_ffi/__init__.py
@@ -1,13 +1,16 @@
from pypy.interpreter.mixedmodule import MixedModule
-from pypy.module._ffi import interp_ffi
class Module(MixedModule):
interpleveldefs = {
- 'CDLL': 'interp_ffi.W_CDLL',
- 'types': 'interp_ffi.W_types',
- 'FuncPtr': 'interp_ffi.W_FuncPtr',
- 'get_libc':'interp_ffi.get_libc',
+ 'types': 'interp_ffitype.W_types',
+ 'CDLL': 'interp_funcptr.W_CDLL',
+ 'FuncPtr': 'interp_funcptr.W_FuncPtr',
+ 'get_libc':'interp_funcptr.get_libc',
+ '_StructDescr': 'interp_struct.W__StructDescr',
+ 'Field': 'interp_struct.W_Field',
}
- appleveldefs = {}
+ appleveldefs = {
+ 'Structure': 'app_struct.Structure',
+ }
diff --git a/pypy/module/_ffi/app_struct.py b/pypy/module/_ffi/app_struct.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_ffi/app_struct.py
@@ -0,0 +1,21 @@
+import _ffi
+
+class MetaStructure(type):
+
+ def __new__(cls, name, bases, dic):
+ cls._compute_shape(name, dic)
+ return type.__new__(cls, name, bases, dic)
+
+ @classmethod
+ def _compute_shape(cls, name, dic):
+ fields = dic.get('_fields_')
+ if fields is None:
+ return
+ struct_descr = _ffi._StructDescr(name, fields)
+ for field in fields:
+ dic[field.name] = field
+ dic['_struct_'] = struct_descr
+
+
+class Structure(object):
+ __metaclass__ = MetaStructure
diff --git a/pypy/module/_ffi/interp_ffitype.py b/pypy/module/_ffi/interp_ffitype.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_ffi/interp_ffitype.py
@@ -0,0 +1,181 @@
+from pypy.rlib import libffi, clibffi
+from pypy.rlib.rarithmetic import intmask
+from pypy.rlib import jit
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.typedef import TypeDef, interp_attrproperty
+from pypy.interpreter.gateway import interp2app
+from pypy.interpreter.error import OperationError
+
+class W_FFIType(Wrappable):
+
+ _immutable_fields_ = ['name', 'w_structdescr', 'w_pointer_to']
+
+ def __init__(self, name, ffitype, w_structdescr=None, w_pointer_to=None):
+ self.name = name
+ self._ffitype = clibffi.FFI_TYPE_NULL
+ self.w_structdescr = w_structdescr
+ self.w_pointer_to = w_pointer_to
+ self.set_ffitype(ffitype)
+
+ @jit.elidable
+ def get_ffitype(self):
+ if not self._ffitype:
+ raise ValueError("Operation not permitted on an incomplete type")
+ return self._ffitype
+
+ def set_ffitype(self, ffitype):
+ if self._ffitype:
+ raise ValueError("The _ffitype is already set")
+ self._ffitype = ffitype
+ if ffitype and self.is_struct():
+ assert self.w_structdescr is not None
+
+ def descr_deref_pointer(self, space):
+ if self.w_pointer_to is None:
+ return space.w_None
+ return self.w_pointer_to
+
+ def descr_sizeof(self, space):
+ try:
+ return space.wrap(self.sizeof())
+ except ValueError:
+ msg = "Operation not permitted on an incomplete type"
+ raise OperationError(space.w_ValueError, space.wrap(msg))
+
+ def sizeof(self):
+ return intmask(self.get_ffitype().c_size)
+
+ def get_alignment(self):
+ return intmask(self.get_ffitype().c_alignment)
+
+ def repr(self, space):
+ return space.wrap(self.__repr__())
+
+ def __repr__(self):
+ name = self.name
+ if not self._ffitype:
+ name += ' (incomplete)'
+ return "<ffi type %s>" % name
+
+ def is_signed(self):
+ return (self is app_types.slong or
+ self is app_types.sint or
+ self is app_types.sshort or
+ self is app_types.sbyte or
+ self is app_types.slonglong)
+
+ def is_unsigned(self):
+ return (self is app_types.ulong or
+ self is app_types.uint or
+ self is app_types.ushort or
+ self is app_types.ubyte or
+ self is app_types.ulonglong)
+
+ def is_pointer(self):
+ return self.get_ffitype() is libffi.types.pointer
+
+ def is_char(self):
+ return self is app_types.char
+
+ def is_unichar(self):
+ return self is app_types.unichar
+
+ def is_longlong(self):
+ return libffi.IS_32_BIT and (self is app_types.slonglong or
+ self is app_types.ulonglong)
+
+ def is_double(self):
+ return self is app_types.double
+
+ def is_singlefloat(self):
+ return self is app_types.float
+
+ def is_void(self):
+ return self is app_types.void
+
+ def is_struct(self):
+ return libffi.types.is_struct(self.get_ffitype())
+
+ def is_char_p(self):
+ return self is app_types.char_p
+
+ def is_unichar_p(self):
+ return self is app_types.unichar_p
+
+
+W_FFIType.typedef = TypeDef(
+ 'FFIType',
+ name = interp_attrproperty('name', W_FFIType),
+ __repr__ = interp2app(W_FFIType.repr),
+ deref_pointer = interp2app(W_FFIType.descr_deref_pointer),
+ sizeof = interp2app(W_FFIType.descr_sizeof),
+ )
+
+
+def build_ffi_types():
+ types = [
+ # note: most of the type name directly come from the C equivalent,
+ # with the exception of bytes: in C, ubyte and char are equivalent,
+ # but for _ffi the first expects a number while the second a 1-length
+ # string
+ W_FFIType('slong', libffi.types.slong),
+ W_FFIType('sint', libffi.types.sint),
+ W_FFIType('sshort', libffi.types.sshort),
+ W_FFIType('sbyte', libffi.types.schar),
+ W_FFIType('slonglong', libffi.types.slonglong),
+ #
+ W_FFIType('ulong', libffi.types.ulong),
+ W_FFIType('uint', libffi.types.uint),
+ W_FFIType('ushort', libffi.types.ushort),
+ W_FFIType('ubyte', libffi.types.uchar),
+ W_FFIType('ulonglong', libffi.types.ulonglong),
+ #
+ W_FFIType('char', libffi.types.uchar),
+ W_FFIType('unichar', libffi.types.wchar_t),
+ #
+ W_FFIType('double', libffi.types.double),
+ W_FFIType('float', libffi.types.float),
+ W_FFIType('void', libffi.types.void),
+ W_FFIType('void_p', libffi.types.pointer),
+ #
+ # missing types:
+
+ ## 's' : ffi_type_pointer,
+ ## 'z' : ffi_type_pointer,
+ ## 'O' : ffi_type_pointer,
+ ## 'Z' : ffi_type_pointer,
+
+ ]
+ d = dict([(t.name, t) for t in types])
+ w_char = d['char']
+ w_unichar = d['unichar']
+ d['char_p'] = W_FFIType('char_p', libffi.types.pointer, w_pointer_to = w_char)
+ d['unichar_p'] = W_FFIType('unichar_p', libffi.types.pointer, w_pointer_to = w_unichar)
+ return d
+
+class app_types:
+ pass
+app_types.__dict__ = build_ffi_types()
+
+def descr_new_pointer(space, w_cls, w_pointer_to):
+ try:
+ return descr_new_pointer.cache[w_pointer_to]
+ except KeyError:
+ if w_pointer_to is app_types.char:
+ w_result = app_types.char_p
+ elif w_pointer_to is app_types.unichar:
+ w_result = app_types.unichar_p
+ else:
+ w_pointer_to = space.interp_w(W_FFIType, w_pointer_to)
+ name = '(pointer to %s)' % w_pointer_to.name
+ w_result = W_FFIType(name, libffi.types.pointer, w_pointer_to = w_pointer_to)
+ descr_new_pointer.cache[w_pointer_to] = w_result
+ return w_result
+descr_new_pointer.cache = {}
+
+class W_types(Wrappable):
+ pass
+W_types.typedef = TypeDef(
+ 'types',
+ Pointer = interp2app(descr_new_pointer, as_classmethod=True),
+ **app_types.__dict__)
diff --git a/pypy/module/_ffi/interp_ffi.py b/pypy/module/_ffi/interp_funcptr.py
rename from pypy/module/_ffi/interp_ffi.py
rename to pypy/module/_ffi/interp_funcptr.py
--- a/pypy/module/_ffi/interp_ffi.py
+++ b/pypy/module/_ffi/interp_funcptr.py
@@ -3,7 +3,7 @@
operationerrfmt
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef
-from pypy.module._rawffi.structure import W_StructureInstance, W_Structure
+from pypy.module._ffi.interp_ffitype import W_FFIType
#
from pypy.rpython.lltypesystem import lltype, rffi
#
@@ -12,165 +12,16 @@
from pypy.rlib.rdynload import DLOpenError
from pypy.rlib.rarithmetic import intmask, r_uint
from pypy.rlib.objectmodel import we_are_translated
-
-class W_FFIType(Wrappable):
-
- _immutable_fields_ = ['name', 'ffitype', 'w_datashape', 'w_pointer_to']
-
- def __init__(self, name, ffitype, w_datashape=None, w_pointer_to=None):
- self.name = name
- self.ffitype = ffitype
- self.w_datashape = w_datashape
- self.w_pointer_to = w_pointer_to
- if self.is_struct():
- assert w_datashape is not None
-
- def descr_deref_pointer(self, space):
- if self.w_pointer_to is None:
- return space.w_None
- return self.w_pointer_to
-
- def repr(self, space):
- return space.wrap(self.__repr__())
-
- def __repr__(self):
- return "<ffi type %s>" % self.name
-
- def is_signed(self):
- return (self is app_types.slong or
- self is app_types.sint or
- self is app_types.sshort or
- self is app_types.sbyte or
- self is app_types.slonglong)
-
- def is_unsigned(self):
- return (self is app_types.ulong or
- self is app_types.uint or
- self is app_types.ushort or
- self is app_types.ubyte or
- self is app_types.ulonglong)
-
- def is_pointer(self):
- return self.ffitype is libffi.types.pointer
-
- def is_char(self):
- return self is app_types.char
-
- def is_unichar(self):
- return self is app_types.unichar
-
- def is_longlong(self):
- return libffi.IS_32_BIT and (self is app_types.slonglong or
- self is app_types.ulonglong)
-
- def is_double(self):
- return self is app_types.double
-
- def is_singlefloat(self):
- return self is app_types.float
-
- def is_void(self):
- return self is app_types.void
-
- def is_struct(self):
- return libffi.types.is_struct(self.ffitype)
-
- def is_char_p(self):
- return self is app_types.char_p
-
- def is_unichar_p(self):
- return self is app_types.unichar_p
-
-
-W_FFIType.typedef = TypeDef(
- 'FFIType',
- __repr__ = interp2app(W_FFIType.repr),
- deref_pointer = interp2app(W_FFIType.descr_deref_pointer),
- )
-
-
-def build_ffi_types():
- types = [
- # note: most of the type name directly come from the C equivalent,
- # with the exception of bytes: in C, ubyte and char are equivalent,
- # but for _ffi the first expects a number while the second a 1-length
- # string
- W_FFIType('slong', libffi.types.slong),
- W_FFIType('sint', libffi.types.sint),
- W_FFIType('sshort', libffi.types.sshort),
- W_FFIType('sbyte', libffi.types.schar),
- W_FFIType('slonglong', libffi.types.slonglong),
- #
- W_FFIType('ulong', libffi.types.ulong),
- W_FFIType('uint', libffi.types.uint),
- W_FFIType('ushort', libffi.types.ushort),
- W_FFIType('ubyte', libffi.types.uchar),
- W_FFIType('ulonglong', libffi.types.ulonglong),
- #
- W_FFIType('char', libffi.types.uchar),
- W_FFIType('unichar', libffi.types.wchar_t),
- #
- W_FFIType('double', libffi.types.double),
- W_FFIType('float', libffi.types.float),
- W_FFIType('void', libffi.types.void),
- W_FFIType('void_p', libffi.types.pointer),
- #
- # missing types:
-
- ## 's' : ffi_type_pointer,
- ## 'z' : ffi_type_pointer,
- ## 'O' : ffi_type_pointer,
- ## 'Z' : ffi_type_pointer,
-
- ]
- d = dict([(t.name, t) for t in types])
- w_char = d['char']
- w_unichar = d['unichar']
- d['char_p'] = W_FFIType('char_p', libffi.types.pointer, w_pointer_to = w_char)
- d['unichar_p'] = W_FFIType('unichar_p', libffi.types.pointer, w_pointer_to = w_unichar)
- return d
-
-class app_types:
- pass
-app_types.__dict__ = build_ffi_types()
-
-def descr_new_pointer(space, w_cls, w_pointer_to):
- try:
- return descr_new_pointer.cache[w_pointer_to]
- except KeyError:
- if w_pointer_to is app_types.char:
- w_result = app_types.char_p
- elif w_pointer_to is app_types.unichar:
- w_result = app_types.unichar_p
- else:
- w_pointer_to = space.interp_w(W_FFIType, w_pointer_to)
- name = '(pointer to %s)' % w_pointer_to.name
- w_result = W_FFIType(name, libffi.types.pointer, w_pointer_to = w_pointer_to)
- descr_new_pointer.cache[w_pointer_to] = w_result
- return w_result
-descr_new_pointer.cache = {}
-
-class W_types(Wrappable):
- pass
-W_types.typedef = TypeDef(
- 'types',
- Pointer = interp2app(descr_new_pointer, as_classmethod=True),
- **app_types.__dict__)
+from pypy.module._ffi.type_converter import FromAppLevelConverter, ToAppLevelConverter
def unwrap_ffitype(space, w_argtype, allow_void=False):
- res = w_argtype.ffitype
+ res = w_argtype.get_ffitype()
if res is libffi.types.void and not allow_void:
msg = 'void is not a valid argument type'
raise OperationError(space.w_TypeError, space.wrap(msg))
return res
-def unwrap_truncate_int(TP, space, w_arg):
- if space.is_true(space.isinstance(w_arg, space.w_int)):
- return rffi.cast(TP, space.int_w(w_arg))
- else:
- return rffi.cast(TP, space.bigint_w(w_arg).ulonglongmask())
-unwrap_truncate_int._annspecialcase_ = 'specialize:arg(0)'
# ========================================================================
@@ -197,101 +48,19 @@
self.func.name, expected, arg, given)
#
argchain = libffi.ArgChain()
+ argpusher = PushArgumentConverter(space, argchain, self)
for i in range(expected):
w_argtype = self.argtypes_w[i]
w_arg = args_w[i]
- if w_argtype.is_longlong():
- # note that we must check for longlong first, because either
- # is_signed or is_unsigned returns true anyway
- assert libffi.IS_32_BIT
- self.arg_longlong(space, argchain, w_arg)
- elif w_argtype.is_signed():
- argchain.arg(unwrap_truncate_int(rffi.LONG, space, w_arg))
- elif self.add_char_p_maybe(space, argchain, w_arg, w_argtype):
- # the argument is added to the argchain direcly by the method above
- pass
- elif w_argtype.is_pointer():
- w_arg = self.convert_pointer_arg_maybe(space, w_arg, w_argtype)
- argchain.arg(intmask(space.uint_w(w_arg)))
- elif w_argtype.is_unsigned():
- argchain.arg(unwrap_truncate_int(rffi.ULONG, space, w_arg))
- elif w_argtype.is_char():
- w_arg = space.ord(w_arg)
- argchain.arg(space.int_w(w_arg))
- elif w_argtype.is_unichar():
- w_arg = space.ord(w_arg)
- argchain.arg(space.int_w(w_arg))
- elif w_argtype.is_double():
- self.arg_float(space, argchain, w_arg)
- elif w_argtype.is_singlefloat():
- self.arg_singlefloat(space, argchain, w_arg)
- elif w_argtype.is_struct():
- # arg_raw directly takes value to put inside ll_args
- w_arg = space.interp_w(W_StructureInstance, w_arg)
- ptrval = w_arg.ll_buffer
- argchain.arg_raw(ptrval)
- else:
- assert False, "Argument shape '%s' not supported" % w_argtype
+ argpusher.unwrap_and_do(w_argtype, w_arg)
return argchain
- def add_char_p_maybe(self, space, argchain, w_arg, w_argtype):
- """
- Automatic conversion from string to char_p. The allocated buffer will
- be automatically freed after the call.
- """
- w_type = jit.promote(space.type(w_arg))
- if w_argtype.is_char_p() and w_type is space.w_str:
- strval = space.str_w(w_arg)
- buf = rffi.str2charp(strval)
- self.to_free.append(rffi.cast(rffi.VOIDP, buf))
- addr = rffi.cast(rffi.ULONG, buf)
- argchain.arg(addr)
- return True
- elif w_argtype.is_unichar_p() and (w_type is space.w_str or
- w_type is space.w_unicode):
- unicodeval = space.unicode_w(w_arg)
- buf = rffi.unicode2wcharp(unicodeval)
- self.to_free.append(rffi.cast(rffi.VOIDP, buf))
- addr = rffi.cast(rffi.ULONG, buf)
- argchain.arg(addr)
- return True
- return False
-
- def convert_pointer_arg_maybe(self, space, w_arg, w_argtype):
- """
- Try to convert the argument by calling _as_ffi_pointer_()
- """
- meth = space.lookup(w_arg, '_as_ffi_pointer_') # this also promotes the type
- if meth:
- return space.call_function(meth, w_arg, w_argtype)
- else:
- return w_arg
-
- def arg_float(self, space, argchain, w_arg):
- # a separate function, which can be seen by the jit or not,
- # depending on whether floats are supported
- argchain.arg(space.float_w(w_arg))
-
- def arg_longlong(self, space, argchain, w_arg):
- # a separate function, which can be seen by the jit or not,
- # depending on whether longlongs are supported
- bigarg = space.bigint_w(w_arg)
- ullval = bigarg.ulonglongmask()
- llval = rffi.cast(rffi.LONGLONG, ullval)
- argchain.arg(llval)
-
- def arg_singlefloat(self, space, argchain, w_arg):
- # a separate function, which can be seen by the jit or not,
- # depending on whether singlefloats are supported
- from pypy.rlib.rarithmetic import r_singlefloat
- fval = space.float_w(w_arg)
- sfval = r_singlefloat(fval)
- argchain.arg(sfval)
-
def call(self, space, args_w):
self = jit.promote(self)
argchain = self.build_argchain(space, args_w)
- return self._do_call(space, argchain)
+ func_caller = CallFunctionConverter(space, self.func, argchain)
+ return func_caller.do_and_wrap(self.w_restype)
+ #return self._do_call(space, argchain)
def free_temp_buffers(self, space):
for buf in self.to_free:
@@ -301,40 +70,89 @@
lltype.free(buf, flavor='raw')
self.to_free = []
- def _do_call(self, space, argchain):
- w_restype = self.w_restype
- if w_restype.is_longlong():
- # note that we must check for longlong first, because either
- # is_signed or is_unsigned returns true anyway
- assert libffi.IS_32_BIT
- return self._call_longlong(space, argchain)
- elif w_restype.is_signed():
- return self._call_int(space, argchain)
- elif w_restype.is_unsigned() or w_restype.is_pointer():
- return self._call_uint(space, argchain)
- elif w_restype.is_char():
- intres = self.func.call(argchain, rffi.UCHAR)
- return space.wrap(chr(intres))
- elif w_restype.is_unichar():
- intres = self.func.call(argchain, rffi.WCHAR_T)
- return space.wrap(unichr(intres))
- elif w_restype.is_double():
- return self._call_float(space, argchain)
- elif w_restype.is_singlefloat():
- return self._call_singlefloat(space, argchain)
- elif w_restype.is_struct():
- w_datashape = w_restype.w_datashape
- assert isinstance(w_datashape, W_Structure)
- ptrval = self.func.call(argchain, rffi.ULONG, is_struct=True)
- return w_datashape.fromaddress(space, ptrval)
- elif w_restype.is_void():
- voidres = self.func.call(argchain, lltype.Void)
- assert voidres is None
- return space.w_None
- else:
- assert False, "Return value shape '%s' not supported" % w_restype
+ def getaddr(self, space):
+ """
+ Return the physical address in memory of the function
+ """
+ return space.wrap(rffi.cast(rffi.LONG, self.func.funcsym))
- def _call_int(self, space, argchain):
+
+class PushArgumentConverter(FromAppLevelConverter):
+ """
+ A converter used by W_FuncPtr to unwrap the app-level objects into
+ low-level types and push them to the argchain.
+ """
+
+ def __init__(self, space, argchain, w_func):
+ FromAppLevelConverter.__init__(self, space)
+ self.argchain = argchain
+ self.w_func = w_func
+
+ def handle_signed(self, w_ffitype, w_obj, intval):
+ self.argchain.arg(intval)
+
+ def handle_unsigned(self, w_ffitype, w_obj, uintval):
+ self.argchain.arg(uintval)
+
+ def handle_pointer(self, w_ffitype, w_obj, intval):
+ self.argchain.arg(intval)
+
+ def handle_char(self, w_ffitype, w_obj, intval):
+ self.argchain.arg(intval)
+
+ def handle_unichar(self, w_ffitype, w_obj, intval):
+ self.argchain.arg(intval)
+
+ def handle_longlong(self, w_ffitype, w_obj, longlongval):
+ self.argchain.arg(longlongval)
+
+ def handle_char_p(self, w_ffitype, w_obj, strval):
+ buf = rffi.str2charp(strval)
+ self.w_func.to_free.append(rffi.cast(rffi.VOIDP, buf))
+ addr = rffi.cast(rffi.ULONG, buf)
+ self.argchain.arg(addr)
+
+ def handle_unichar_p(self, w_ffitype, w_obj, unicodeval):
+ buf = rffi.unicode2wcharp(unicodeval)
+ self.w_func.to_free.append(rffi.cast(rffi.VOIDP, buf))
+ addr = rffi.cast(rffi.ULONG, buf)
+ self.argchain.arg(addr)
+
+ def handle_float(self, w_ffitype, w_obj, floatval):
+ self.argchain.arg(floatval)
+
+ def handle_singlefloat(self, w_ffitype, w_obj, singlefloatval):
+ self.argchain.arg(singlefloatval)
+
+ def handle_struct(self, w_ffitype, w_structinstance):
+ # arg_raw directly takes value to put inside ll_args
+ ptrval = w_structinstance.rawmem
+ self.argchain.arg_raw(ptrval)
+
+ def handle_struct_rawffi(self, w_ffitype, w_structinstance):
+ # arg_raw directly takes value to put inside ll_args
+ ptrval = w_structinstance.ll_buffer
+ self.argchain.arg_raw(ptrval)
+
+
+class CallFunctionConverter(ToAppLevelConverter):
+ """
+ A converter used by W_FuncPtr to call the function, expect the result of
+ a correct low-level type and wrap it to the corresponding app-level type
+ """
+
+ def __init__(self, space, func, argchain):
+ ToAppLevelConverter.__init__(self, space)
+ self.func = func
+ self.argchain = argchain
+
+ def get_longlong(self, w_ffitype):
+ return self.func.call(self.argchain, rffi.LONGLONG)
+
+ def get_ulonglong(self, w_ffitype):
+ return self.func.call(self.argchain, rffi.ULONGLONG)
+
+ def get_signed(self, w_ffitype):
# if the declared return type of the function is smaller than LONG,
# the result buffer may contains garbage in its higher bits. To get
# the correct value, and to be sure to handle the signed/unsigned case
@@ -342,88 +160,66 @@
# that, we cast it back to LONG, because this is what we want to pass
# to space.wrap in order to get a nice applevel <int>.
#
- restype = self.func.restype
+ restype = w_ffitype.get_ffitype()
call = self.func.call
if restype is libffi.types.slong:
- intres = call(argchain, rffi.LONG)
+ return call(self.argchain, rffi.LONG)
elif restype is libffi.types.sint:
- intres = rffi.cast(rffi.LONG, call(argchain, rffi.INT))
+ return rffi.cast(rffi.LONG, call(self.argchain, rffi.INT))
elif restype is libffi.types.sshort:
- intres = rffi.cast(rffi.LONG, call(argchain, rffi.SHORT))
+ return rffi.cast(rffi.LONG, call(self.argchain, rffi.SHORT))
elif restype is libffi.types.schar:
- intres = rffi.cast(rffi.LONG, call(argchain, rffi.SIGNEDCHAR))
+ return rffi.cast(rffi.LONG, call(self.argchain, rffi.SIGNEDCHAR))
else:
- raise OperationError(space.w_ValueError,
- space.wrap('Unsupported restype'))
- return space.wrap(intres)
+ self.error(w_ffitype)
+
+ def get_unsigned(self, w_ffitype):
+ return self.func.call(self.argchain, rffi.ULONG)
- def _call_uint(self, space, argchain):
- # the same comment as above apply. Moreover, we need to be careful
- # when the return type is ULONG, because the value might not fit into
- # a signed LONG: this is the only case in which we cast the result to
- # something different than LONG; as a result, the applevel value will
- # be a <long>.
- #
- # Note that we check for ULONG before UINT: this is needed on 32bit
- # machines, where they are they same: if we checked for UINT before
- # ULONG, we would cast to the wrong type. Note that this also means
- # that on 32bit the UINT case will never be entered (because it is
- # handled by the ULONG case).
- restype = self.func.restype
+ def get_unsigned_which_fits_into_a_signed(self, w_ffitype):
+ # the same comment as get_signed apply
+ restype = w_ffitype.get_ffitype()
call = self.func.call
- if restype is libffi.types.ulong:
- # special case
- uintres = call(argchain, rffi.ULONG)
- return space.wrap(uintres)
- elif restype is libffi.types.pointer:
- ptrres = call(argchain, rffi.VOIDP)
- uintres = rffi.cast(rffi.ULONG, ptrres)
- return space.wrap(uintres)
- elif restype is libffi.types.uint:
- intres = rffi.cast(rffi.LONG, call(argchain, rffi.UINT))
+ if restype is libffi.types.uint:
+ assert not libffi.IS_32_BIT
+ # on 32bit machines, we should never get here, because it's a case
+ # which has already been handled by get_unsigned above.
+ return rffi.cast(rffi.LONG, call(self.argchain, rffi.UINT))
elif restype is libffi.types.ushort:
- intres = rffi.cast(rffi.LONG, call(argchain, rffi.USHORT))
+ return rffi.cast(rffi.LONG, call(self.argchain, rffi.USHORT))
elif restype is libffi.types.uchar:
- intres = rffi.cast(rffi.LONG, call(argchain, rffi.UCHAR))
+ return rffi.cast(rffi.LONG, call(self.argchain, rffi.UCHAR))
else:
- raise OperationError(space.w_ValueError,
- space.wrap('Unsupported restype'))
- return space.wrap(intres)
+ self.error(w_ffitype)
- def _call_float(self, space, argchain):
- # a separate function, which can be seen by the jit or not,
- # depending on whether floats are supported
- floatres = self.func.call(argchain, rffi.DOUBLE)
- return space.wrap(floatres)
- def _call_longlong(self, space, argchain):
- # a separate function, which can be seen by the jit or not,
- # depending on whether longlongs are supported
- restype = self.func.restype
- call = self.func.call
- if restype is libffi.types.slonglong:
- llres = call(argchain, rffi.LONGLONG)
- return space.wrap(llres)
- elif restype is libffi.types.ulonglong:
- ullres = call(argchain, rffi.ULONGLONG)
- return space.wrap(ullres)
- else:
- raise OperationError(space.w_ValueError,
- space.wrap('Unsupported longlong restype'))
+ def get_pointer(self, w_ffitype):
+ ptrres = self.func.call(self.argchain, rffi.VOIDP)
+ return rffi.cast(rffi.ULONG, ptrres)
- def _call_singlefloat(self, space, argchain):
- # a separate function, which can be seen by the jit or not,
- # depending on whether singlefloats are supported
- sfres = self.func.call(argchain, rffi.FLOAT)
- return space.wrap(float(sfres))
+ def get_char(self, w_ffitype):
+ return self.func.call(self.argchain, rffi.UCHAR)
- def getaddr(self, space):
- """
- Return the physical address in memory of the function
- """
- return space.wrap(rffi.cast(rffi.LONG, self.func.funcsym))
+ def get_unichar(self, w_ffitype):
+ return self.func.call(self.argchain, rffi.WCHAR_T)
+ def get_float(self, w_ffitype):
+ return self.func.call(self.argchain, rffi.DOUBLE)
+ def get_singlefloat(self, w_ffitype):
+ return self.func.call(self.argchain, rffi.FLOAT)
+
+ def get_struct(self, w_ffitype, w_structdescr):
+ addr = self.func.call(self.argchain, rffi.LONG, is_struct=True)
+ return w_structdescr.fromaddress(self.space, addr)
+
+ def get_struct_rawffi(self, w_ffitype, w_structdescr):
+ uintval = self.func.call(self.argchain, rffi.ULONG, is_struct=True)
+ return w_structdescr.fromaddress(self.space, uintval)
+
+ def get_void(self, w_ffitype):
+ return self.func.call(self.argchain, lltype.Void)
+
def unpack_argtypes(space, w_argtypes, w_restype):
argtypes_w = [space.interp_w(W_FFIType, w_argtype)
@@ -512,3 +308,4 @@
return space.wrap(W_CDLL(space, get_libc_name(), -1))
except OSError, e:
raise wrap_oserror(space, e)
+
diff --git a/pypy/module/_ffi/interp_struct.py b/pypy/module/_ffi/interp_struct.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_ffi/interp_struct.py
@@ -0,0 +1,319 @@
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib import clibffi
+from pypy.rlib import libffi
+from pypy.rlib import jit
+from pypy.rlib.rgc import must_be_light_finalizer
+from pypy.rlib.rarithmetic import r_uint, r_ulonglong, r_singlefloat, intmask
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.typedef import TypeDef, interp_attrproperty
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.error import operationerrfmt
+from pypy.objspace.std.typetype import type_typedef
+from pypy.module._ffi.interp_ffitype import W_FFIType, app_types
+from pypy.module._ffi.type_converter import FromAppLevelConverter, ToAppLevelConverter
+
+
+class W_Field(Wrappable):
+
+ def __init__(self, name, w_ffitype):
+ self.name = name
+ self.w_ffitype = w_ffitype
+ self.offset = -1
+
+ def __repr__(self):
+ return '<Field %s %s>' % (self.name, self.w_ffitype.name)
+
+ at unwrap_spec(name=str)
+def descr_new_field(space, w_type, name, w_ffitype):
+ w_ffitype = space.interp_w(W_FFIType, w_ffitype)
+ return W_Field(name, w_ffitype)
+
+W_Field.typedef = TypeDef(
+ 'Field',
+ __new__ = interp2app(descr_new_field),
+ name = interp_attrproperty('name', W_Field),
+ ffitype = interp_attrproperty('w_ffitype', W_Field),
+ offset = interp_attrproperty('offset', W_Field),
+ )
+
+
+# ==============================================================================
+
+class FFIStructOwner(object):
+ """
+ The only job of this class is to stay outside of the reference cycle
+ W__StructDescr -> W_FFIType -> W__StructDescr and free the ffistruct
+ """
+
+ def __init__(self, ffistruct):
+ self.ffistruct = ffistruct
+
+ @must_be_light_finalizer
+ def __del__(self):
+ if self.ffistruct:
+ lltype.free(self.ffistruct, flavor='raw', track_allocation=True)
+
+
+class W__StructDescr(Wrappable):
+
+ def __init__(self, space, name):
+ self.space = space
+ self.w_ffitype = W_FFIType('struct %s' % name, clibffi.FFI_TYPE_NULL,
+ w_structdescr=self)
+ self.fields_w = None
+ self.name2w_field = {}
+ self._ffistruct_owner = None
+
+ def define_fields(self, space, w_fields):
+ if self.fields_w is not None:
+ raise operationerrfmt(space.w_ValueError,
+ "%s's fields has already been defined",
+ self.w_ffitype.name)
+ space = self.space
+ fields_w = space.fixedview(w_fields)
+ # note that the fields_w returned by compute_size_and_alignement has a
+ # different annotation than the original: list(W_Root) vs list(W_Field)
+ size, alignment, fields_w = compute_size_and_alignement(space, fields_w)
+ self.fields_w = fields_w
+ field_types = [] # clibffi's types
+ for w_field in fields_w:
+ field_types.append(w_field.w_ffitype.get_ffitype())
+ self.name2w_field[w_field.name] = w_field
+ #
+ # on CPython, the FFIStructOwner might go into gc.garbage and thus the
+ # __del__ never be called. Thus, we don't track the allocation of the
+ # malloc done inside this function, else the leakfinder might complain
+ ffistruct = clibffi.make_struct_ffitype_e(size, alignment, field_types,
+ track_allocation=False)
+ self.w_ffitype.set_ffitype(ffistruct.ffistruct)
+ self._ffistruct_owner = FFIStructOwner(ffistruct)
+
+ def check_complete(self, space):
+ if self.fields_w is None:
+ raise operationerrfmt(space.w_ValueError, "%s has an incomplete type",
+ self.w_ffitype.name)
+
+ def allocate(self, space):
+ self.check_complete(space)
+ return W__StructInstance(self)
+
+ @unwrap_spec(addr=int)
+ def fromaddress(self, space, addr):
+ self.check_complete(space)
+ rawmem = rffi.cast(rffi.VOIDP, addr)
+ return W__StructInstance(self, allocate=False, autofree=True, rawmem=rawmem)
+
+ @jit.elidable_promote('0')
+ def get_type_and_offset_for_field(self, name):
+ try:
+ w_field = self.name2w_field[name]
+ except KeyError:
+ raise operationerrfmt(self.space.w_AttributeError, '%s', name)
+
+ return w_field.w_ffitype, w_field.offset
+
+
+
+ at unwrap_spec(name=str)
+def descr_new_structdescr(space, w_type, name, w_fields=None):
+ descr = W__StructDescr(space, name)
+ if w_fields is not space.w_None:
+ descr.define_fields(space, w_fields)
+ return descr
+
+def round_up(size, alignment):
+ return (size + alignment - 1) & -alignment
+
+def compute_size_and_alignement(space, fields_w):
+ size = 0
+ alignment = 1
+ fields_w2 = []
+ for w_field in fields_w:
+ w_field = space.interp_w(W_Field, w_field)
+ fieldsize = w_field.w_ffitype.sizeof()
+ fieldalignment = w_field.w_ffitype.get_alignment()
+ alignment = max(alignment, fieldalignment)
+ size = round_up(size, fieldalignment)
+ w_field.offset = size
+ size += fieldsize
+ fields_w2.append(w_field)
+ #
+ size = round_up(size, alignment)
+ return size, alignment, fields_w2
+
+
+
+W__StructDescr.typedef = TypeDef(
+ '_StructDescr',
+ __new__ = interp2app(descr_new_structdescr),
+ ffitype = interp_attrproperty('w_ffitype', W__StructDescr),
+ define_fields = interp2app(W__StructDescr.define_fields),
+ allocate = interp2app(W__StructDescr.allocate),
+ fromaddress = interp2app(W__StructDescr.fromaddress),
+ )
+
+
+# ==============================================================================
+
+NULL = lltype.nullptr(rffi.VOIDP.TO)
+
+class W__StructInstance(Wrappable):
+
+ _immutable_fields_ = ['structdescr', 'rawmem']
+
+ def __init__(self, structdescr, allocate=True, autofree=True, rawmem=NULL):
+ self.structdescr = structdescr
+ self.autofree = autofree
+ if allocate:
+ assert not rawmem
+ assert autofree
+ size = structdescr.w_ffitype.sizeof()
+ self.rawmem = lltype.malloc(rffi.VOIDP.TO, size, flavor='raw',
+ zero=True, add_memory_pressure=True)
+ else:
+ self.rawmem = rawmem
+
+ @must_be_light_finalizer
+ def __del__(self):
+ if self.autofree and self.rawmem:
+ lltype.free(self.rawmem, flavor='raw')
+ self.rawmem = lltype.nullptr(rffi.VOIDP.TO)
+
+ def getaddr(self, space):
+ addr = rffi.cast(rffi.ULONG, self.rawmem)
+ return space.wrap(addr)
+
+ @unwrap_spec(name=str)
+ def getfield(self, space, name):
+ w_ffitype, offset = self.structdescr.get_type_and_offset_for_field(name)
+ field_getter = GetFieldConverter(space, self.rawmem, offset)
+ return field_getter.do_and_wrap(w_ffitype)
+
+ @unwrap_spec(name=str)
+ def setfield(self, space, name, w_value):
+ w_ffitype, offset = self.structdescr.get_type_and_offset_for_field(name)
+ field_setter = SetFieldConverter(space, self.rawmem, offset)
+ field_setter.unwrap_and_do(w_ffitype, w_value)
+
+
+class GetFieldConverter(ToAppLevelConverter):
+ """
+ A converter used by W__StructInstance to get a field from the struct and
+ wrap it to the correct app-level type.
+ """
+
+ def __init__(self, space, rawmem, offset):
+ self.space = space
+ self.rawmem = rawmem
+ self.offset = offset
+
+ def get_longlong(self, w_ffitype):
+ return libffi.struct_getfield_longlong(libffi.types.slonglong,
+ self.rawmem, self.offset)
+
+ def get_ulonglong(self, w_ffitype):
+ longlongval = libffi.struct_getfield_longlong(libffi.types.ulonglong,
+ self.rawmem, self.offset)
+ return r_ulonglong(longlongval)
+
+
+ def get_signed(self, w_ffitype):
+ return libffi.struct_getfield_int(w_ffitype.get_ffitype(),
+ self.rawmem, self.offset)
+
+ def get_unsigned(self, w_ffitype):
+ value = libffi.struct_getfield_int(w_ffitype.get_ffitype(),
+ self.rawmem, self.offset)
+ return r_uint(value)
+
+ get_unsigned_which_fits_into_a_signed = get_signed
+ get_pointer = get_unsigned
+
+ def get_char(self, w_ffitype):
+ intval = libffi.struct_getfield_int(w_ffitype.get_ffitype(),
+ self.rawmem, self.offset)
+ return rffi.cast(rffi.UCHAR, intval)
+
+ def get_unichar(self, w_ffitype):
+ intval = libffi.struct_getfield_int(w_ffitype.get_ffitype(),
+ self.rawmem, self.offset)
+ return rffi.cast(rffi.WCHAR_T, intval)
+
+ def get_float(self, w_ffitype):
+ return libffi.struct_getfield_float(w_ffitype.get_ffitype(),
+ self.rawmem, self.offset)
+
+ def get_singlefloat(self, w_ffitype):
+ return libffi.struct_getfield_singlefloat(w_ffitype.get_ffitype(),
+ self.rawmem, self.offset)
+
+ def get_struct(self, w_ffitype, w_structdescr):
+ assert isinstance(w_structdescr, W__StructDescr)
+ rawmem = rffi.cast(rffi.CCHARP, self.rawmem)
+ innermem = rffi.cast(rffi.VOIDP, rffi.ptradd(rawmem, self.offset))
+ # we return a reference to the inner struct, not a copy
+ # autofree=False because it's still owned by the parent struct
+ return W__StructInstance(w_structdescr, allocate=False, autofree=False,
+ rawmem=innermem)
+
+ ## def get_void(self, w_ffitype):
+ ## ...
+
+
+class SetFieldConverter(FromAppLevelConverter):
+ """
+ A converter used by W__StructInstance to convert an app-level object to
+ the corresponding low-level value and set the field of a structure.
+ """
+
+ def __init__(self, space, rawmem, offset):
+ self.space = space
+ self.rawmem = rawmem
+ self.offset = offset
+
+ def handle_signed(self, w_ffitype, w_obj, intval):
+ libffi.struct_setfield_int(w_ffitype.get_ffitype(), self.rawmem, self.offset,
+ intval)
+
+ def handle_unsigned(self, w_ffitype, w_obj, uintval):
+ libffi.struct_setfield_int(w_ffitype.get_ffitype(), self.rawmem, self.offset,
+ intmask(uintval))
+
+ handle_pointer = handle_signed
+ handle_char = handle_signed
+ handle_unichar = handle_signed
+
+ def handle_longlong(self, w_ffitype, w_obj, longlongval):
+ libffi.struct_setfield_longlong(w_ffitype.get_ffitype(),
+ self.rawmem, self.offset, longlongval)
+
+ def handle_float(self, w_ffitype, w_obj, floatval):
+ libffi.struct_setfield_float(w_ffitype.get_ffitype(),
+ self.rawmem, self.offset, floatval)
+
+ def handle_singlefloat(self, w_ffitype, w_obj, singlefloatval):
+ libffi.struct_setfield_singlefloat(w_ffitype.get_ffitype(),
+ self.rawmem, self.offset, singlefloatval)
+
+ def handle_struct(self, w_ffitype, w_structinstance):
+ rawmem = rffi.cast(rffi.CCHARP, self.rawmem)
+ dst = rffi.cast(rffi.VOIDP, rffi.ptradd(rawmem, self.offset))
+ src = w_structinstance.rawmem
+ length = w_ffitype.sizeof()
+ rffi.c_memcpy(dst, src, length)
+
+ ## def handle_char_p(self, w_ffitype, w_obj, strval):
+ ## ...
+
+ ## def handle_unichar_p(self, w_ffitype, w_obj, unicodeval):
+ ## ...
+
+
+
+
+W__StructInstance.typedef = TypeDef(
+ '_StructInstance',
+ getaddr = interp2app(W__StructInstance.getaddr),
+ getfield = interp2app(W__StructInstance.getfield),
+ setfield = interp2app(W__StructInstance.setfield),
+ )
diff --git a/pypy/module/_ffi/test/test_ffitype.py b/pypy/module/_ffi/test/test_ffitype.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_ffi/test/test_ffitype.py
@@ -0,0 +1,39 @@
+from pypy.module._ffi.test.test_funcptr import BaseAppTestFFI
+
+class AppTestFFIType(BaseAppTestFFI):
+
+ def test_simple_types(self):
+ from _ffi import types
+ assert str(types.sint) == "<ffi type sint>"
+ assert str(types.uint) == "<ffi type uint>"
+ assert types.sint.name == 'sint'
+ assert types.uint.name == 'uint'
+
+ def test_sizeof(self):
+ from _ffi import types
+ assert types.sbyte.sizeof() == 1
+ assert types.sint.sizeof() == 4
+
+ def test_typed_pointer(self):
+ from _ffi import types
+ intptr = types.Pointer(types.sint) # create a typed pointer to sint
+ assert intptr.deref_pointer() is types.sint
+ assert str(intptr) == '<ffi type (pointer to sint)>'
+ assert types.sint.deref_pointer() is None
+ raises(TypeError, "types.Pointer(42)")
+
+ def test_pointer_identity(self):
+ from _ffi import types
+ x = types.Pointer(types.slong)
+ y = types.Pointer(types.slong)
+ z = types.Pointer(types.char)
+ assert x is y
+ assert x is not z
+
+ def test_char_p_cached(self):
+ from _ffi import types
+ x = types.Pointer(types.char)
+ assert x is types.char_p
+ x = types.Pointer(types.unichar)
+ assert x is types.unichar_p
+
diff --git a/pypy/module/_ffi/test/test__ffi.py b/pypy/module/_ffi/test/test_funcptr.py
rename from pypy/module/_ffi/test/test__ffi.py
rename to pypy/module/_ffi/test/test_funcptr.py
--- a/pypy/module/_ffi/test/test__ffi.py
+++ b/pypy/module/_ffi/test/test_funcptr.py
@@ -7,7 +7,7 @@
import os, sys, py
-class AppTestFfi:
+class BaseAppTestFFI(object):
@classmethod
def prepare_c_example(cls):
@@ -36,7 +36,6 @@
eci = ExternalCompilationInfo(export_symbols=[])
return str(platform.compile([c_file], eci, 'x', standalone=False))
-
def setup_class(cls):
from pypy.rpython.lltypesystem import rffi
from pypy.rlib.libffi import get_libc_name, CDLL, types
@@ -52,7 +51,12 @@
pow = libm.getpointer('pow', [], types.void)
pow_addr = rffi.cast(rffi.LONG, pow.funcsym)
cls.w_pow_addr = space.wrap(pow_addr)
- #
+
+class AppTestFFI(BaseAppTestFFI):
+
+ def setup_class(cls):
+ BaseAppTestFFI.setup_class.im_func(cls)
+ space = cls.space
# these are needed for test_single_float_args
from ctypes import c_float
f_12_34 = c_float(12.34).value
@@ -78,11 +82,6 @@
res = dll.getfunc('Py_IsInitialized', [], types.slong)()
assert res == 1
- def test_simple_types(self):
- from _ffi import types
- assert str(types.sint) == "<ffi type sint>"
- assert str(types.uint) == "<ffi type uint>"
-
def test_callfunc(self):
from _ffi import CDLL, types
libm = CDLL(self.libm_name)
@@ -263,29 +262,6 @@
assert list(array) == list('foobar\00')
do_nothing.free_temp_buffers()
- def test_typed_pointer(self):
- from _ffi import types
- intptr = types.Pointer(types.sint) # create a typed pointer to sint
- assert intptr.deref_pointer() is types.sint
- assert str(intptr) == '<ffi type (pointer to sint)>'
- assert types.sint.deref_pointer() is None
- raises(TypeError, "types.Pointer(42)")
-
- def test_pointer_identity(self):
- from _ffi import types
- x = types.Pointer(types.slong)
- y = types.Pointer(types.slong)
- z = types.Pointer(types.char)
- assert x is y
- assert x is not z
-
- def test_char_p_cached(self):
- from _ffi import types
- x = types.Pointer(types.char)
- assert x is types.char_p
- x = types.Pointer(types.unichar)
- assert x is types.unichar_p
-
def test_typed_pointer_args(self):
"""
extern int dummy; // defined in test_void_result
@@ -476,6 +452,51 @@
return p.x + p.y;
}
"""
+ from _ffi import CDLL, types, _StructDescr, Field
+ Point = _StructDescr('Point', [
+ Field('x', types.slong),
+ Field('y', types.slong),
+ ])
+ libfoo = CDLL(self.libfoo_name)
+ sum_point = libfoo.getfunc('sum_point', [Point.ffitype], types.slong)
+ #
+ p = Point.allocate()
+ p.setfield('x', 30)
+ p.setfield('y', 12)
+ res = sum_point(p)
+ assert res == 42
+
+ def test_byval_result(self):
+ """
+ DLLEXPORT struct Point make_point(long x, long y) {
+ struct Point p;
+ p.x = x;
+ p.y = y;
+ return p;
+ }
+ """
+ from _ffi import CDLL, types, _StructDescr, Field
+ Point = _StructDescr('Point', [
+ Field('x', types.slong),
+ Field('y', types.slong),
+ ])
+ libfoo = CDLL(self.libfoo_name)
+ make_point = libfoo.getfunc('make_point', [types.slong, types.slong],
+ Point.ffitype)
+ #
+ p = make_point(12, 34)
+ assert p.getfield('x') == 12
+ assert p.getfield('y') == 34
+
+ # XXX: support for _rawffi structures should be killed as soon as we
+ # implement ctypes.Structure on top of _ffi. In the meantime, we support
+ # both
+ def test_byval_argument__rawffi(self):
+ """
+ // defined above
+ struct Point;
+ DLLEXPORT long sum_point(struct Point p);
+ """
import _rawffi
from _ffi import CDLL, types
POINT = _rawffi.Structure([('x', 'l'), ('y', 'l')])
@@ -490,14 +511,10 @@
assert res == 42
p.free()
- def test_byval_result(self):
+ def test_byval_result__rawffi(self):
"""
- DLLEXPORT struct Point make_point(long x, long y) {
- struct Point p;
- p.x = x;
- p.y = y;
- return p;
- }
+ // defined above
+ DLLEXPORT struct Point make_point(long x, long y);
"""
import _rawffi
from _ffi import CDLL, types
@@ -511,6 +528,7 @@
assert p.y == 34
p.free()
+
def test_TypeError_numargs(self):
from _ffi import CDLL, types
libfoo = CDLL(self.libfoo_name)
diff --git a/pypy/module/_ffi/test/test_struct.py b/pypy/module/_ffi/test/test_struct.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_ffi/test/test_struct.py
@@ -0,0 +1,320 @@
+import sys
+from pypy.conftest import gettestobjspace
+from pypy.module._ffi.test.test_funcptr import BaseAppTestFFI
+from pypy.module._ffi.interp_struct import compute_size_and_alignement, W_Field
+from pypy.module._ffi.interp_ffitype import app_types, W_FFIType
+
+
+class TestStruct(object):
+
+ class FakeSpace(object):
+ def interp_w(self, cls, obj):
+ return obj
+
+ def compute(self, ffitypes_w):
+ fields_w = [W_Field('<dummy>', w_ffitype) for
+ w_ffitype in ffitypes_w]
+ return compute_size_and_alignement(self.FakeSpace(), fields_w)
+
+ def sizeof(self, ffitypes_w):
+ size, aligned, fields_w = self.compute(ffitypes_w)
+ return size
+
+ def test_compute_size(self):
+ T = app_types
+ byte_size = app_types.sbyte.sizeof()
+ long_size = app_types.slong.sizeof()
+ llong_size = app_types.slonglong.sizeof()
+ llong_align = app_types.slonglong.get_alignment()
+ #
+ assert llong_align >= 4
+ assert self.sizeof([T.sbyte, T.slong]) == 2*long_size
+ assert self.sizeof([T.sbyte, T.slonglong]) == llong_align + llong_size
+ assert self.sizeof([T.sbyte, T.sbyte, T.slonglong]) == llong_align + llong_size
+ assert self.sizeof([T.sbyte, T.sbyte, T.sbyte, T.slonglong]) == llong_align + llong_size
+ assert self.sizeof([T.sbyte, T.sbyte, T.sbyte, T.sbyte, T.slonglong]) == llong_align + llong_size
+ assert self.sizeof([T.slonglong, T.sbyte]) == llong_size + llong_align
+ assert self.sizeof([T.slonglong, T.sbyte, T.sbyte]) == llong_size + llong_align
+ assert self.sizeof([T.slonglong, T.sbyte, T.sbyte, T.sbyte]) == llong_size + llong_align
+ assert self.sizeof([T.slonglong, T.sbyte, T.sbyte, T.sbyte, T.sbyte]) == llong_size + llong_align
+
+class AppTestStruct(BaseAppTestFFI):
+
+ def setup_class(cls):
+ BaseAppTestFFI.setup_class.im_func(cls)
+ #
+ def read_raw_mem(self, addr, typename, length):
+ import ctypes
+ addr = ctypes.cast(addr, ctypes.c_void_p)
+ c_type = getattr(ctypes, typename)
+ array_type = ctypes.POINTER(c_type * length)
+ ptr_array = ctypes.cast(addr, array_type)
+ array = ptr_array[0]
+ lst = [array[i] for i in range(length)]
+ return lst
+ cls.w_read_raw_mem = cls.space.wrap(read_raw_mem)
+ #
+ from pypy.rlib import clibffi
+ from pypy.rlib.rarithmetic import r_uint
+ from pypy.rpython.lltypesystem import lltype, rffi
+ dummy_type = lltype.malloc(clibffi.FFI_TYPE_P.TO, flavor='raw')
+ dummy_type.c_size = r_uint(123)
+ dummy_type.c_alignment = rffi.cast(rffi.USHORT, 0)
+ dummy_type.c_type = rffi.cast(rffi.USHORT, 0)
+ cls.w_dummy_type = W_FFIType('dummy', dummy_type)
+
+ def test__StructDescr(self):
+ from _ffi import _StructDescr, Field, types
+ longsize = types.slong.sizeof()
+ fields = [
+ Field('x', types.slong),
+ Field('y', types.slong),
+ ]
+ descr = _StructDescr('foo', fields)
+ assert descr.ffitype.sizeof() == longsize*2
+ assert descr.ffitype.name == 'struct foo'
+
+ def test_alignment(self):
+ from _ffi import _StructDescr, Field, types
+ longsize = types.slong.sizeof()
+ fields = [
+ Field('x', types.sbyte),
+ Field('y', types.slong),
+ ]
+ descr = _StructDescr('foo', fields)
+ assert descr.ffitype.sizeof() == longsize*2
+ assert fields[0].offset == 0
+ assert fields[1].offset == longsize # aligned to WORD
+
+ def test_missing_field(self):
+ from _ffi import _StructDescr, Field, types
+ longsize = types.slong.sizeof()
+ fields = [
+ Field('x', types.slong),
+ Field('y', types.slong),
+ ]
+ descr = _StructDescr('foo', fields)
+ struct = descr.allocate()
+ raises(AttributeError, "struct.getfield('missing')")
+ raises(AttributeError, "struct.setfield('missing', 42)")
+
+ def test_unknown_type(self):
+ from _ffi import _StructDescr, Field
+ fields = [
+ Field('x', self.dummy_type),
+ ]
+ descr = _StructDescr('foo', fields)
+ struct = descr.allocate()
+ raises(TypeError, "struct.getfield('x')")
+ raises(TypeError, "struct.setfield('x', 42)")
+
+ def test_getfield_setfield(self):
+ from _ffi import _StructDescr, Field, types
+ longsize = types.slong.sizeof()
+ fields = [
+ Field('x', types.slong),
+ Field('y', types.slong),
+ ]
+ descr = _StructDescr('foo', fields)
+ struct = descr.allocate()
+ struct.setfield('x', 42)
+ struct.setfield('y', 43)
+ assert struct.getfield('x') == 42
+ assert struct.getfield('y') == 43
+ mem = self.read_raw_mem(struct.getaddr(), 'c_long', 2)
+ assert mem == [42, 43]
+
+ def test_getfield_setfield_signed_types(self):
+ import sys
+ from _ffi import _StructDescr, Field, types
+ longsize = types.slong.sizeof()
+ fields = [
+ Field('sbyte', types.sbyte),
+ Field('sshort', types.sshort),
+ Field('sint', types.sint),
+ Field('slong', types.slong),
+ ]
+ descr = _StructDescr('foo', fields)
+ struct = descr.allocate()
+ struct.setfield('sbyte', 128)
+ assert struct.getfield('sbyte') == -128
+ struct.setfield('sshort', 32768)
+ assert struct.getfield('sshort') == -32768
+ struct.setfield('sint', 43)
+ assert struct.getfield('sint') == 43
+ struct.setfield('slong', sys.maxint+1)
+ assert struct.getfield('slong') == -sys.maxint-1
+ struct.setfield('slong', sys.maxint*3)
+ assert struct.getfield('slong') == sys.maxint-2
+
+ def test_getfield_setfield_unsigned_types(self):
+ import sys
+ from _ffi import _StructDescr, Field, types
+ longsize = types.slong.sizeof()
+ fields = [
+ Field('ubyte', types.ubyte),
+ Field('ushort', types.ushort),
+ Field('uint', types.uint),
+ Field('ulong', types.ulong),
+ Field('char', types.char),
+ Field('unichar', types.unichar),
+ Field('ptr', types.void_p),
+ ]
+ descr = _StructDescr('foo', fields)
+ struct = descr.allocate()
+ struct.setfield('ubyte', -1)
+ assert struct.getfield('ubyte') == 255
+ struct.setfield('ushort', -1)
+ assert struct.getfield('ushort') == 65535
+ struct.setfield('uint', 43)
+ assert struct.getfield('uint') == 43
+ struct.setfield('ulong', -1)
+ assert struct.getfield('ulong') == sys.maxint*2 + 1
+ struct.setfield('ulong', sys.maxint*2 + 2)
+ assert struct.getfield('ulong') == 0
+ struct.setfield('char', 'a')
+ assert struct.getfield('char') == 'a'
+ struct.setfield('unichar', u'\u1234')
+ assert struct.getfield('unichar') == u'\u1234'
+ struct.setfield('ptr', -1)
+ assert struct.getfield('ptr') == sys.maxint*2 + 1
+
+ def test_getfield_setfield_longlong(self):
+ import sys
+ from _ffi import _StructDescr, Field, types
+ longsize = types.slong.sizeof()
+ fields = [
+ Field('slonglong', types.slonglong),
+ Field('ulonglong', types.ulonglong),
+ ]
+ descr = _StructDescr('foo', fields)
+ struct = descr.allocate()
+ struct.setfield('slonglong', 9223372036854775808)
+ assert struct.getfield('slonglong') == -9223372036854775808
+ struct.setfield('ulonglong', -1)
+ assert struct.getfield('ulonglong') == 18446744073709551615
+ mem = self.read_raw_mem(struct.getaddr(), 'c_longlong', 2)
+ assert mem == [-9223372036854775808, -1]
+
+ def test_getfield_setfield_float(self):
+ import sys
+ from _ffi import _StructDescr, Field, types
+ longsize = types.slong.sizeof()
+ fields = [
+ Field('x', types.double),
+ ]
+ descr = _StructDescr('foo', fields)
+ struct = descr.allocate()
+ struct.setfield('x', 123.4)
+ assert struct.getfield('x') == 123.4
+ mem = self.read_raw_mem(struct.getaddr(), 'c_double', 1)
+ assert mem == [123.4]
+
+ def test_getfield_setfield_singlefloat(self):
+ import sys
+ from _ffi import _StructDescr, Field, types
+ longsize = types.slong.sizeof()
+ fields = [
+ Field('x', types.float),
+ ]
+ descr = _StructDescr('foo', fields)
+ struct = descr.allocate()
+ struct.setfield('x', 123.4) # this is a value which DOES loose
+ # precision in a single float
+ assert 0 < abs(struct.getfield('x') - 123.4) < 0.0001
+ #
+ struct.setfield('x', 123.5) # this is a value which does not loose
+ # precision in a single float
+ assert struct.getfield('x') == 123.5
+ mem = self.read_raw_mem(struct.getaddr(), 'c_float', 1)
+ assert mem == [123.5]
+
+ def test_define_fields(self):
+ from _ffi import _StructDescr, Field, types
+ longsize = types.slong.sizeof()
+ fields = [
+ Field('x', types.slong),
+ Field('y', types.slong),
+ ]
+ descr = _StructDescr('foo')
+ assert descr.ffitype.name == 'struct foo'
+ assert repr(descr.ffitype) == '<ffi type struct foo (incomplete)>'
+ raises(ValueError, "descr.ffitype.sizeof()")
+ raises(ValueError, "descr.allocate()")
+ #
+ descr.define_fields(fields)
+ assert repr(descr.ffitype) == '<ffi type struct foo>'
+ assert descr.ffitype.sizeof() == longsize*2
+ raises(ValueError, "descr.define_fields(fields)")
+
+ def test_pointer_to_incomplete_struct(self):
+ from _ffi import _StructDescr, Field, types
+ longsize = types.slong.sizeof()
+ fields = [
+ Field('x', types.slong),
+ Field('y', types.slong),
+ ]
+ descr = _StructDescr('foo')
+ foo_ffitype = descr.ffitype
+ foo_p = types.Pointer(descr.ffitype)
+ assert foo_p.deref_pointer() is foo_ffitype
+ descr.define_fields(fields)
+ assert descr.ffitype is foo_ffitype
+ assert foo_p.deref_pointer() is foo_ffitype
+ assert types.Pointer(descr.ffitype) is foo_p
+
+ def test_nested_structure(self):
+ from _ffi import _StructDescr, Field, types
+ longsize = types.slong.sizeof()
+ foo_fields = [
+ Field('x', types.slong),
+ Field('y', types.slong),
+ ]
+ foo_descr = _StructDescr('foo', foo_fields)
+ #
+ bar_fields = [
+ Field('x', types.slong),
+ Field('foo', foo_descr.ffitype),
+ ]
+ bar_descr = _StructDescr('bar', bar_fields)
+ assert bar_descr.ffitype.sizeof() == longsize*3
+ #
+ struct = bar_descr.allocate()
+ struct.setfield('x', 40)
+ # reading a nested structure yields a reference to it
+ struct_foo = struct.getfield('foo')
+ struct_foo.setfield('x', 41)
+ struct_foo.setfield('y', 42)
+ mem = self.read_raw_mem(struct.getaddr(), 'c_long', 3)
+ assert mem == [40, 41, 42]
+ #
+ struct_foo2 = foo_descr.allocate()
+ struct_foo2.setfield('x', 141)
+ struct_foo2.setfield('y', 142)
+ # writing a nested structure copies its memory into the target
+ struct.setfield('foo', struct_foo2)
+ struct_foo2.setfield('x', 241)
+ struct_foo2.setfield('y', 242)
+ mem = self.read_raw_mem(struct.getaddr(), 'c_long', 3)
+ assert mem == [40, 141, 142]
+ mem = self.read_raw_mem(struct_foo2.getaddr(), 'c_long', 2)
+ assert mem == [241, 242]
+
+
+
+ def test_compute_shape(self):
+ from _ffi import Structure, Field, types
+ class Point(Structure):
+ _fields_ = [
+ Field('x', types.slong),
+ Field('y', types.slong),
+ ]
+
+ longsize = types.slong.sizeof()
+ assert isinstance(Point.x, Field)
+ assert isinstance(Point.y, Field)
+ assert Point.x.offset == 0
+ assert Point.y.offset == longsize
+ assert Point._struct_.ffitype.sizeof() == longsize*2
+ assert Point._struct_.ffitype.name == 'struct Point'
+
diff --git a/pypy/module/_ffi/test/test_type_converter.py b/pypy/module/_ffi/test/test_type_converter.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_ffi/test/test_type_converter.py
@@ -0,0 +1,128 @@
+import sys
+from pypy.conftest import gettestobjspace
+from pypy.rlib.rarithmetic import r_uint, r_singlefloat, r_longlong, r_ulonglong
+from pypy.rlib.libffi import IS_32_BIT
+from pypy.module._ffi.interp_ffitype import app_types, descr_new_pointer
+from pypy.module._ffi.type_converter import FromAppLevelConverter, ToAppLevelConverter
+
+class DummyFromAppLevelConverter(FromAppLevelConverter):
+
+ def handle_all(self, w_ffitype, w_obj, val):
+ self.lastval = val
+
+ handle_signed = handle_all
+ handle_unsigned = handle_all
+ handle_pointer = handle_all
+ handle_char = handle_all
+ handle_unichar = handle_all
+ handle_longlong = handle_all
+ handle_char_p = handle_all
+ handle_unichar_p = handle_all
+ handle_float = handle_all
+ handle_singlefloat = handle_all
+
+ def handle_struct(self, w_ffitype, w_structinstance):
+ self.lastval = w_structinstance
+
+ def convert(self, w_ffitype, w_obj):
+ self.unwrap_and_do(w_ffitype, w_obj)
+ return self.lastval
+
+
+class TestFromAppLevel(object):
+
+ def setup_class(cls):
+ cls.space = gettestobjspace(usemodules=('_ffi',))
+ converter = DummyFromAppLevelConverter(cls.space)
+ cls.from_app_level = staticmethod(converter.convert)
+
+ def check(self, w_ffitype, w_obj, expected):
+ v = self.from_app_level(w_ffitype, w_obj)
+ assert v == expected
+ assert type(v) is type(expected)
+
+ def test_int(self):
+ self.check(app_types.sint, self.space.wrap(42), 42)
+ self.check(app_types.sint, self.space.wrap(sys.maxint+1), -sys.maxint-1)
+ self.check(app_types.sint, self.space.wrap(sys.maxint*2), -2)
+
+ def test_unsigned(self):
+ space = self.space
+ self.check(app_types.uint, space.wrap(42), r_uint(42))
+ self.check(app_types.uint, space.wrap(-1), r_uint(sys.maxint*2 +1))
+ self.check(app_types.uint, space.wrap(sys.maxint*3),
+ r_uint(sys.maxint - 2))
+ self.check(app_types.ulong, space.wrap(sys.maxint+12),
+ r_uint(sys.maxint+12))
+ self.check(app_types.ulong, space.wrap(sys.maxint*2+3), r_uint(1))
+
+ def test_char(self):
+ space = self.space
+ self.check(app_types.char, space.wrap('a'), ord('a'))
+ self.check(app_types.unichar, space.wrap(u'\u1234'), 0x1234)
+
+ def test_signed_longlong(self):
+ space = self.space
+ maxint32 = 2147483647 # we cannot really go above maxint on 64 bits
+ # (and we would not test anything, as there long
+ # is the same as long long)
+ expected = maxint32+1
+ if IS_32_BIT:
+ expected = r_longlong(expected)
+ self.check(app_types.slonglong, space.wrap(maxint32+1), expected)
+
+ def test_unsigned_longlong(self):
+ space = self.space
+ maxint64 = 9223372036854775807 # maxint64+1 does not fit into a
+ # longlong, but it does into a
+ # ulonglong
+ if IS_32_BIT:
+ # internally, the type converter always casts to signed longlongs
+ expected = r_longlong(-maxint64-1)
+ else:
+ # on 64 bit, ulonglong == uint (i.e., unsigned long in C terms)
+ expected = r_uint(maxint64+1)
+ self.check(app_types.ulonglong, space.wrap(maxint64+1), expected)
+
+ def test_float_and_double(self):
+ space = self.space
+ self.check(app_types.float, space.wrap(12.34), r_singlefloat(12.34))
+ self.check(app_types.double, space.wrap(12.34), 12.34)
+
+ def test_pointer(self):
+ # pointers are "unsigned" at applevel, but signed at interp-level (for
+ # no good reason, at interp-level Signed or Unsigned makes no
+ # difference for passing bits around)
+ space = self.space
+ self.check(app_types.void_p, space.wrap(42), 42)
+ self.check(app_types.void_p, space.wrap(sys.maxint+1), -sys.maxint-1)
+ #
+ # typed pointers
+ w_ptr_sint = descr_new_pointer(space, None, app_types.sint)
+ self.check(w_ptr_sint, space.wrap(sys.maxint+1), -sys.maxint-1)
+
+
+ def test__as_ffi_pointer_(self):
+ space = self.space
+ w_MyPointerWrapper = space.appexec([], """():
+ import _ffi
+ class MyPointerWrapper(object):
+ def __init__(self, value):
+ self.value = value
+ def _as_ffi_pointer_(self, ffitype):
+ assert ffitype is _ffi.types.void_p
+ return self.value
+
+ return MyPointerWrapper
+ """)
+ w_obj = space.call_function(w_MyPointerWrapper, space.wrap(42))
+ self.check(app_types.void_p, w_obj, 42)
+
+ def test_strings(self):
+ # first, try automatic conversion from applevel
+ self.check(app_types.char_p, self.space.wrap('foo'), 'foo')
+ self.check(app_types.unichar_p, self.space.wrap(u'foo\u1234'), u'foo\u1234')
+ self.check(app_types.unichar_p, self.space.wrap('foo'), u'foo')
+ # then, try to pass explicit pointers
+ self.check(app_types.char_p, self.space.wrap(42), 42)
+ self.check(app_types.unichar_p, self.space.wrap(42), 42)
diff --git a/pypy/module/_ffi/type_converter.py b/pypy/module/_ffi/type_converter.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_ffi/type_converter.py
@@ -0,0 +1,364 @@
+from pypy.rlib import libffi
+from pypy.rlib import jit
+from pypy.rlib.rarithmetic import intmask, r_uint
+from pypy.rpython.lltypesystem import rffi
+from pypy.interpreter.error import operationerrfmt, OperationError
+from pypy.module._rawffi.structure import W_StructureInstance, W_Structure
+from pypy.module._ffi.interp_ffitype import app_types
+
+class FromAppLevelConverter(object):
+ """
+ Unwrap an app-level object to the corresponding low-level type, following
+ the conversion rules which apply to the specified w_ffitype. Once
+ unwrapped, the value is passed to the corresponding handle_* method.
+ Subclasses should override the desired ones.
+ """
+
+ def __init__(self, space):
+ self.space = space
+
+ def unwrap_and_do(self, w_ffitype, w_obj):
+ from pypy.module._ffi.interp_struct import W__StructInstance
+ space = self.space
+ if w_ffitype.is_longlong():
+ # note that we must check for longlong first, because either
+ # is_signed or is_unsigned returns true anyway
+ assert libffi.IS_32_BIT
+ self._longlong(w_ffitype, w_obj)
+ elif w_ffitype.is_signed():
+ intval = space.truncatedint_w(w_obj)
+ self.handle_signed(w_ffitype, w_obj, intval)
+ elif self.maybe_handle_char_or_unichar_p(w_ffitype, w_obj):
+ # the object was already handled from within
+ # maybe_handle_char_or_unichar_p
+ pass
+ elif w_ffitype.is_pointer():
+ w_obj = self.convert_pointer_arg_maybe(w_obj, w_ffitype)
+ intval = space.truncatedint_w(w_obj)
+ self.handle_pointer(w_ffitype, w_obj, intval)
+ elif w_ffitype.is_unsigned():
+ uintval = r_uint(space.truncatedint_w(w_obj))
+ self.handle_unsigned(w_ffitype, w_obj, uintval)
+ elif w_ffitype.is_char():
+ intval = space.int_w(space.ord(w_obj))
+ self.handle_char(w_ffitype, w_obj, intval)
+ elif w_ffitype.is_unichar():
+ intval = space.int_w(space.ord(w_obj))
+ self.handle_unichar(w_ffitype, w_obj, intval)
+ elif w_ffitype.is_double():
+ self._float(w_ffitype, w_obj)
+ elif w_ffitype.is_singlefloat():
+ self._singlefloat(w_ffitype, w_obj)
+ elif w_ffitype.is_struct():
+ if isinstance(w_obj, W_StructureInstance):
+ self.handle_struct_rawffi(w_ffitype, w_obj)
+ else:
+ w_obj = space.interp_w(W__StructInstance, w_obj)
+ self.handle_struct(w_ffitype, w_obj)
+ else:
+ self.error(w_ffitype, w_obj)
+
+ def _longlong(self, w_ffitype, w_obj):
+ # a separate function, which can be seen by the jit or not,
+ # depending on whether longlongs are supported
+ longlongval = self.space.truncatedlonglong_w(w_obj)
+ self.handle_longlong(w_ffitype, w_obj, longlongval)
+
+ def _float(self, w_ffitype, w_obj):
+ # a separate function, which can be seen by the jit or not,
+ # depending on whether floats are supported
+ floatval = self.space.float_w(w_obj)
+ self.handle_float(w_ffitype, w_obj, floatval)
+
+ def _singlefloat(self, w_ffitype, w_obj):
+ # a separate function, which can be seen by the jit or not,
+ # depending on whether singlefloats are supported
+ from pypy.rlib.rarithmetic import r_singlefloat
+ floatval = self.space.float_w(w_obj)
+ singlefloatval = r_singlefloat(floatval)
+ self.handle_singlefloat(w_ffitype, w_obj, singlefloatval)
+
+ def maybe_handle_char_or_unichar_p(self, w_ffitype, w_obj):
+ w_type = jit.promote(self.space.type(w_obj))
+ if w_ffitype.is_char_p() and w_type is self.space.w_str:
+ strval = self.space.str_w(w_obj)
+ self.handle_char_p(w_ffitype, w_obj, strval)
+ return True
+ elif w_ffitype.is_unichar_p() and (w_type is self.space.w_str or
+ w_type is self.space.w_unicode):
+ unicodeval = self.space.unicode_w(w_obj)
+ self.handle_unichar_p(w_ffitype, w_obj, unicodeval)
+ return True
+ return False
+
+ def convert_pointer_arg_maybe(self, w_arg, w_argtype):
+ """
+ Try to convert the argument by calling _as_ffi_pointer_()
+ """
+ space = self.space
+ meth = space.lookup(w_arg, '_as_ffi_pointer_') # this also promotes the type
+ if meth:
+ return space.call_function(meth, w_arg, w_argtype)
+ else:
+ return w_arg
+
+ def error(self, w_ffitype, w_obj):
+ raise operationerrfmt(self.space.w_TypeError,
+ 'Unsupported ffi type to convert: %s',
+ w_ffitype.name)
+
+ def handle_signed(self, w_ffitype, w_obj, intval):
+ """
+ intval: lltype.Signed
+ """
+ self.error(w_ffitype, w_obj)
+
+ def handle_unsigned(self, w_ffitype, w_obj, uintval):
+ """
+ uintval: lltype.Unsigned
+ """
+ self.error(w_ffitype, w_obj)
+
+ def handle_pointer(self, w_ffitype, w_obj, intval):
+ """
+ intval: lltype.Signed
+ """
+ self.error(w_ffitype, w_obj)
+
+ def handle_char(self, w_ffitype, w_obj, intval):
+ """
+ intval: lltype.Signed
+ """
+ self.error(w_ffitype, w_obj)
+
+ def handle_unichar(self, w_ffitype, w_obj, intval):
+ """
+ intval: lltype.Signed
+ """
+ self.error(w_ffitype, w_obj)
+
+ def handle_longlong(self, w_ffitype, w_obj, longlongval):
+ """
+ longlongval: lltype.SignedLongLong
+ """
+ self.error(w_ffitype, w_obj)
+
+ def handle_char_p(self, w_ffitype, w_obj, strval):
+ """
+ strval: interp-level str
+ """
+ self.error(w_ffitype, w_obj)
+
+ def handle_unichar_p(self, w_ffitype, w_obj, unicodeval):
+ """
+ unicodeval: interp-level unicode
+ """
+ self.error(w_ffitype, w_obj)
+
+ def handle_float(self, w_ffitype, w_obj, floatval):
+ """
+ floatval: lltype.Float
+ """
+ self.error(w_ffitype, w_obj)
+
+ def handle_singlefloat(self, w_ffitype, w_obj, singlefloatval):
+ """
+ singlefloatval: lltype.SingleFloat
+ """
+ self.error(w_ffitype, w_obj)
+
+ def handle_struct(self, w_ffitype, w_structinstance):
+ """
+ w_structinstance: W_StructureInstance
+ """
+ self.error(w_ffitype, w_structinstance)
+
+ def handle_struct_rawffi(self, w_ffitype, w_structinstance):
+ """
+ This method should be killed as soon as we remove support for _rawffi structures
+
+ w_structinstance: W_StructureInstance
+ """
+ self.error(w_ffitype, w_structinstance)
+
+
+
+class ToAppLevelConverter(object):
+ """
+ Wrap a low-level value to an app-level object, following the conversion
+ rules which apply to the specified w_ffitype. The value is got by calling
+ the get_* method corresponding to the w_ffitype. Subclasses should
+ override the desired ones.
+ """
+
+ def __init__(self, space):
+ self.space = space
+
+ def do_and_wrap(self, w_ffitype):
+ from pypy.module._ffi.interp_struct import W__StructDescr
+ space = self.space
+ if w_ffitype.is_longlong():
+ # note that we must check for longlong first, because either
+ # is_signed or is_unsigned returns true anyway
+ assert libffi.IS_32_BIT
+ return self._longlong(w_ffitype)
+ elif w_ffitype.is_signed():
+ intval = self.get_signed(w_ffitype)
+ return space.wrap(intval)
+ elif w_ffitype is app_types.ulong or w_ffitype is app_types.ulonglong:
+ # Note that we the second check (for ulonglong) is meaningful only
+ # on 64 bit, because on 32 bit the ulonglong case would have been
+ # handled by the is_longlong() branch above. On 64 bit, ulonglong
+ # is essentially the same as ulong.
+ #
+ # We need to be careful when the return type is ULONG, because the
+ # value might not fit into a signed LONG, and thus might require
+ # and app-evel <long>. This is why we need to treat it separately
+ # than the other unsigned types.
+ uintval = self.get_unsigned(w_ffitype)
+ return space.wrap(uintval)
+ elif w_ffitype.is_unsigned(): # note that ulong is handled just before
+ intval = self.get_unsigned_which_fits_into_a_signed(w_ffitype)
+ return space.wrap(intval)
+ elif w_ffitype.is_pointer():
+ uintval = self.get_pointer(w_ffitype)
+ return space.wrap(uintval)
+ elif w_ffitype.is_char():
+ ucharval = self.get_char(w_ffitype)
+ return space.wrap(chr(ucharval))
+ elif w_ffitype.is_unichar():
+ wcharval = self.get_unichar(w_ffitype)
+ return space.wrap(unichr(wcharval))
+ elif w_ffitype.is_double():
+ return self._float(w_ffitype)
+ elif w_ffitype.is_singlefloat():
+ return self._singlefloat(w_ffitype)
+ elif w_ffitype.is_struct():
+ w_structdescr = w_ffitype.w_structdescr
+ if isinstance(w_structdescr, W__StructDescr):
+ return self.get_struct(w_ffitype, w_structdescr)
+ elif isinstance(w_structdescr, W_Structure):
+ return self.get_struct_rawffi(w_ffitype, w_structdescr)
+ else:
+ raise OperationError(self.space.w_TypeError,
+ self.space.wrap("Unsupported struct shape"))
+ elif w_ffitype.is_void():
+ voidval = self.get_void(w_ffitype)
+ assert voidval is None
+ return space.w_None
+ else:
+ self.error(w_ffitype)
+
+ def _longlong(self, w_ffitype):
+ # a separate function, which can be seen by the jit or not,
+ # depending on whether longlongs are supported
+ if w_ffitype is app_types.slonglong:
+ longlongval = self.get_longlong(w_ffitype)
+ return self.space.wrap(longlongval)
+ elif w_ffitype is app_types.ulonglong:
+ ulonglongval = self.get_ulonglong(w_ffitype)
+ return self.space.wrap(ulonglongval)
+ else:
+ self.error(w_ffitype)
+
+ def _float(self, w_ffitype):
+ # a separate function, which can be seen by the jit or not,
+ # depending on whether floats are supported
+ floatval = self.get_float(w_ffitype)
+ return self.space.wrap(floatval)
+
+ def _singlefloat(self, w_ffitype):
+ # a separate function, which can be seen by the jit or not,
+ # depending on whether singlefloats are supported
+ singlefloatval = self.get_singlefloat(w_ffitype)
+ return self.space.wrap(float(singlefloatval))
+
+ def error(self, w_ffitype):
+ raise operationerrfmt(self.space.w_TypeError,
+ 'Unsupported ffi type to convert: %s',
+ w_ffitype.name)
+
+ def get_longlong(self, w_ffitype):
+ """
+ Return type: lltype.SignedLongLong
+ """
+ self.error(w_ffitype)
+
+ def get_ulonglong(self, w_ffitype):
+ """
+ Return type: lltype.UnsignedLongLong
+ """
+ self.error(w_ffitype)
+
+ def get_signed(self, w_ffitype):
+ """
+ Return type: lltype.Signed
+ """
+ self.error(w_ffitype)
+
+ def get_unsigned(self, w_ffitype):
+ """
+ Return type: lltype.Unsigned
+ """
+ self.error(w_ffitype)
+
+ def get_unsigned_which_fits_into_a_signed(self, w_ffitype):
+ """
+ Return type: lltype.Signed.
+
+ We return Signed even if the input type is unsigned, because this way
+ we get an app-level <int> instead of a <long>.
+ """
+ self.error(w_ffitype)
+
+ def get_pointer(self, w_ffitype):
+ """
+ Return type: lltype.Unsigned
+ """
+ self.error(w_ffitype)
+
+ def get_char(self, w_ffitype):
+ """
+ Return type: rffi.UCHAR
+ """
+ self.error(w_ffitype)
+
+ def get_unichar(self, w_ffitype):
+ """
+ Return type: rffi.WCHAR_T
+ """
+ self.error(w_ffitype)
+
+ def get_float(self, w_ffitype):
+ """
+ Return type: lltype.Float
+ """
+ self.error(w_ffitype)
+
+ def get_singlefloat(self, w_ffitype):
+ """
+ Return type: lltype.SingleFloat
+ """
+ self.error(w_ffitype)
+
+ def get_struct(self, w_ffitype, w_structdescr):
+ """
+ Return type: lltype.Signed
+ (the address of the structure)
+ """
+ self.error(w_ffitype)
+
+ def get_struct_rawffi(self, w_ffitype, w_structdescr):
+ """
+ This should be killed as soon as we kill support for _rawffi structures
+
+ Return type: lltype.Unsigned
+ (the address of the structure)
+ """
+ self.error(w_ffitype)
+
+ def get_void(self, w_ffitype):
+ """
+ Return type: None
+ """
+ self.error(w_ffitype)
diff --git a/pypy/module/_rawffi/interp_rawffi.py b/pypy/module/_rawffi/interp_rawffi.py
--- a/pypy/module/_rawffi/interp_rawffi.py
+++ b/pypy/module/_rawffi/interp_rawffi.py
@@ -253,7 +253,7 @@
# XXX: this assumes that you have the _ffi module enabled. In the long
# term, probably we will move the code for build structures and arrays
# from _rawffi to _ffi
- from pypy.module._ffi.interp_ffi import W_FFIType
+ from pypy.module._ffi.interp_ffitype import W_FFIType
return W_FFIType('<unknown>', self.get_basic_ffi_type(), self)
@unwrap_spec(n=int)
diff --git a/pypy/module/binascii/interp_crc32.py b/pypy/module/binascii/interp_crc32.py
--- a/pypy/module/binascii/interp_crc32.py
+++ b/pypy/module/binascii/interp_crc32.py
@@ -61,7 +61,7 @@
crc_32_tab = map(r_uint, crc_32_tab)
- at unwrap_spec(data='bufferstr', oldcrc='truncatedint')
+ at unwrap_spec(data='bufferstr', oldcrc='truncatedint_w')
def crc32(space, data, oldcrc=0):
"Compute the CRC-32 incrementally."
diff --git a/pypy/module/pypyjit/test_pypy_c/test__ffi.py b/pypy/module/pypyjit/test_pypy_c/test__ffi.py
--- a/pypy/module/pypyjit/test_pypy_c/test__ffi.py
+++ b/pypy/module/pypyjit/test_pypy_c/test__ffi.py
@@ -134,3 +134,31 @@
call = ops[idx]
assert (call.args[0] == 'ConstClass(fabs)' or # e.g. OS/X
int(call.args[0]) == fabs_addr)
+
+
+ def test__ffi_struct(self):
+ def main():
+ from _ffi import _StructDescr, Field, types
+ fields = [
+ Field('x', types.slong),
+ ]
+ descr = _StructDescr('foo', fields)
+ struct = descr.allocate()
+ i = 0
+ while i < 300:
+ x = struct.getfield('x') # ID: getfield
+ x = x+1
+ struct.setfield('x', x) # ID: setfield
+ i += 1
+ return struct.getfield('x')
+ #
+ log = self.run(main, [])
+ loop, = log.loops_by_filename(self.filepath)
+ assert loop.match_by_id('getfield', """
+ guard_not_invalidated(descr=...)
+ i57 = getfield_raw(i46, descr=<FieldS dynamic 0>)
+ """)
+ assert loop.match_by_id('setfield', """
+ setfield_raw(i44, i57, descr=<FieldS dynamic 0>)
+ """)
+
diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py
--- a/pypy/module/zlib/interp_zlib.py
+++ b/pypy/module/zlib/interp_zlib.py
@@ -20,7 +20,7 @@
return intmask((x ^ SIGN_EXTEND2) - SIGN_EXTEND2)
- at unwrap_spec(string='bufferstr', start='truncatedint')
+ at unwrap_spec(string='bufferstr', start='truncatedint_w')
def crc32(space, string, start = rzlib.CRC32_DEFAULT_START):
"""
crc32(string[, start]) -- Compute a CRC-32 checksum of string.
@@ -41,7 +41,7 @@
return space.wrap(checksum)
- at unwrap_spec(string='bufferstr', start='truncatedint')
+ at unwrap_spec(string='bufferstr', start='truncatedint_w')
def adler32(space, string, start=rzlib.ADLER32_DEFAULT_START):
"""
adler32(string[, start]) -- Compute an Adler-32 checksum of string.
diff --git a/pypy/rlib/clibffi.py b/pypy/rlib/clibffi.py
--- a/pypy/rlib/clibffi.py
+++ b/pypy/rlib/clibffi.py
@@ -11,6 +11,7 @@
from pypy.rlib.rdynload import dlopen, dlclose, dlsym, dlsym_byordinal
from pypy.rlib.rdynload import DLOpenError, DLLHANDLE
from pypy.rlib import jit
+from pypy.rlib.objectmodel import specialize
from pypy.tool.autopath import pypydir
from pypy.translator.tool.cbuild import ExternalCompilationInfo
from pypy.translator.platform import platform
@@ -141,6 +142,7 @@
FFI_TYPE_P = lltype.Ptr(lltype.ForwardReference())
FFI_TYPE_PP = rffi.CArrayPtr(FFI_TYPE_P)
+FFI_TYPE_NULL = lltype.nullptr(FFI_TYPE_P.TO)
class CConfig:
_compilation_info_ = eci
@@ -346,11 +348,13 @@
('ffistruct', FFI_TYPE_P.TO),
('members', lltype.Array(FFI_TYPE_P))))
-def make_struct_ffitype_e(size, aligment, field_types):
+ at specialize.arg(3)
+def make_struct_ffitype_e(size, aligment, field_types, track_allocation=True):
"""Compute the type of a structure. Returns a FFI_STRUCT_P out of
which the 'ffistruct' member is a regular FFI_TYPE.
"""
- tpe = lltype.malloc(FFI_STRUCT_P.TO, len(field_types)+1, flavor='raw')
+ tpe = lltype.malloc(FFI_STRUCT_P.TO, len(field_types)+1, flavor='raw',
+ track_allocation=track_allocation)
tpe.ffistruct.c_type = rffi.cast(rffi.USHORT, FFI_TYPE_STRUCT)
tpe.ffistruct.c_size = rffi.cast(rffi.SIZE_T, size)
tpe.ffistruct.c_alignment = rffi.cast(rffi.USHORT, aligment)
diff --git a/pypy/rlib/libffi.py b/pypy/rlib/libffi.py
--- a/pypy/rlib/libffi.py
+++ b/pypy/rlib/libffi.py
@@ -210,7 +210,7 @@
_immutable_fields_ = ['funcsym']
argtypes = []
- restype = lltype.nullptr(clibffi.FFI_TYPE_P.TO)
+ restype = clibffi.FFI_TYPE_NULL
flags = 0
funcsym = lltype.nullptr(rffi.VOIDP.TO)
@@ -416,6 +416,96 @@
def getaddressindll(self, name):
return dlsym(self.lib, name)
+# ======================================================================
+
+ at jit.oopspec('libffi_struct_getfield(ffitype, addr, offset)')
+def struct_getfield_int(ffitype, addr, offset):
+ """
+ Return the field of type ``ffitype`` at ``addr+offset``, widened to
+ lltype.Signed.
+ """
+ for TYPE, ffitype2 in clibffi.ffitype_map_int_or_ptr:
+ if ffitype is ffitype2:
+ value = _struct_getfield(TYPE, addr, offset)
+ return rffi.cast(lltype.Signed, value)
+ assert False, "cannot find the given ffitype"
+
+
+ at jit.oopspec('libffi_struct_setfield(ffitype, addr, offset, value)')
+def struct_setfield_int(ffitype, addr, offset, value):
+ """
+ Set the field of type ``ffitype`` at ``addr+offset``. ``value`` is of
+ type lltype.Signed, and it's automatically converted to the right type.
+ """
+ for TYPE, ffitype2 in clibffi.ffitype_map_int_or_ptr:
+ if ffitype is ffitype2:
+ value = rffi.cast(TYPE, value)
+ _struct_setfield(TYPE, addr, offset, value)
+ return
+ assert False, "cannot find the given ffitype"
+
+
+ at jit.oopspec('libffi_struct_getfield(ffitype, addr, offset)')
+def struct_getfield_longlong(ffitype, addr, offset):
+ """
+ Return the field of type ``ffitype`` at ``addr+offset``, casted to
+ lltype.LongLong.
+ """
+ value = _struct_getfield(lltype.SignedLongLong, addr, offset)
+ return value
+
+ at jit.oopspec('libffi_struct_setfield(ffitype, addr, offset, value)')
+def struct_setfield_longlong(ffitype, addr, offset, value):
+ """
+ Set the field of type ``ffitype`` at ``addr+offset``. ``value`` is of
+ type lltype.LongLong
+ """
+ _struct_setfield(lltype.SignedLongLong, addr, offset, value)
+
+
+ at jit.oopspec('libffi_struct_getfield(ffitype, addr, offset)')
+def struct_getfield_float(ffitype, addr, offset):
+ value = _struct_getfield(lltype.Float, addr, offset)
+ return value
+
+ at jit.oopspec('libffi_struct_setfield(ffitype, addr, offset, value)')
+def struct_setfield_float(ffitype, addr, offset, value):
+ _struct_setfield(lltype.Float, addr, offset, value)
+
+
+ at jit.oopspec('libffi_struct_getfield(ffitype, addr, offset)')
+def struct_getfield_singlefloat(ffitype, addr, offset):
+ value = _struct_getfield(lltype.SingleFloat, addr, offset)
+ return value
+
+ at jit.oopspec('libffi_struct_setfield(ffitype, addr, offset, value)')
+def struct_setfield_singlefloat(ffitype, addr, offset, value):
+ _struct_setfield(lltype.SingleFloat, addr, offset, value)
+
+
+ at specialize.arg(0)
+def _struct_getfield(TYPE, addr, offset):
+ """
+ Read the field of type TYPE at addr+offset.
+ addr is of type rffi.VOIDP, offset is an int.
+ """
+ addr = rffi.ptradd(addr, offset)
+ PTR_FIELD = lltype.Ptr(rffi.CArray(TYPE))
+ return rffi.cast(PTR_FIELD, addr)[0]
+
+
+ at specialize.arg(0)
+def _struct_setfield(TYPE, addr, offset, value):
+ """
+ Write the field of type TYPE at addr+offset.
+ addr is of type rffi.VOIDP, offset is an int.
+ """
+ addr = rffi.ptradd(addr, offset)
+ PTR_FIELD = lltype.Ptr(rffi.CArray(TYPE))
+ rffi.cast(PTR_FIELD, addr)[0] = value
+
+# ======================================================================
+
# These specialize.call_location's should really be specialize.arg(0), however
# you can't hash a pointer obj, which the specialize machinery wants to do.
# Given the present usage of these functions, it's good enough.
diff --git a/pypy/rlib/rarithmetic.py b/pypy/rlib/rarithmetic.py
--- a/pypy/rlib/rarithmetic.py
+++ b/pypy/rlib/rarithmetic.py
@@ -97,6 +97,9 @@
# XXX TODO: replace all int(n) by long(n) and fix everything that breaks.
# XXX Then relax it and replace int(n) by n.
def intmask(n):
+ """
+ NOT_RPYTHON
+ """
if isinstance(n, objectmodel.Symbolic):
return n # assume Symbolics don't overflow
assert not isinstance(n, float)
@@ -109,6 +112,9 @@
return int(n)
def longlongmask(n):
+ """
+ NOT_RPYTHON
+ """
assert isinstance(n, (int, long))
n = long(n)
n &= LONGLONG_MASK
diff --git a/pypy/rlib/test/test_libffi.py b/pypy/rlib/test/test_libffi.py
--- a/pypy/rlib/test/test_libffi.py
+++ b/pypy/rlib/test/test_libffi.py
@@ -2,12 +2,16 @@
import py
-from pypy.rlib.libffi import (CDLL, Func, get_libc_name, ArgChain, types,
- IS_32_BIT, array_getitem, array_setitem)
from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
from pypy.rlib.test.test_clibffi import BaseFfiTest, get_libm_name, make_struct_ffitype_e
from pypy.rpython.lltypesystem import rffi, lltype
from pypy.rpython.lltypesystem.ll2ctypes import ALLOCATED
+from pypy.rlib.libffi import (CDLL, Func, get_libc_name, ArgChain, types,
+ IS_32_BIT, array_getitem, array_setitem)
+from pypy.rlib.libffi import (struct_getfield_int, struct_setfield_int,
+ struct_getfield_longlong, struct_setfield_longlong,
+ struct_getfield_float, struct_setfield_float,
+ struct_getfield_singlefloat, struct_setfield_singlefloat)
class TestLibffiMisc(BaseFfiTest):
@@ -54,6 +58,33 @@
del lib
assert not ALLOCATED
+ def test_struct_fields(self):
+ longsize = 4 if IS_32_BIT else 8
+ POINT = lltype.Struct('POINT',
+ ('x', rffi.LONG),
+ ('y', rffi.SHORT),
+ ('z', rffi.VOIDP),
+ )
+ y_ofs = longsize
+ z_ofs = longsize*2
+ p = lltype.malloc(POINT, flavor='raw')
+ p.x = 42
+ p.y = rffi.cast(rffi.SHORT, -1)
+ p.z = rffi.cast(rffi.VOIDP, 0x1234)
+ addr = rffi.cast(rffi.VOIDP, p)
+ assert struct_getfield_int(types.slong, addr, 0) == 42
+ assert struct_getfield_int(types.sshort, addr, y_ofs) == -1
+ assert struct_getfield_int(types.pointer, addr, z_ofs) == 0x1234
+ #
+ struct_setfield_int(types.slong, addr, 0, 43)
+ struct_setfield_int(types.sshort, addr, y_ofs, 0x1234FFFE) # 0x1234 is masked out
+ struct_setfield_int(types.pointer, addr, z_ofs, 0x4321)
+ assert p.x == 43
+ assert p.y == -2
+ assert rffi.cast(rffi.LONG, p.z) == 0x4321
+ #
+ lltype.free(p, flavor='raw')
+
def test_array_fields(self):
POINT = lltype.Struct("POINT",
("x", lltype.Float),
@@ -69,18 +100,81 @@
assert array_getitem(types.double, 16, points, 0, 8) == 2.0
assert array_getitem(types.double, 16, points, 1, 0) == 3.0
assert array_getitem(types.double, 16, points, 1, 8) == 4.0
-
+ #
array_setitem(types.double, 16, points, 0, 0, 10.0)
array_setitem(types.double, 16, points, 0, 8, 20.0)
array_setitem(types.double, 16, points, 1, 0, 30.0)
array_setitem(types.double, 16, points, 1, 8, 40.0)
-
+ #
assert array_getitem(types.double, 16, points, 0, 0) == 10.0
assert array_getitem(types.double, 16, points, 0, 8) == 20.0
assert array_getitem(types.double, 16, points, 1, 0) == 30.0
assert array_getitem(types.double, 16, points, 1, 8) == 40.0
+ #
+ lltype.free(points, flavor="raw")
- lltype.free(points, flavor="raw")
+
+ def test_struct_fields_longlong(self):
+ POINT = lltype.Struct('POINT',
+ ('x', rffi.LONGLONG),
+ ('y', rffi.ULONGLONG)
+ )
+ y_ofs = 8
+ p = lltype.malloc(POINT, flavor='raw')
+ p.x = r_longlong(123)
+ p.y = r_ulonglong(456)
+ addr = rffi.cast(rffi.VOIDP, p)
+ assert struct_getfield_longlong(types.slonglong, addr, 0) == 123
+ assert struct_getfield_longlong(types.ulonglong, addr, y_ofs) == 456
+ #
+ v = rffi.cast(lltype.SignedLongLong, r_ulonglong(9223372036854775808))
+ struct_setfield_longlong(types.slonglong, addr, 0, v)
+ struct_setfield_longlong(types.ulonglong, addr, y_ofs, r_longlong(-1))
+ assert p.x == -9223372036854775808
+ assert rffi.cast(lltype.UnsignedLongLong, p.y) == 18446744073709551615
+ #
+ lltype.free(p, flavor='raw')
+
+ def test_struct_fields_float(self):
+ POINT = lltype.Struct('POINT',
+ ('x', rffi.DOUBLE),
+ ('y', rffi.DOUBLE)
+ )
+ y_ofs = 8
+ p = lltype.malloc(POINT, flavor='raw')
+ p.x = 123.4
+ p.y = 567.8
+ addr = rffi.cast(rffi.VOIDP, p)
+ assert struct_getfield_float(types.double, addr, 0) == 123.4
+ assert struct_getfield_float(types.double, addr, y_ofs) == 567.8
+ #
+ struct_setfield_float(types.double, addr, 0, 321.0)
+ struct_setfield_float(types.double, addr, y_ofs, 876.5)
+ assert p.x == 321.0
+ assert p.y == 876.5
+ #
+ lltype.free(p, flavor='raw')
+
+ def test_struct_fields_singlefloat(self):
+ POINT = lltype.Struct('POINT',
+ ('x', rffi.FLOAT),
+ ('y', rffi.FLOAT)
+ )
+ y_ofs = 4
+ p = lltype.malloc(POINT, flavor='raw')
+ p.x = r_singlefloat(123.4)
+ p.y = r_singlefloat(567.8)
+ addr = rffi.cast(rffi.VOIDP, p)
+ assert struct_getfield_singlefloat(types.double, addr, 0) == r_singlefloat(123.4)
+ assert struct_getfield_singlefloat(types.double, addr, y_ofs) == r_singlefloat(567.8)
+ #
+ struct_setfield_singlefloat(types.double, addr, 0, r_singlefloat(321.0))
+ struct_setfield_singlefloat(types.double, addr, y_ofs, r_singlefloat(876.5))
+ assert p.x == r_singlefloat(321.0)
+ assert p.y == r_singlefloat(876.5)
+ #
+ lltype.free(p, flavor='raw')
+
class TestLibffiCall(BaseFfiTest):
"""
diff --git a/pypy/rpython/rbuiltin.py b/pypy/rpython/rbuiltin.py
--- a/pypy/rpython/rbuiltin.py
+++ b/pypy/rpython/rbuiltin.py
@@ -247,6 +247,11 @@
vlist = hop.inputargs(lltype.Signed)
return vlist[0]
+def rtype_longlongmask(hop):
+ hop.exception_cannot_occur()
+ vlist = hop.inputargs(lltype.SignedLongLong)
+ return vlist[0]
+
def rtype_builtin_min(hop):
v1, v2 = hop.inputargs(hop.r_result, hop.r_result)
hop.exception_cannot_occur()
@@ -564,6 +569,7 @@
BUILTIN_TYPER[lltype.Ptr] = rtype_const_result
BUILTIN_TYPER[lltype.runtime_type_info] = rtype_runtime_type_info
BUILTIN_TYPER[rarithmetic.intmask] = rtype_intmask
+BUILTIN_TYPER[rarithmetic.longlongmask] = rtype_longlongmask
BUILTIN_TYPER[objectmodel.hlinvoke] = rtype_hlinvoke
diff --git a/pypy/rpython/test/test_rbuiltin.py b/pypy/rpython/test/test_rbuiltin.py
--- a/pypy/rpython/test/test_rbuiltin.py
+++ b/pypy/rpython/test/test_rbuiltin.py
@@ -5,7 +5,7 @@
from pypy.rlib.debug import llinterpcall
from pypy.rpython.lltypesystem import lltype
from pypy.tool import udir
-from pypy.rlib.rarithmetic import intmask, is_valid_int
+from pypy.rlib.rarithmetic import intmask, longlongmask, r_int64, is_valid_int
from pypy.rlib.rarithmetic import r_int, r_uint, r_longlong, r_ulonglong
from pypy.annotation.builtin import *
from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
@@ -79,6 +79,16 @@
res = self.interpret(f, [r_uint(5)])
assert type(res) is int and res == 5
+ def test_longlongmask(self):
+ def f(x=r_ulonglong):
+ try:
+ return longlongmask(x)
+ except ValueError:
+ return 0
+
+ res = self.interpret(f, [r_ulonglong(5)])
+ assert type(res) is r_int64 and res == 5
+
def test_rbuiltin_list(self):
def f():
l=list((1,2,3))
diff --git a/pypy/translator/backendopt/test/test_finalizer.py b/pypy/translator/backendopt/test/test_finalizer.py
--- a/pypy/translator/backendopt/test/test_finalizer.py
+++ b/pypy/translator/backendopt/test/test_finalizer.py
@@ -84,8 +84,8 @@
def __del__(self):
if self.x:
+ lltype.free(self.x, flavor='raw')
self.x = lltype.nullptr(S)
- lltype.free(self.x, flavor='raw')
def f():
return A()
diff --git a/pypy/translator/c/test/test_typed.py b/pypy/translator/c/test/test_typed.py
--- a/pypy/translator/c/test/test_typed.py
+++ b/pypy/translator/c/test/test_typed.py
@@ -885,3 +885,13 @@
assert res == 'acquire, hello, raised, release'
res = f(2)
assert res == 'acquire, hello, raised, release'
+
+ def test_longlongmask(self):
+ from pypy.rlib.rarithmetic import longlongmask, r_ulonglong
+ def func(n):
+ m = r_ulonglong(n)
+ m *= 100000
+ return longlongmask(m)
+ f = self.getcompiled(func, [int])
+ res = f(-2000000000)
+ assert res == -200000000000000
diff --git a/pypy/translator/cli/test/test_builtin.py b/pypy/translator/cli/test/test_builtin.py
--- a/pypy/translator/cli/test/test_builtin.py
+++ b/pypy/translator/cli/test/test_builtin.py
@@ -16,7 +16,10 @@
test_os_isdir = skip_os
test_os_dup_oo = skip_os
test_os_access = skip_os
-
+
+ def test_longlongmask(self):
+ py.test.skip("fix me")
+
def test_builtin_math_frexp(self):
self._skip_powerpc("Mono math floating point problem")
BaseTestBuiltin.test_builtin_math_frexp(self)
diff --git a/pypy/translator/jvm/test/test_builtin.py b/pypy/translator/jvm/test/test_builtin.py
--- a/pypy/translator/jvm/test/test_builtin.py
+++ b/pypy/translator/jvm/test/test_builtin.py
@@ -47,6 +47,9 @@
res = self.interpret(fn, [])
assert stat.S_ISREG(res)
+ def test_longlongmask(self):
+ py.test.skip("fix me")
+
class TestJvmTime(JvmTest, BaseTestTime):
pass
More information about the pypy-commit
mailing list