[pypy-svn] r51900 - in pypy/dist/pypy: lib/_ctypes lib/app_test/ctypes module/_rawffi module/_rawffi/test rlib/test

pedronis at codespeak.net pedronis at codespeak.net
Wed Feb 27 14:38:36 CET 2008


Author: pedronis
Date: Wed Feb 27 14:38:34 2008
New Revision: 51900

Modified:
   pypy/dist/pypy/lib/_ctypes/array.py
   pypy/dist/pypy/lib/_ctypes/basics.py
   pypy/dist/pypy/lib/_ctypes/function.py
   pypy/dist/pypy/lib/_ctypes/structure.py
   pypy/dist/pypy/lib/app_test/ctypes/test_as_parameter.py
   pypy/dist/pypy/lib/app_test/ctypes/test_functions.py
   pypy/dist/pypy/module/_rawffi/array.py
   pypy/dist/pypy/module/_rawffi/interp_rawffi.py
   pypy/dist/pypy/module/_rawffi/structure.py
   pypy/dist/pypy/module/_rawffi/test/test__rawffi.py
   pypy/dist/pypy/rlib/test/test_libffi.py
Log:
Support for functions returning structure values.

Now though the mess gettypecode/get_ffi_type/get_arg_type... needs to be refactored and cleaned up.
See the new xxxs.



Modified: pypy/dist/pypy/lib/_ctypes/array.py
==============================================================================
--- pypy/dist/pypy/lib/_ctypes/array.py	(original)
+++ pypy/dist/pypy/lib/_ctypes/array.py	Wed Feb 27 14:38:34 2008
@@ -85,6 +85,9 @@
         res._index = index
         return res.__ctypes_from_outparam__()
 
+    def _CData_retval(self, resbuffer):
+        raise NotImplementedError
+
     def _CData_value(self, value):
         # array accepts very strange parameters as part of structure
         # or function argument...

Modified: pypy/dist/pypy/lib/_ctypes/basics.py
==============================================================================
--- pypy/dist/pypy/lib/_ctypes/basics.py	(original)
+++ pypy/dist/pypy/lib/_ctypes/basics.py	Wed Feb 27 14:38:34 2008
@@ -47,19 +47,22 @@
         # interested only in value anyway
         return cobj._get_buffer_value()
 
-    def _CData_output(self, resarray, base=None, index=-1, needs_free=False):
-        assert isinstance(resarray, _rawffi.ArrayInstance)
+    def _CData_output(self, resbuffer, base=None, index=-1, needs_free=False):
+        assert isinstance(resbuffer, _rawffi.ArrayInstance)
         """Used when data exits ctypes and goes into user code.
-        'resarray' is a _rawffi array of length 1 containing the value,
+        'resbuffer' is a _rawffi array of length 1 containing the value,
         and this returns a general Python object that corresponds.
         """
         res = self.__new__(self)
-        res.__dict__['_buffer'] = resarray
+        res.__dict__['_buffer'] = resbuffer
         res.__dict__['_base'] = base
         res.__dict__['_index'] = index
         res.__dict__['_needs_free'] = needs_free
         return res.__ctypes_from_outparam__()
 
+    def _CData_retval(self, resbuffer):
+        return self._CData_output(resbuffer, needs_free=True)
+
     def __mul__(self, other):
         from _ctypes.array import create_array_type
         return create_array_type(self, other)

Modified: pypy/dist/pypy/lib/_ctypes/function.py
==============================================================================
--- pypy/dist/pypy/lib/_ctypes/function.py	(original)
+++ pypy/dist/pypy/lib/_ctypes/function.py	Wed Feb 27 14:38:34 2008
@@ -80,18 +80,22 @@
         restype = self._restype_
         funcptr = self._getfuncptr(argtypes, restype)
         args = self._wrap_args(argtypes, args)
-        resarray = funcptr(*[arg._buffer for obj, arg in args])
+        resbuffer = funcptr(*[arg._buffer for obj, arg in args])
         if restype is not None:
-            return restype._CData_output(resarray, needs_free=True)
+            return restype._CData_retval(resbuffer)
         else:
-            resarray.free()
+            resbuffer.free()
 
     def _getfuncptr(self, argtypes, restype):
         if restype is None:
             import ctypes
             restype = ctypes.c_int
         argshapes = [arg._ffiargshape for arg in argtypes]
-        return self.dll._handle.ptr(self.name, argshapes, restype._ffiletter)
+        if isinstance(restype._ffiargshape, str): # xxx refactor
+            resshape = restype._ffiargshape
+        else:
+            resshape = restype._ffistruct
+        return self.dll._handle.ptr(self.name, argshapes, resshape)
 
     def _guess_argtypes(self, args):
         from _ctypes import _CData

Modified: pypy/dist/pypy/lib/_ctypes/structure.py
==============================================================================
--- pypy/dist/pypy/lib/_ctypes/structure.py	(original)
+++ pypy/dist/pypy/lib/_ctypes/structure.py	Wed Feb 27 14:38:34 2008
@@ -155,6 +155,14 @@
         res.__dict__['_base'] = base
         res.__dict__['_index'] = index
         return res.__ctypes_from_outparam__()
+    
+    def _CData_retval(self, resbuffer):
+        res = self.__new__(self)
+        res.__dict__['_buffer'] = resbuffer
+        res.__dict__['_base'] = None
+        res.__dict__['_index'] = -1
+        res.__dict__['_needs_free'] = True
+        return res.__ctypes_from_outparam__()
 
 class Structure(_CData):
     __metaclass__ = StructureMeta

Modified: pypy/dist/pypy/lib/app_test/ctypes/test_as_parameter.py
==============================================================================
--- pypy/dist/pypy/lib/app_test/ctypes/test_as_parameter.py	(original)
+++ pypy/dist/pypy/lib/app_test/ctypes/test_as_parameter.py	Wed Feb 27 14:38:34 2008
@@ -164,7 +164,6 @@
         assert got == expected
 
     def test_struct_return_2H(self):
-        py.test.skip("Structure by value")
         class S2H(Structure):
             _fields_ = [("x", c_short),
                         ("y", c_short)]
@@ -175,7 +174,6 @@
         assert (s2h.x, s2h.y) == (99*2, 88*3)
 
     def test_struct_return_8H(self):
-        py.test.skip("Structure by value")
         class S8I(Structure):
             _fields_ = [("a", c_int),
                         ("b", c_int),

Modified: pypy/dist/pypy/lib/app_test/ctypes/test_functions.py
==============================================================================
--- pypy/dist/pypy/lib/app_test/ctypes/test_functions.py	(original)
+++ pypy/dist/pypy/lib/app_test/ctypes/test_functions.py	Wed Feb 27 14:38:34 2008
@@ -330,7 +330,6 @@
         assert got == expected
 
     def test_struct_return_2H(self):
-        py.test.skip("Raw structure return")
         class S2H(Structure):
             _fields_ = [("x", c_short),
                         ("y", c_short)]
@@ -352,7 +351,6 @@
             assert (s2h.x, s2h.y) == (99*2, 88*3)
 
     def test_struct_return_8H(self):
-        py.test.skip("Raw structure return")
         class S8I(Structure):
             _fields_ = [("a", c_int),
                         ("b", c_int),

Modified: pypy/dist/pypy/module/_rawffi/array.py
==============================================================================
--- pypy/dist/pypy/module/_rawffi/array.py	(original)
+++ pypy/dist/pypy/module/_rawffi/array.py	Wed Feb 27 14:38:34 2008
@@ -10,7 +10,7 @@
 from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.interpreter.error import OperationError, wrap_oserror
 from pypy.module._rawffi.interp_rawffi import segfault_exception
-from pypy.module._rawffi.interp_rawffi import W_DataInstance
+from pypy.module._rawffi.interp_rawffi import W_DataShape, W_DataInstance
 from pypy.module._rawffi.interp_rawffi import unwrap_value, wrap_value
 from pypy.module._rawffi.interp_rawffi import unpack_typecode, letter2tp
 from pypy.rlib.rarithmetic import intmask, r_uint
@@ -26,7 +26,7 @@
     return ll_array[pos]
 get_elem._annspecialcase_ = 'specialize:arg(2)'
 
-class W_Array(Wrappable):
+class W_Array(W_DataShape):
     def __init__(self, space, itemtp):
         assert isinstance(itemtp, tuple)
         self.space = space

Modified: pypy/dist/pypy/module/_rawffi/interp_rawffi.py
==============================================================================
--- pypy/dist/pypy/module/_rawffi/interp_rawffi.py	(original)
+++ pypy/dist/pypy/module/_rawffi/interp_rawffi.py	Wed Feb 27 14:38:34 2008
@@ -108,6 +108,7 @@
         self.w_cache = space.newdict()
         self.space = space
 
+    # xxx refactor away !
     def get_arg_type(self, letter, argsize, argalignment):
         space = self.space
         if letter == 'V': # xxx leaks
@@ -123,15 +124,28 @@
         """ Get a pointer for function name with provided argtypes
         and restype
         """
+        # xxx refactor
         if space.is_w(w_restype, space.w_None):
-            restype = 'v'
+            resshape = None
             ffi_restype = ffi_type_void
+        elif space.is_true(space.isinstance(w_restype, space.w_str)):
+            tp_letter = space.str_w(w_restype)
+            if tp_letter == 'v':
+                resshape = None
+                ffi_restype = ffi_type_void
+            else:
+                from pypy.module._rawffi.array import get_array_cache
+                cache = get_array_cache(space)
+                resshape = cache.get_array_type(letter2tp(space, tp_letter))
+                ffi_restype = self.get_type(tp_letter)
         else:
-            restype = space.str_w(w_restype)
-            ffi_restype = self.get_type(restype)
+            from pypy.module._rawffi.structure import W_Structure
+            resshape = space.interp_w(W_Structure, w_restype)
+            ffi_restype = resshape.get_ffi_type()
+                
         w = space.wrap
         w_argtypes = space.newtuple(space.unpackiterable(w_argtypes))
-        w_key = space.newtuple([w(name), w_argtypes, w(restype)])
+        w_key = space.newtuple([w(name), w_argtypes, w(resshape)])
         try:
             return space.getitem(self.w_cache, w_key)
         except OperationError, e:
@@ -146,7 +160,7 @@
                                                     in argtypes]
         try:
             ptr = self.cdll.getrawpointer(name, ffi_argtypes, ffi_restype)
-            w_funcptr = W_FuncPtr(space, ptr, argtypes, restype)
+            w_funcptr = W_FuncPtr(space, ptr, argtypes, resshape)
             space.setitem(self.w_cache, w_key, w_funcptr)
             return w_funcptr
         except KeyError:
@@ -187,7 +201,7 @@
 where argtype_list is a list of single characters and restype is a single
 character. The character meanings are more or less the same as in the struct
 module, except that s has trailing \x00 added, while p is considered a raw
-buffer."""
+buffer.""" # xxx fix doc
 )
 
 unroll_letters_for_numbers = unrolling_iterable("bBhHiIlLqQ")
@@ -197,6 +211,10 @@
     w_exception = space.getattr(w_mod, space.wrap("SegfaultException"))
     return OperationError(w_exception, space.wrap(reason))
 
+class W_DataShape(Wrappable):
+    
+    def allocate(self, space, length, autofree=False):
+        raise NotImplementedError
 
 class W_DataInstance(Wrappable):
     def __init__(self, space, size, address=r_uint(0)):
@@ -305,14 +323,9 @@
 wrap_value._annspecialcase_ = 'specialize:arg(1)'
 
 class W_FuncPtr(Wrappable):
-    def __init__(self, space, ptr, argtypes, restype):
-        from pypy.module._rawffi.array import get_array_cache
+    def __init__(self, space, ptr, argtypes, resshape):
         self.ptr = ptr
-        if restype != 'v':
-            cache = get_array_cache(space)
-            self.resarray = cache.get_array_type(letter2tp(space, restype))
-        else:
-            self.resarray = None
+        self.resshape = resshape
         self.argtypes = argtypes
 
     def call(self, space, args_w):
@@ -352,8 +365,8 @@
                         raise OperationError(space.w_TypeError, space.wrap(msg))
             args_ll.append(arg.ll_buffer)
             # XXX we could avoid the intermediate list args_ll
-        if self.resarray is not None:
-            result = self.resarray.allocate(space, 1)
+        if self.resshape is not None:
+            result = self.resshape.allocate(space, 1)
             self.ptr.call(args_ll, result.ll_buffer)
             return space.wrap(result)
         else:

Modified: pypy/dist/pypy/module/_rawffi/structure.py
==============================================================================
--- pypy/dist/pypy/module/_rawffi/structure.py	(original)
+++ pypy/dist/pypy/module/_rawffi/structure.py	Wed Feb 27 14:38:34 2008
@@ -11,9 +11,10 @@
 from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.interpreter.error import OperationError, wrap_oserror
 from pypy.module._rawffi.interp_rawffi import segfault_exception
-from pypy.module._rawffi.interp_rawffi import W_DataInstance
+from pypy.module._rawffi.interp_rawffi import W_DataShape, W_DataInstance
 from pypy.module._rawffi.interp_rawffi import wrap_value, unwrap_value
 from pypy.module._rawffi.interp_rawffi import unpack_typecode
+from pypy.rlib import libffi
 from pypy.rlib.rarithmetic import intmask, r_uint
 
 def unpack_fields(space, w_fields):
@@ -45,7 +46,7 @@
     return size, alignment, pos
 
 
-class W_Structure(Wrappable):
+class W_Structure(W_DataShape):
     def __init__(self, space, w_fields):
         fields = unpack_fields(space, w_fields)
         name_to_index = {}
@@ -62,6 +63,12 @@
         self.fields = fields
         self.name_to_index = name_to_index
 
+    def allocate(self, space, length, autofree=False):
+        # length is ignored!
+        if autofree:
+            return W_StructureInstanceAutoFree(space, self)
+        return W_StructureInstance(space, self, 0)
+
     def getindex(self, space, attr):
         try:
             return self.name_to_index[attr]
@@ -70,9 +77,7 @@
                 "C Structure has no attribute %s" % attr))
 
     def descr_call(self, space, autofree=False):
-        if autofree:
-            return space.wrap(W_StructureInstanceAutoFree(space, self))
-        return space.wrap(W_StructureInstance(space, self, 0))
+        return space.wrap(self.allocate(space, 1, autofree))
     descr_call.unwrap_spec = ['self', ObjSpace, int]
 
     def descr_repr(self, space):
@@ -96,6 +101,21 @@
                                space.wrap(self.alignment)])
     descr_gettypecode.unwrap_spec = ['self', ObjSpace]
 
+    # get the corresponding ffi_type
+    ffi_type = lltype.nullptr(libffi.FFI_TYPE_P.TO)
+
+    def get_ffi_type(self):
+        if not self.ffi_type:
+            self.ffi_type = libffi.make_struct_ffitype(self.size,
+                                                       self.alignment)
+        return self.ffi_type
+    
+    def __del__(self):
+        if self.ffi_type:
+            lltype.free(self.ffi_type, flavor='raw')
+    
+
+
 def descr_new_structure(space, w_type, w_fields):
     return space.wrap(W_Structure(space, w_fields))
 

Modified: pypy/dist/pypy/module/_rawffi/test/test__rawffi.py
==============================================================================
--- pypy/dist/pypy/module/_rawffi/test/test__rawffi.py	(original)
+++ pypy/dist/pypy/module/_rawffi/test/test__rawffi.py	Wed Feb 27 14:38:34 2008
@@ -120,6 +120,24 @@
         long sum_x_y(struct x_y s) {
             return s.x + s.y;
         }
+
+        struct s2h {
+            short x;
+            short y;
+        };
+
+        struct s2h give(short x, short y) {
+            struct s2h out;
+            out.x = x;
+            out.y = y;
+            return out;
+        }
+
+        struct s2h perturb(struct s2h inp) {
+            inp.x *= 2;
+            inp.y *= 3;
+            return inp;
+        }
         
         '''))
         return compile_c_module([c_file], 'x', ExternalCompilationInfo())
@@ -632,6 +650,40 @@
         res = sum_x_y(x_y)
         assert res[0] == 420
         x_y.free()
+        res.free()
+
+    def test_ret_struct(self):
+        import _rawffi
+        S2H = _rawffi.Structure([('x', 'h'), ('y', 'h')])
+        s2h = S2H()
+        lib = _rawffi.CDLL(self.lib_name)
+        give = lib.ptr('give', ['h', 'h'], S2H)
+        a1 = _rawffi.Array('h')(1)
+        a2 = _rawffi.Array('h')(1)
+        a1[0] = 13
+        a2[0] = 17
+        res = give(a1, a2)
+        assert isinstance(res, _rawffi.StructureInstance)
+        assert res.shape is S2H
+        assert res.x == 13
+        assert res.y == 17
+        res.free()
+        a1.free()
+        a2.free()
+
+        s2h.x = 7
+        s2h.y = 11
+        perturb = lib.ptr('perturb', [S2H.gettypecode()], S2H)
+        res = perturb(s2h)
+        assert isinstance(res, _rawffi.StructureInstance)
+        assert res.shape is S2H
+        assert res.x == 14
+        assert res.y == 33
+        assert s2h.x == 7
+        assert s2h.y == 11
+        res.free()
+        
+        s2h.free()
 
 
 class AppTestAutoFree:

Modified: pypy/dist/pypy/rlib/test/test_libffi.py
==============================================================================
--- pypy/dist/pypy/rlib/test/test_libffi.py	(original)
+++ pypy/dist/pypy/rlib/test/test_libffi.py	Wed Feb 27 14:38:34 2008
@@ -231,3 +231,78 @@
         del lib
 
         assert not ALLOCATED
+
+    def test_ret_struct_val(self):
+        from pypy.translator.tool.cbuild import compile_c_module, \
+             ExternalCompilationInfo
+        from pypy.tool.udir import udir
+
+        c_file = udir.ensure("test_libffi", dir=1).join("xlib.c")
+        c_file.write(py.code.Source('''
+        #include <stdlib.h>
+        #include <stdio.h>
+
+        struct s2h {
+            short x;
+            short y;
+        };
+
+        struct s2h give(short x, short y) {
+            struct s2h out;
+            out.x = x;
+            out.y = y;
+            return out;
+        }
+
+        struct s2h perturb(struct s2h inp) {
+            inp.x *= 2;
+            inp.y *= 3;
+            return inp;
+        }
+        
+        '''))
+        lib_name = compile_c_module([c_file], 'x', ExternalCompilationInfo())
+
+        lib = CDLL(lib_name)
+
+        size = ffi_type_sshort.c_size*2
+        alignment = ffi_type_sshort.c_alignment
+        tp = make_struct_ffitype(size, alignment)
+
+        give  = lib.getrawpointer('give', [ffi_type_sshort, ffi_type_sshort],
+                                  tp)
+        inbuffer = lltype.malloc(rffi.SHORTP.TO, 2, flavor='raw')
+        inbuffer[0] = rffi.cast(rffi.SHORT, 40)
+        inbuffer[1] = rffi.cast(rffi.SHORT, 72)
+
+        outbuffer = lltype.malloc(rffi.SHORTP.TO, 2, flavor='raw')
+
+        give.call([rffi.cast(rffi.VOIDP, inbuffer),
+                   rffi.cast(rffi.VOIDP, rffi.ptradd(inbuffer, 1))],
+                   rffi.cast(rffi.VOIDP, outbuffer))
+
+        assert outbuffer[0] == 40
+        assert outbuffer[1] == 72
+
+        perturb  = lib.getrawpointer('perturb', [tp], tp)
+
+        inbuffer[0] = rffi.cast(rffi.SHORT, 7)
+        inbuffer[1] = rffi.cast(rffi.SHORT, 11)
+
+        perturb.call([rffi.cast(rffi.VOIDP, inbuffer)],
+                     rffi.cast(rffi.VOIDP, outbuffer))
+
+        assert inbuffer[0] == 7
+        assert inbuffer[1] == 11
+
+        assert outbuffer[0] == 14
+        assert outbuffer[1] == 33
+
+        lltype.free(outbuffer, flavor='raw')
+        lltype.free(inbuffer, flavor='raw')
+        del give
+        del perturb
+        lltype.free(tp, flavor='raw')
+        del lib
+
+        assert not ALLOCATED



More information about the Pypy-commit mailing list