Commits

Amaury Forgeot d'Arc committed 7a17880 Merge

merge heads

Comments (0)

Files changed (47)

pypy/annotation/builtin.py

 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
         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

pypy/annotation/unaryop.py

File contents unchanged.
 .. _`pypy/rlib/rbigint.py`: https://bitbucket.org/pypy/pypy/src/default/pypy/rlib/rbigint.py
 .. _`pypy/rlib/rrandom.py`: https://bitbucket.org/pypy/pypy/src/default/pypy/rlib/rrandom.py
 .. _`pypy/rlib/rsocket.py`: https://bitbucket.org/pypy/pypy/src/default/pypy/rlib/rsocket.py
-.. _`pypy/rlib/rstack.py`: https://bitbucket.org/pypy/pypy/src/default/pypy/rlib/rstack.py
 .. _`pypy/rlib/streamio.py`: https://bitbucket.org/pypy/pypy/src/default/pypy/rlib/streamio.py
 .. _`pypy/rlib/test`: https://bitbucket.org/pypy/pypy/src/default/pypy/rlib/test/
 .. _`pypy/rlib/unroll.py`: https://bitbucket.org/pypy/pypy/src/default/pypy/rlib/unroll.py
 .. _`pypy/rpython/memory/gc/hybrid.py`: https://bitbucket.org/pypy/pypy/src/default/pypy/rpython/memory/gc/hybrid.py
 .. _`pypy/rpython/memory/gc/markcompact.py`: https://bitbucket.org/pypy/pypy/src/default/pypy/rpython/memory/gc/markcompact.py
 .. _`pypy/rpython/memory/gc/marksweep.py`: https://bitbucket.org/pypy/pypy/src/default/pypy/rpython/memory/gc/marksweep.py
-.. _`pypy/rpython/memory/gc/minimark.py`: https://bitbucket.org/pypy/pypy/src/default/pypy/rpython/memory/gc/minimark.py
 .. _`pypy/rpython/memory/gc/minimarkpage.py`: https://bitbucket.org/pypy/pypy/src/default/pypy/rpython/memory/gc/minimarkpage.py
 .. _`pypy/rpython/memory/gc/semispace.py`: https://bitbucket.org/pypy/pypy/src/default/pypy/rpython/memory/gc/semispace.py
 .. _`pypy/rpython/ootypesystem/`: https://bitbucket.org/pypy/pypy/src/default/pypy/rpython/ootypesystem/
 .. _`pypy/translator/backendopt/`: https://bitbucket.org/pypy/pypy/src/default/pypy/translator/backendopt/
 .. _`pypy/translator/c/`: https://bitbucket.org/pypy/pypy/src/default/pypy/translator/c/
 .. _`pypy/translator/c/src/stacklet/`: https://bitbucket.org/pypy/pypy/src/default/pypy/translator/c/src/stacklet/
+.. _`pypy/translator/c/src/stacklet/stacklet.h`: https://bitbucket.org/pypy/pypy/src/default/pypy/translator/c/src/stacklet/stacklet.h
 .. _`pypy/translator/cli/`: https://bitbucket.org/pypy/pypy/src/default/pypy/translator/cli/
 .. _`pypy/translator/goal/`: https://bitbucket.org/pypy/pypy/src/default/pypy/translator/goal/
 .. _`pypy/translator/jvm/`: https://bitbucket.org/pypy/pypy/src/default/pypy/translator/jvm/
-.. _`pypy/translator/stackless/`: https://bitbucket.org/pypy/pypy/src/default/pypy/translator/stackless/
 .. _`pypy/translator/tool/`: https://bitbucket.org/pypy/pypy/src/default/pypy/translator/tool/

pypy/doc/whatsnew-1.9.rst

 
 .. branch: array_equal
 .. branch: better-jit-hooks-2
-.. branch: exception-cannot-occur
 .. branch: faster-heapcache
 .. branch: faster-str-decode-escape
 .. branch: float-bytes
+Added some primitives for dealing with floats as raw bytes.
 .. branch: float-bytes-2
+Added more float byte primitives.
 .. branch: jit-frame-counter
+Put more debug info into resops.
 .. branch: kill-geninterp
 .. branch: kqueue
+Finished select.kqueue.
 .. branch: kwargsdict-strategy
 .. branch: matrixmath-dot
 numpypy can now handle matrix multiplication.
 The directory "lib-python/modified-2.7" has been removed, and its
 content merged into "lib-python/2.7".
 .. branch: step-one-xrange
+The common case of a xrange iterator with no step argument specifed
+was somewhat optimized. The tightest loop involving it,
+sum(xrange(n)), is now 18% faster on average.
 .. branch: string-NUL
 PyPy refuses filenames with chr(0) characters. This is implemented in
 RPython which can enforce no-NUL correctness and propagation, similar
 .. branch: win64-stage1
 .. branch: zlib-mem-pressure
 
+.. branch: ffistruct
+The ``ffistruct`` branch adds a very low level way to express C structures
+with _ffi in a very JIT-friendly way
+
+
 
 .. "uninteresting" branches that we should just ignore for the whatsnew:
+.. branch: exception-cannot-occur
 .. branch: sanitize-finally-stack
-.. branch: revive-dlltool (preliminary work for sepcomp)
+.. branch: revive-dlltool
+     (preliminary work for sepcomp)

pypy/interpreter/baseobjspace.py

                                  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:
             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

pypy/interpreter/gateway.py

     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):
     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:
     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()

pypy/interpreter/test/test_objspace.py

         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
         

pypy/jit/backend/llgraph/llimpl.py

     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)
             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)
 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)
     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)

pypy/jit/backend/llgraph/runner.py

         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)

pypy/jit/backend/llsupport/descr.py

         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)
 

pypy/jit/backend/llsupport/llmodel.py

 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
 
 
     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

pypy/jit/backend/test/runner_test.py

         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):

pypy/jit/codewriter/effectinfo.py

     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

pypy/jit/codewriter/jtransform.py

         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

pypy/jit/codewriter/support.py

 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),

pypy/jit/metainterp/optimizeopt/fficall.py

 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):
             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)
         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))
             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))

pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py

                              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
         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__
 
         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)

pypy/jit/metainterp/test/test_fficall.py

 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
 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

pypy/module/_ffi/__init__.py

 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',
+        }

pypy/module/_ffi/app_struct.py

+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

pypy/module/_ffi/interp_ffi.py

-from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.error import OperationError, wrap_oserror, \
-    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.rpython.lltypesystem import lltype, rffi
-#
-from pypy.rlib import jit
-from pypy.rlib import libffi
-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__)
-
-
-def unwrap_ffitype(space, w_argtype, allow_void=False):
-    res = w_argtype.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)'
-
-# ========================================================================
-
-class W_FuncPtr(Wrappable):
-
-    _immutable_fields_ = ['func', 'argtypes_w[*]', 'w_restype']
-
-    def __init__(self, func, argtypes_w, w_restype):
-        self.func = func
-        self.argtypes_w = argtypes_w
-        self.w_restype = w_restype
-        self.to_free = []
-
-    @jit.unroll_safe
-    def build_argchain(self, space, args_w):
-        expected = len(self.argtypes_w)
-        given = len(args_w)
-        if given != expected:
-            arg = 'arguments'
-            if len(self.argtypes_w) == 1:
-                arg = 'argument'
-            raise operationerrfmt(space.w_TypeError,
-                                  '%s() takes exactly %d %s (%d given)',
-                                  self.func.name, expected, arg, given)
-        #
-        argchain = libffi.ArgChain()
-        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
-        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)
-
-    def free_temp_buffers(self, space):
-        for buf in self.to_free:
-            if not we_are_translated():
-                buf[0] = '\00' # invalidate the buffer, so that
-                               # test_keepalive_temp_buffer can fail
-            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 _call_int(self, space, argchain):
-        # 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
-        # correctly, we need to cast the result to the correct type.  After
-        # 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
-        call = self.func.call
-        if restype is libffi.types.slong:
-            intres = call(argchain, rffi.LONG)
-        elif restype is libffi.types.sint:
-            intres = rffi.cast(rffi.LONG, call(argchain, rffi.INT))
-        elif restype is libffi.types.sshort:
-            intres = rffi.cast(rffi.LONG, call(argchain, rffi.SHORT))
-        elif restype is libffi.types.schar:
-            intres = rffi.cast(rffi.LONG, call(argchain, rffi.SIGNEDCHAR))
-        else:
-            raise OperationError(space.w_ValueError,
-                                 space.wrap('Unsupported restype'))
-        return space.wrap(intres)
-
-    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
-        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))
-        elif restype is libffi.types.ushort:
-            intres = rffi.cast(rffi.LONG, call(argchain, rffi.USHORT))
-        elif restype is libffi.types.uchar:
-            intres = rffi.cast(rffi.LONG, call(argchain, rffi.UCHAR))
-        else:
-            raise OperationError(space.w_ValueError,
-                                 space.wrap('Unsupported restype'))
-        return space.wrap(intres)
-
-    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 _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 getaddr(self, space):
-        """
-        Return the physical address in memory of the function
-        """
-        return space.wrap(rffi.cast(rffi.LONG, self.func.funcsym))
-
-
-
-def unpack_argtypes(space, w_argtypes, w_restype):
-    argtypes_w = [space.interp_w(W_FFIType, w_argtype)
-                  for w_argtype in space.listview(w_argtypes)]
-    argtypes = [unwrap_ffitype(space, w_argtype) for w_argtype in
-                argtypes_w]
-    w_restype = space.interp_w(W_FFIType, w_restype)
-    restype = unwrap_ffitype(space, w_restype, allow_void=True)
-    return argtypes_w, argtypes, w_restype, restype
-
-@unwrap_spec(addr=r_uint, name=str)
-def descr_fromaddr(space, w_cls, addr, name, w_argtypes, w_restype):
-    argtypes_w, argtypes, w_restype, restype = unpack_argtypes(space,
-                                                               w_argtypes,
-                                                               w_restype)
-    addr = rffi.cast(rffi.VOIDP, addr)
-    func = libffi.Func(name, argtypes, restype, addr)
-    return W_FuncPtr(func, argtypes_w, w_restype)
-
-
-W_FuncPtr.typedef = TypeDef(
-    '_ffi.FuncPtr',
-    __call__ = interp2app(W_FuncPtr.call),
-    getaddr = interp2app(W_FuncPtr.getaddr),
-    free_temp_buffers = interp2app(W_FuncPtr.free_temp_buffers),
-    fromaddr = interp2app(descr_fromaddr, as_classmethod=True)
-    )
-
-
-
-# ========================================================================
-
-class W_CDLL(Wrappable):
-    def __init__(self, space, name, mode):
-        self.space = space
-        if name is None:
-            self.name = "<None>"
-        else:
-            self.name = name
-        try:
-            self.cdll = libffi.CDLL(name, mode)
-        except DLOpenError, e:
-            raise operationerrfmt(space.w_OSError, '%s: %s', self.name,
-                                  e.msg or 'unspecified error')
-
-    @unwrap_spec(name=str)
-    def getfunc(self, space, name, w_argtypes, w_restype):
-        argtypes_w, argtypes, w_restype, restype = unpack_argtypes(space,
-                                                                   w_argtypes,
-                                                                   w_restype)
-        try:
-            func = self.cdll.getpointer(name, argtypes, restype)
-        except KeyError:
-            raise operationerrfmt(space.w_AttributeError,
-                                  "No symbol %s found in library %s", name, self.name)
-
-        return W_FuncPtr(func, argtypes_w, w_restype)
-
-    @unwrap_spec(name=str)
-    def getaddressindll(self, space, name):
-        try:
-            address_as_uint = rffi.cast(lltype.Unsigned,
-                                        self.cdll.getaddressindll(name))
-        except KeyError:
-            raise operationerrfmt(space.w_ValueError,
-                                  "No symbol %s found in library %s", name, self.name)
-        return space.wrap(address_as_uint)
-
-@unwrap_spec(name='str_or_None', mode=int)
-def descr_new_cdll(space, w_type, name, mode=-1):
-    return space.wrap(W_CDLL(space, name, mode))
-
-
-W_CDLL.typedef = TypeDef(
-    '_ffi.CDLL',
-    __new__     = interp2app(descr_new_cdll),
-    getfunc     = interp2app(W_CDLL.getfunc),
-    getaddressindll = interp2app(W_CDLL.getaddressindll),
-    )
-
-# ========================================================================
-
-def get_libc(space):
-    from pypy.rlib.clibffi import get_libc_name
-    try:
-        return space.wrap(W_CDLL(space, get_libc_name(), -1))
-    except OSError, e:
-        raise wrap_oserror(space, e)

pypy/module/_ffi/interp_ffitype.py

+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__)

pypy/module/_ffi/interp_funcptr.py

+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.error import OperationError, wrap_oserror, \
+    operationerrfmt
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.typedef import TypeDef
+from pypy.module._ffi.interp_ffitype import W_FFIType
+#
+from pypy.rpython.lltypesystem import lltype, rffi
+#
+from pypy.rlib import jit
+from pypy.rlib import libffi
+from pypy.rlib.rdynload import DLOpenError
+from pypy.rlib.rarithmetic import intmask, r_uint
+from pypy.rlib.objectmodel import we_are_translated
+from pypy.module._ffi.type_converter import FromAppLevelConverter, ToAppLevelConverter
+
+
+def unwrap_ffitype(space, w_argtype, allow_void=False):
+    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
+
+
+# ========================================================================
+
+class W_FuncPtr(Wrappable):
+
+    _immutable_fields_ = ['func', 'argtypes_w[*]', 'w_restype']
+
+    def __init__(self, func, argtypes_w, w_restype):
+        self.func = func
+        self.argtypes_w = argtypes_w
+        self.w_restype = w_restype
+        self.to_free = []
+
+    @jit.unroll_safe
+    def build_argchain(self, space, args_w):
+        expected = len(self.argtypes_w)
+        given = len(args_w)
+        if given != expected:
+            arg = 'arguments'
+            if len(self.argtypes_w) == 1:
+                arg = 'argument'
+            raise operationerrfmt(space.w_TypeError,
+                                  '%s() takes exactly %d %s (%d given)',
+                                  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]
+            argpusher.unwrap_and_do(w_argtype, w_arg)
+        return argchain
+
+    def call(self, space, args_w):
+        self = jit.promote(self)
+        argchain = self.build_argchain(space, args_w)
+        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:
+            if not we_are_translated():
+                buf[0] = '\00' # invalidate the buffer, so that
+                               # test_keepalive_temp_buffer can fail
+            lltype.free(buf, flavor='raw')
+        self.to_free = []
+
+    def getaddr(self, space):
+        """
+        Return the physical address in memory of the function
+        """
+        return space.wrap(rffi.cast(rffi.LONG, self.func.funcsym))
+
+
+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
+        # correctly, we need to cast the result to the correct type.  After
+        # 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 = w_ffitype.get_ffitype()
+        call = self.func.call
+        if restype is libffi.types.slong:
+            return call(self.argchain, rffi.LONG)
+        elif restype is libffi.types.sint:
+            return rffi.cast(rffi.LONG, call(self.argchain, rffi.INT))
+        elif restype is libffi.types.sshort:
+            return rffi.cast(rffi.LONG, call(self.argchain, rffi.SHORT))
+        elif restype is libffi.types.schar:
+            return rffi.cast(rffi.LONG, call(self.argchain, rffi.SIGNEDCHAR))
+        else:
+            self.error(w_ffitype)
+            
+    def get_unsigned(self, w_ffitype):
+        return self.func.call(self.argchain, rffi.ULONG)
+
+    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.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:
+            return rffi.cast(rffi.LONG, call(self.argchain, rffi.USHORT))
+        elif restype is libffi.types.uchar:
+            return rffi.cast(rffi.LONG, call(self.argchain, rffi.UCHAR))
+        else:
+            self.error(w_ffitype)
+
+
+    def get_pointer(self, w_ffitype):
+        ptrres = self.func.call(self.argchain, rffi.VOIDP)
+        return rffi.cast(rffi.ULONG, ptrres)
+
+    def get_char(self, w_ffitype):
+        return self.func.call(self.argchain, rffi.UCHAR)
+
+    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)
+                  for w_argtype in space.listview(w_argtypes)]
+    argtypes = [unwrap_ffitype(space, w_argtype) for w_argtype in
+                argtypes_w]
+    w_restype = space.interp_w(W_FFIType, w_restype)
+    restype = unwrap_ffitype(space, w_restype, allow_void=True)
+    return argtypes_w, argtypes, w_restype, restype
+
+@unwrap_spec(addr=r_uint, name=str)
+def descr_fromaddr(space, w_cls, addr, name, w_argtypes, w_restype):
+    argtypes_w, argtypes, w_restype, restype = unpack_argtypes(space,
+                                                               w_argtypes,
+                                                               w_restype)
+    addr = rffi.cast(rffi.VOIDP, addr)
+    func = libffi.Func(name, argtypes, restype, addr)
+    return W_FuncPtr(func, argtypes_w, w_restype)
+
+
+W_FuncPtr.typedef = TypeDef(
+    '_ffi.FuncPtr',
+    __call__ = interp2app(W_FuncPtr.call),
+    getaddr = interp2app(W_FuncPtr.getaddr),
+    free_temp_buffers = interp2app(W_FuncPtr.free_temp_buffers),
+    fromaddr = interp2app(descr_fromaddr, as_classmethod=True)
+    )
+
+
+
+# ========================================================================
+
+class W_CDLL(Wrappable):
+    def __init__(self, space, name, mode):
+        self.space = space
+        if name is None:
+            self.name = "<None>"
+        else:
+            self.name = name
+        try:
+            self.cdll = libffi.CDLL(name, mode)
+        except DLOpenError, e:
+            raise operationerrfmt(space.w_OSError, '%s: %s', self.name,
+                                  e.msg or 'unspecified error')
+
+    @unwrap_spec(name=str)
+    def getfunc(self, space, name, w_argtypes, w_restype):
+        argtypes_w, argtypes, w_restype, restype = unpack_argtypes(space,
+                                                                   w_argtypes,
+                                                                   w_restype)
+        try:
+            func = self.cdll.getpointer(name, argtypes, restype)
+        except KeyError:
+            raise operationerrfmt(space.w_AttributeError,
+                                  "No symbol %s found in library %s", name, self.name)
+
+        return W_FuncPtr(func, argtypes_w, w_restype)
+
+    @unwrap_spec(name=str)
+    def getaddressindll(self, space, name):
+        try:
+            address_as_uint = rffi.cast(lltype.Unsigned,
+                                        self.cdll.getaddressindll(name))
+        except KeyError:
+            raise operationerrfmt(space.w_ValueError,
+                                  "No symbol %s found in library %s", name, self.name)
+        return space.wrap(address_as_uint)
+
+@unwrap_spec(name='str_or_None', mode=int)
+def descr_new_cdll(space, w_type, name, mode=-1):
+    return space.wrap(W_CDLL(space, name, mode))
+
+
+W_CDLL.typedef = TypeDef(
+    '_ffi.CDLL',
+    __new__     = interp2app(descr_new_cdll),
+    getfunc     = interp2app(W_CDLL.getfunc),
+    getaddressindll = interp2app(W_CDLL.getaddressindll),
+    )
+
+# ========================================================================
+
+def get_libc(space):
+    from pypy.rlib.clibffi import get_libc_name
+    try:
+        return space.wrap(W_CDLL(space, get_libc_name(), -1))
+    except OSError, e:
+        raise wrap_oserror(space, e)
+

pypy/module/_ffi/interp_struct.py

+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)
+
+@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
+
+
+
+@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
+