[pypy-svn] r50224 - in pypy/dist/pypy/rpython/lltypesystem: . test

fijal at codespeak.net fijal at codespeak.net
Mon Dec 31 14:43:46 CET 2007


Author: fijal
Date: Mon Dec 31 14:43:45 2007
New Revision: 50224

Modified:
   pypy/dist/pypy/rpython/lltypesystem/rffi.py
   pypy/dist/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
   pypy/dist/pypy/rpython/lltypesystem/test/test_rffi.py
Log:
Rough implementation of rffi callbacks. Does not include:
* exception handling
* GIL handling
* stackless explosion
etc...


Modified: pypy/dist/pypy/rpython/lltypesystem/rffi.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/rffi.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/rffi.py	Mon Dec 31 14:43:45 2007
@@ -11,8 +11,12 @@
 from pypy.tool.sourcetools import func_with_new_name
 from pypy.rpython.tool.rfficache import platform
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
+from pypy.translator.backendopt.canraise import RaiseAnalyzer
 import os
 
+class UnhandledRPythonException(Exception):
+    pass
+
 class CConstant(Symbolic):
     """ A C-level constant, maybe #define, rendered directly.
     """
@@ -26,6 +30,13 @@
     def lltype(self):
         return self.TP
 
+def isfunctype(TP):
+    """ Evil hack to get rid of flow objspace inability
+    to accept .TO when TP is not a pointer
+    """
+    return isinstance(TP, lltype.Ptr) and isinstance(TP.TO, lltype.FuncType)
+isfunctype._annspecialcase_ = 'specialize:memo'
+
 def llexternal(name, args, result, _callable=None,
                compilation_info=ExternalCompilationInfo(),
                sandboxsafe=False, threadsafe='auto',
@@ -92,6 +103,8 @@
                     # XXX leaks if a str2charp() fails with MemoryError
                     # and was not the first in this function
                     freeme = arg
+            elif isfunctype(TARGET):
+                arg = _callback(arg, TARGET)
             else:
                 SOURCE = lltype.typeOf(arg)
                 if SOURCE != TARGET:
@@ -121,6 +134,7 @@
     wrapper._always_inline_ = True
     # for debugging, stick ll func ptr to that
     wrapper._ptr = funcptr
+
     return func_with_new_name(wrapper, name)
 
 AroundFnPtr = lltype.Ptr(lltype.FuncType([], lltype.Void))
@@ -132,6 +146,54 @@
 aroundstate = AroundState()
 aroundstate._freeze_()
 
+def _callback(arg, TP):
+    return lltype.functionptr(TP.TO, arg.func_name, _callable=arg)
+_callback._annspecialcase_ = 'specialize:arg(1)'
+
+class Entry(ExtRegistryEntry):
+    _about_ = _callback
+    _CACHE = {}
+
+    def compute_result_annotation(self, s_pbc, s_TP):
+        assert s_TP.is_constant()
+        assert s_pbc.is_constant()
+        # XXX in general this can be non-constant, but get_unique_llfn
+        #     will not work in this case
+        TP = s_TP.const
+        bk = self.bookkeeper
+        args_s = [annmodel.lltype_to_annotation(ll_arg) for ll_arg in TP.TO.ARGS]
+        res = bk.emulate_pbc_call(s_pbc, s_pbc, args_s)
+        return annmodel.SomePtr(TP)
+
+    # this is some wrapper creation for handling exceptions.
+    # I think this is ugly and better way is needed for that,
+    # but we definitely need a way to express exception raising inside
+    # the callback function
+
+    #def _get_or_create_wrapper_pbc(self, bk, func):
+    #    try:
+    #        return self._CACHE[func]
+    #    except:
+    #        def wrapper(*args):
+    #            try:
+    #                return func(*args)
+    #            except Exception, e:
+    #                os.write(2, "Unhandled Fatal RPython exception %s in callback" % str(e))
+    #                return 0
+    #            # we ignore exception here, we can exit the program as well
+    #            # not sure what is the best way
+    #        s_pbc = annmodel.SomePBC([bk.getdesc(wrapper)])
+    #        self._CACHE[func] = s_pbc
+    #        return s_pbc
+
+    def specialize_call(self, hop):
+        #hop.exception_cannot_occur()
+        ## XXX fish a bit to have a wrapper here, not sure if annmixlevel
+        ##     is not waaaay better here
+        #repr = hop.rtyper.getrepr(self._CACHE[hop.args_s[0].const])
+        repr = hop.args_r[0]
+        return repr.get_unique_llfn()
+
 # ____________________________________________________________
 
 TYPES = []
@@ -219,6 +281,10 @@
     return lltype.Ptr(CArray(tp))
 CArray._annspecialcase_ = 'specialize:memo'
 
+def CCallback(args, res):
+    return lltype.Ptr(lltype.FuncType(args, res))
+CCallback._annspecialcase_ = 'specialize:memo'
+
 def COpaque(name, hints=None, compilation_info=None):
     if compilation_info is None:
         compilation_info = ExternalCompilationInfo()
@@ -297,6 +363,9 @@
 # void *   - for now, represented as char *
 VOIDP = lltype.Ptr(lltype.Array(lltype.Char, hints={'nolength': True}))
 
+# void **
+VOIDPP = CArrayPtr(VOIDP)
+
 # char *
 CCHARP = lltype.Ptr(lltype.Array(lltype.Char, hints={'nolength': True}))
 

Modified: pypy/dist/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/test/test_ll2ctypes.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/test/test_ll2ctypes.py	Mon Dec 31 14:43:45 2007
@@ -709,3 +709,48 @@
         res = f()
         assert res == 16
         assert g() == "c"
+
+    def test_c_callback(self):
+        c_source = py.code.Source("""
+        int eating_callback(int arg, int(*call)(int))
+        {
+            return call(arg);
+        }
+        """)
+
+        eci = ExternalCompilationInfo(separate_module_sources=[c_source])
+
+        args = [rffi.INT, rffi.CCallback([rffi.INT], rffi.INT)]
+        eating_callback = rffi.llexternal('eating_callback', args, rffi.INT,
+                                          compilation_info=eci)
+
+        def g(i):
+            return i + 3
+
+        def f():
+            return eating_callback(3, g)
+
+        assert f() == 6
+
+    def test_qsort(self):
+        TP = rffi.CArrayPtr(rffi.INT)
+        a = lltype.malloc(TP.TO, 5, flavor='raw')
+        a[0] = 5
+        a[1] = 3
+        a[2] = 2
+        a[3] = 1
+        a[4] = 4
+
+        def compare(a, b):
+            if a[0] > b[0]:
+                return 1
+            else:
+                return -1
+
+        CALLBACK = rffi.CCallback([rffi.VOIDP, rffi.VOIDP], rffi.INT)
+        qsort = rffi.llexternal('qsort', [rffi.VOIDP, rffi.INT,
+                                          rffi.INT, CALLBACK], lltype.Void)
+
+        qsort(rffi.cast(rffi.VOIDP, a), 5, rffi.sizeof(rffi.INT), compare)
+        for i in range(5):
+            assert a[i] == i + 1

Modified: pypy/dist/pypy/rpython/lltypesystem/test/test_rffi.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/test/test_rffi.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/test/test_rffi.py	Mon Dec 31 14:43:45 2007
@@ -319,6 +319,48 @@
         assert fn() == 16
         gn = self.compile(g, [])
         assert gn() == "c"
+
+    def test_c_callback(self):
+        h_source = py.code.Source("""
+        int eating_callback(int arg, int(*call)(int))
+        {
+            return call(arg);
+        }
+        """)
+        
+        h_include = udir.join('callback.h')
+        h_include.write(h_source)
+
+        eci = ExternalCompilationInfo(includes=['callback.h'],
+                                      include_dirs=[str(udir)])
+
+        args = [INT, CCallback([INT], INT)]
+        eating_callback = llexternal('eating_callback', args, INT,
+                                     compilation_info=eci)
+
+        def g(i):
+            return i + 3
+
+        def z(i):
+            if i:
+                raise ValueError()
+            else:
+                return 0
+
+        def f():
+            return eating_callback(3, g)
+
+        fn = self.compile(f, [])
+        assert fn() == 6
+
+        def z2():
+            return eating_callback(3, z)
+
+        # this should complain if there are unhandled exceptions inside
+        # callbacks, or complain if really raising exception
+        #fn = self.compile(z2, [])
+        #fn()
+        #raises(UnhandledRPythonException, "self.compile(z2, [])")
             
 class TestRffiInternals:
     def test_struct_create(self):



More information about the Pypy-commit mailing list