[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