[pypy-svn] r31116 - in pypy/dist/pypy: annotation rpython/rctypes rpython/rctypes/test

arigo at codespeak.net arigo at codespeak.net
Mon Aug 7 16:54:10 CEST 2006


Author: arigo
Date: Mon Aug  7 16:54:08 2006
New Revision: 31116

Added:
   pypy/dist/pypy/rpython/rctypes/rfunc.py   (contents, props changed)
Modified:
   pypy/dist/pypy/annotation/unaryop.py
   pypy/dist/pypy/rpython/rctypes/afunc.py
   pypy/dist/pypy/rpython/rctypes/avoid_p.py
   pypy/dist/pypy/rpython/rctypes/rmodel.py
   pypy/dist/pypy/rpython/rctypes/test/_rctypes_test.c
   pypy/dist/pypy/rpython/rctypes/test/test_ctypes.py
   pypy/dist/pypy/rpython/rctypes/test/test_rctypes.py
   pypy/dist/pypy/rpython/rctypes/test/test_rfunc.py
   pypy/dist/pypy/rpython/rctypes/test/test_rvoid_p.py
Log:
rctypes support for passing ctypes function pointers around, calling
them generically and casting between them and 'void*'.



Modified: pypy/dist/pypy/annotation/unaryop.py
==============================================================================
--- pypy/dist/pypy/annotation/unaryop.py	(original)
+++ pypy/dist/pypy/annotation/unaryop.py	Mon Aug  7 16:54:08 2006
@@ -701,6 +701,11 @@
     def is_true(cto):
         return SomeBool()
 
+    def simple_call(cto, *args_s):
+        # for variables containing ctypes function pointers
+        entry = extregistry.lookup_type(cto.knowntype)
+        return entry.compute_result_annotation(*args_s)
+
 #_________________________________________
 # memory addresses
 

Modified: pypy/dist/pypy/rpython/rctypes/afunc.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/afunc.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/afunc.py	Mon Aug  7 16:54:08 2006
@@ -1,5 +1,6 @@
 from pypy.annotation.model import SomeCTypesObject
 from pypy.annotation import model as annmodel
+from pypy.annotation.pairtype import pairtype
 from pypy.rpython.error import TyperError
 from pypy.rpython.rctypes.implementation import CTypesEntry
 from pypy.rpython.lltypesystem import lltype
@@ -10,17 +11,75 @@
 CFuncPtrType = type(ctypes.CFUNCTYPE(None))
 
 
+class SomeCTypesFunc(annmodel.SomeBuiltin):
+    """Stands for a known constant ctypes function.  Variables containing
+    potentially multiple ctypes functions are regular SomeCTypesObjects.
+    This is a separate annotation because some features are only supported
+    for calls to constant functions, like _rctypes_pyerrchecker_ and
+    functions with no declared argtypes.  It also produces better code:
+    a direct_call instead of an indirect_call.
+    """
+    def normalized(self):
+        ctype = normalized_func_ctype(self.const)
+        return cto_union(ctype, ctype)    # -> SomeCTypesObject
+
+class __extend__(pairtype(SomeCTypesFunc, SomeCTypesFunc)):
+    def union((ctf1, ctf2)):
+        ctype1 = normalized_func_ctype(ctf1.const)
+        ctype2 = normalized_func_ctype(ctf2.const)
+        return cto_union(ctype1, ctype2)
+
+class __extend__(pairtype(SomeCTypesFunc, SomeCTypesObject)):
+    def union((ctf1, cto2)):
+        ctype1 = normalized_func_ctype(ctf1.const)
+        return cto_union(ctype1, cto2.knowntype)
+
+class __extend__(pairtype(SomeCTypesObject, SomeCTypesFunc)):
+    def union((cto1, ctf2)):
+        ctype2 = normalized_func_ctype(ctf2.const)
+        return cto_union(cto1.knowntype, ctype2)
+
+
+def normalized_func_ctype(cfuncptr):
+    if getattr(cfuncptr, 'argtypes', None) is None:
+        raise annmodel.UnionError("cannot merge two ctypes functions "
+                                  "without declared argtypes")
+    return ctypes.CFUNCTYPE(cfuncptr.restype,
+                            *cfuncptr.argtypes)
+
+def cto_union(ctype1, ctype2):
+    if ctype1 != ctype2:
+        raise annmodel.UnionError("a ctypes function object can only be "
+                                  "merged with another function with the same "
+                                  "signature")
+    return SomeCTypesObject(ctype1, ownsmemory=True)
+
+
 class CallEntry(CTypesEntry):
     """Annotation and rtyping of calls to external functions
     declared with ctypes.
     """
     _metatype_ = CFuncPtrType
 
+    def compute_annotation(self):
+        #self.ctype_object_discovered()
+        func = self.instance
+        analyser = self.compute_result_annotation
+        methodname = getattr(func, '__name__', None)
+        return SomeCTypesFunc(analyser, methodname=methodname)
+
+    def get_instance_sample(self):
+        if self.instance is not None:
+            return self.instance
+        else:
+            return self.type()    # a sample NULL function object
+
     def compute_result_annotation(self, *args_s):
         """
         Answer the annotation of the external function's result
         """
-        result_ctype = self.instance.restype
+        cfuncptr = self.get_instance_sample()
+        result_ctype = cfuncptr.restype
         if result_ctype is None:
             return None
         if result_ctype is ctypes.py_object:
@@ -61,98 +120,15 @@
 ##                                                           s_res))
 
     def specialize_call(self, hop):
-        from pypy.rpython.rctypes.rmodel import CTypesValueRepr
+        from pypy.rpython.rctypes.rfunc import get_funcptr_constant
+        from pypy.rpython.rctypes.rfunc import rtype_funcptr_call
         cfuncptr = self.instance
-        fnname = cfuncptr.__name__
-
-        def repr_for_ctype(ctype):
-            s = SomeCTypesObject(ctype, ownsmemory=False)
-            r = hop.rtyper.getrepr(s)
-            return r
-
-        args_r = []
-        if getattr(cfuncptr, 'argtypes', None) is not None:
-            for ctype in cfuncptr.argtypes:
-                args_r.append(repr_for_ctype(ctype))
-        else:
-            # unspecified argtypes: use ctypes rules for arguments
-            for s_arg, r_arg in zip(hop.args_s, hop.args_r):
-                if not isinstance(s_arg, SomeCTypesObject):
-                    # accept integers, strings, or None
-                    if isinstance(s_arg, annmodel.SomeInteger):
-                        r_arg = repr_for_ctype(ctypes.c_long)
-                    elif (isinstance(s_arg, annmodel.SomeString)
-                          or s_arg == annmodel.s_None):
-                        r_arg = repr_for_ctype(ctypes.c_char_p)
-                    else:
-                        raise TyperError("call with no argtypes: don't know "
-                                         "how to convert argument %r"%(s_arg,))
-                args_r.append(r_arg)
-
-        hop.rtyper.call_all_setups()
-        vlist = hop.inputargs(*args_r)
-        unwrapped_args_v = []
-        ARGTYPES = []
-        for r_arg, v in zip(args_r, vlist):
-            if isinstance(r_arg, CTypesValueRepr):
-                # ValueRepr case
-                unwrapped_args_v.append(r_arg.getvalue(hop.llops, v))
-                ARGTYPES.append(r_arg.ll_type)
-            else:
-                # RefRepr case -- i.e. the function argument that we pass by
-                # value is e.g. a complete struct; we pass a pointer to it
-                # in the low-level graphs and it's up to the back-end to
-                # generate the correct dereferencing
-                unwrapped_args_v.append(r_arg.get_c_data(hop.llops, v))
-                ARGTYPES.append(r_arg.c_data_type)
-        if cfuncptr.restype is not None:
-            s_res = SomeCTypesObject(cfuncptr.restype, ownsmemory=True)
-            r_res = hop.rtyper.getrepr(s_res)
-            RESTYPE = r_res.ll_type
-        else:
-            RESTYPE = lltype.Void
-
-        kwds = {}
-        if hasattr(cfuncptr, 'llinterp_friendly_version'):
-            kwds['_callable'] = cfuncptr.llinterp_friendly_version
-        suppress_pyerr_occurred = False
-        if (cfuncptr._flags_ & ctypes._FUNCFLAG_PYTHONAPI) == 0:
-            suppress_pyerr_occurred = True
-        if hasattr(cfuncptr, '_rctypes_pyerrchecker_'):
-            suppress_pyerr_occurred = True
-        if suppress_pyerr_occurred:
-            kwds['includes'] = getattr(cfuncptr, 'includes', ())
-            kwds['libraries'] = getattr(cfuncptr, 'libraries', ())
-        #else:
-        #   no 'includes': hack to trigger in GenC a PyErr_Occurred() check
-
-        hop.exception_cannot_occur()
-        v_result = hop.llops.gencapicall(fnname, unwrapped_args_v,
-                                         resulttype = RESTYPE,
-                                         **kwds)
-        # XXX hack! hack! temporary! I promize!
-        FUNCTYPE = lltype.FuncType(ARGTYPES, RESTYPE)
-        last_op = hop.llops[-1]
-        assert last_op.opname == 'direct_call'
-        last_op.args[0].concretetype = lltype.Ptr(FUNCTYPE)
-        last_op.args[0].value._set_TYPE(last_op.args[0].concretetype)
-        last_op.args[0].value._set_T(FUNCTYPE)
-        last_op.args[0].value._obj._TYPE = FUNCTYPE
-
-        if getattr(cfuncptr, '_rctypes_pyerrchecker_', None):
-            # special extension to support the CPyObjSpace
-            # XXX hackish: someone else -- like the annotator policy --
-            # must ensure that this extra function has been annotated
-            from pypy.translator.translator import graphof
-            func = cfuncptr._rctypes_pyerrchecker_
-            graph = graphof(hop.rtyper.annotator.translator, func)
-            hop.llops.record_extra_call(graph)
-            # build the 'direct_call' operation
-            f = hop.rtyper.getcallable(graph)
-            c = hop.inputconst(lltype.typeOf(f), f)
-            hop.genop('direct_call', [c])
-
-        if RESTYPE is lltype.Void:
-            return None
-        else:
-            return r_res.return_value(hop.llops, v_result)
+        v_funcptr, args_r, r_res = get_funcptr_constant(hop.rtyper, cfuncptr,
+                                                        hop.args_s)
+        pyerrchecker = getattr(cfuncptr, '_rctypes_pyerrchecker_', None)
+        return rtype_funcptr_call(hop, v_funcptr, args_r, r_res, pyerrchecker)
+
+    def get_repr(self, rtyper, s_funcptr):
+        # for variables containing ctypes function pointers
+        from pypy.rpython.rctypes.rfunc import CFuncPtrRepr
+        return CFuncPtrRepr(rtyper, s_funcptr)

Modified: pypy/dist/pypy/rpython/rctypes/avoid_p.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/avoid_p.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/avoid_p.py	Mon Aug  7 16:54:08 2006
@@ -4,6 +4,7 @@
 
 from ctypes import c_void_p, c_int, POINTER, cast, c_char, c_char_p
 from pypy.rpython.rctypes.astringbuf import StringBufferType
+from pypy.rpython.rctypes.afunc import CFuncPtrType, SomeCTypesFunc
 
 PointerType = type(POINTER(c_int))
 
@@ -34,7 +35,8 @@
     _about_ = cast
 
     def checkptr(self, ctype):
-        assert isinstance(ctype, PointerType) or ctype == c_void_p, (
+        assert (isinstance(ctype, PointerType) or ctype == c_void_p or
+                isinstance(ctype, CFuncPtrType)), (
             "cast(): can only cast between pointers so far, not %r" % (ctype,))
 
     def compute_result_annotation(self, s_arg, s_type):
@@ -42,7 +44,8 @@
             "cast(p, %r): argument 2 must be constant" % (s_type,))
         type = s_type.const
         self.checkptr(type)
-        if s_arg.knowntype == StringBufferType:
+        if (s_arg.knowntype == StringBufferType or
+            isinstance(s_arg, SomeCTypesFunc)):
             pass
         else:
             self.checkptr(s_arg.knowntype)
@@ -52,16 +55,23 @@
         from pypy.rpython.rctypes.rpointer import PointerRepr
         from pypy.rpython.rctypes.rvoid_p import CVoidPRepr
         from pypy.rpython.rctypes.rstringbuf import StringBufRepr
+        from pypy.rpython.rctypes.rfunc import CFuncPtrRepr
         from pypy.rpython.lltypesystem import lltype, llmemory
-        assert isinstance(hop.args_r[0], (PointerRepr, CVoidPRepr,
-                                          StringBufRepr))
+        r_arg = hop.args_r[0]
+        if isinstance(hop.args_s[0], SomeCTypesFunc):
+            # cast(const_cfuncptr, c_void_p): force the const_cfuncptr
+            # to become a general non-constant SomeCTypesObject
+            s_arg = hop.args_s[0].normalized()
+            r_arg = hop.rtyper.getrepr(s_arg)
+        assert isinstance(r_arg, (PointerRepr, CVoidPRepr,
+                                  StringBufRepr, CFuncPtrRepr))
         targetctype = hop.args_s[1].const
-        v_box, c_targetctype = hop.inputargs(hop.args_r[0], lltype.Void)
-        if isinstance(hop.args_r[0], StringBufRepr):
+        v_box, c_targetctype = hop.inputargs(r_arg, lltype.Void)
+        if isinstance(r_arg, StringBufRepr):
             v_index = hop.inputconst(lltype.Signed, 0)
-            v_adr = hop.args_r[0].get_c_data_of_item(hop.llops, v_box, v_index)
+            v_adr = r_arg.get_c_data_of_item(hop.llops, v_box, v_index)
         else:
-            v_adr = hop.args_r[0].getvalue(hop.llops, v_box)
+            v_adr = r_arg.getvalue(hop.llops, v_box)
         if v_adr.concretetype != llmemory.Address:
             v_adr = hop.genop('cast_ptr_to_adr', [v_adr],
                               resulttype = llmemory.Address)

Added: pypy/dist/pypy/rpython/rctypes/rfunc.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/rctypes/rfunc.py	Mon Aug  7 16:54:08 2006
@@ -0,0 +1,194 @@
+from pypy.rpython.rtyper import inputconst
+from pypy.rpython.rctypes.rmodel import CTypesValueRepr, CTypesRefRepr
+from pypy.rpython.rctypes.afunc import CFuncPtrType
+from pypy.rpython.error import TyperError
+from pypy.rpython.lltypesystem import lltype
+from pypy.annotation import model as annmodel
+from pypy.annotation.model import SomeCTypesObject
+from pypy.objspace.flow.model import Constant
+
+import ctypes
+
+
+class CFuncPtrRepr(CTypesValueRepr):
+
+    def __init__(self, rtyper, s_funcptr):
+        # For recursive types, getting the args_r and r_result is delayed
+        # until _setup_repr().
+        ll_contents = lltype.Ptr(lltype.ForwardReference())
+        super(CFuncPtrRepr, self).__init__(rtyper, s_funcptr, ll_contents)
+        self.sample = self.ctype()
+        self.argtypes = self.sample.argtypes
+        self.restype = self.sample.restype
+        if self.argtypes is None:
+            raise TyperError("cannot handle yet function pointers with "
+                             "unspecified argument types")
+
+    def _setup_repr(self):
+        # Find the repr and low-level type of the arguments and return value
+        rtyper = self.rtyper
+        args_r = []
+        for arg_ctype in self.argtypes:
+            r = rtyper.getrepr(SomeCTypesObject(arg_ctype,
+                                                ownsmemory=False))
+            args_r.append(r)
+        r_result = rtyper.getrepr(SomeCTypesObject(self.restype,
+                                                   ownsmemory=True))
+        if isinstance(self.ll_type.TO, lltype.ForwardReference):
+            FUNCTYPE = get_funcptr_type(args_r, r_result)
+            self.ll_type.TO.become(FUNCTYPE)
+        self.args_r = args_r
+        self.r_result = r_result
+
+    def ctypecheck(self, value):
+        return (isinstance(value.__class__, CFuncPtrType) and
+                list(value.argtypes) == list(self.argtypes) and
+                value.restype == self.restype)
+
+    def initialize_const(self, p, cfuncptr):
+        if not cfuncptr:   # passed as arg to functions expecting func pointers
+            return
+        c, args_r, r_res = get_funcptr_constant(self.rtyper, cfuncptr, None)
+        p.c_data[0] = c.value
+
+    def rtype_simple_call(self, hop):
+        v_box = hop.inputarg(self, arg=0)
+        v_funcptr = self.getvalue(hop.llops, v_box)
+        hop2 = hop.copy()
+        hop2.r_s_popfirstarg()
+        return rtype_funcptr_call(hop2, v_funcptr, self.args_r, self.r_result)
+
+
+# ____________________________________________________________
+
+
+def get_funcptr_constant(rtyper, cfuncptr, args_s):
+    """Get a Constant ll function pointer from a ctypes function object.
+    """
+    fnname = cfuncptr.__name__
+    args_r, r_res = get_arg_res_repr(rtyper, cfuncptr, args_s)
+    FUNCTYPE = get_funcptr_type(args_r, r_res)
+    flags = get_funcptr_flags(cfuncptr)
+    f = lltype.functionptr(FUNCTYPE, fnname, **flags)
+    return inputconst(lltype.typeOf(f), f), args_r, r_res
+
+
+def get_arg_res_repr(rtyper, cfuncptr, args_s):
+    """Get the reprs to use for the arguments and the return value of a
+    ctypes function call.  The args_s annotations are used to guess the
+    argument types if they are not specified by cfuncptr.argtypes.
+    """
+    def repr_for_ctype(ctype):
+        s = SomeCTypesObject(ctype, ownsmemory=False)
+        r = rtyper.getrepr(s)
+        return r
+
+    args_r = []
+    if getattr(cfuncptr, 'argtypes', None) is not None:
+        for ctype in cfuncptr.argtypes:
+            args_r.append(repr_for_ctype(ctype))
+    else:
+        # unspecified argtypes: use ctypes rules for arguments,
+        # accepting integers, strings, or None
+        for s_arg in args_s:
+            if isinstance(s_arg, SomeCTypesObject):
+                r_arg = rtyper.getrepr(s_arg)
+            elif isinstance(s_arg, annmodel.SomeInteger):
+                r_arg = repr_for_ctype(ctypes.c_long)
+            elif (isinstance(s_arg, annmodel.SomeString)
+                  or s_arg == annmodel.s_None):
+                r_arg = repr_for_ctype(ctypes.c_char_p)
+            else:
+                raise TyperError("call with no argtypes: don't know "
+                                 "how to convert argument %r" % (s_arg,))
+            args_r.append(r_arg)
+    if cfuncptr.restype is not None:
+        s_res = SomeCTypesObject(cfuncptr.restype, ownsmemory=True)
+        r_res = rtyper.getrepr(s_res)
+    else:
+        r_res = None
+    return args_r, r_res
+
+
+def get_funcptr_type(args_r, r_res):
+    """Get the lltype FUNCTYPE to use for a ctypes function call.
+    """
+    ARGTYPES = []
+    for r_arg in args_r:
+        if isinstance(r_arg, CTypesValueRepr):
+            # ValueRepr case
+            ARGTYPES.append(r_arg.ll_type)
+        else:
+            # RefRepr case -- i.e. the function argument that we pass by
+            # value is e.g. a complete struct
+            ARGTYPES.append(r_arg.c_data_type)
+    if r_res is not None:
+        RESTYPE = r_res.ll_type
+    else:
+        RESTYPE = lltype.Void
+    return lltype.FuncType(ARGTYPES, RESTYPE)
+
+
+def get_funcptr_flags(cfuncptr):
+    """Get the fnptr flags to use for the given concrete ctypes function.
+    """
+    kwds = {'external': 'C'}
+    if hasattr(cfuncptr, 'llinterp_friendly_version'):
+        kwds['_callable'] = cfuncptr.llinterp_friendly_version
+    suppress_pyerr_occurred = False
+    if (cfuncptr._flags_ & ctypes._FUNCFLAG_PYTHONAPI) == 0:
+        suppress_pyerr_occurred = True
+    if hasattr(cfuncptr, '_rctypes_pyerrchecker_'):
+        suppress_pyerr_occurred = True
+    if suppress_pyerr_occurred:
+        kwds['includes'] = getattr(cfuncptr, 'includes', ())
+        kwds['libraries'] = getattr(cfuncptr, 'libraries', ())
+    #else:
+    #   no 'includes': hack to trigger in GenC a PyErr_Occurred() check
+    return kwds
+
+
+def rtype_funcptr_call(hop, v_funcptr, args_r, r_res, pyerrchecker=None):
+    """Generate a call to the given ll function pointer.
+    """
+    hop.rtyper.call_all_setups()
+    vlist = hop.inputargs(*args_r)
+    unwrapped_args_v = []
+    for r_arg, v in zip(args_r, vlist):
+        if isinstance(r_arg, CTypesValueRepr):
+            # ValueRepr case
+            unwrapped_args_v.append(r_arg.getvalue(hop.llops, v))
+        elif isinstance(r_arg, CTypesRefRepr):
+            # RefRepr case -- i.e. the function argument that we pass by
+            # value is e.g. a complete struct; we pass a pointer to it
+            # in the low-level graphs and it's up to the back-end to
+            # generate the correct dereferencing
+            unwrapped_args_v.append(r_arg.get_c_data(hop.llops, v))
+        else:
+            assert 0, "ctypes func call got a non-ctypes arg repr"
+
+    FUNCTYPE = v_funcptr.concretetype.TO
+    hop.exception_cannot_occur()
+    if isinstance(v_funcptr, Constant):
+        opname = 'direct_call'
+    else:
+        unwrapped_args_v.append(inputconst(lltype.Void, None))
+        opname = 'indirect_call'
+    v_result = hop.genop(opname, [v_funcptr]+unwrapped_args_v,
+                         resulttype = FUNCTYPE.RESULT)
+
+    if pyerrchecker is not None:
+        # special extension to support the CPyObjSpace
+        # XXX hackish: someone else -- like the annotator policy --
+        # must ensure that this extra function has been annotated
+        from pypy.translator.translator import graphof
+        graph = graphof(hop.rtyper.annotator.translator, pyerrchecker)
+        hop.llops.record_extra_call(graph)
+        # build the 'direct_call' operation
+        f = hop.rtyper.getcallable(graph)
+        c = hop.inputconst(lltype.typeOf(f), f)
+        hop.genop('direct_call', [c])
+
+    if r_res is not None:
+        v_result = r_res.return_value(hop.llops, v_result)
+    return v_result

Modified: pypy/dist/pypy/rpython/rctypes/rmodel.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/rmodel.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/rmodel.py	Mon Aug  7 16:54:08 2006
@@ -69,8 +69,11 @@
         of this object."""
         return None
 
+    def ctypecheck(self, value):
+        return isinstance(value, self.ctype)
+
     def convert_const(self, value):
-        if isinstance(value, self.ctype):
+        if self.ctypecheck(value):
             key = "by_id", id(value)
             keepalive = value
         else:
@@ -82,6 +85,7 @@
         try:
             return self.const_cache[key][0]
         except KeyError:
+            self.setup()
             p = lltype.malloc(self.r_memoryowner.lowleveltype.TO)
             self.initialize_const(p, value)
             if self.ownsmemory:
@@ -229,7 +233,7 @@
     get_c_data_or_value = getvalue
 
     def initialize_const(self, p, value):
-        if isinstance(value, self.ctype):
+        if self.ctypecheck(value):
             value = value.value
         p.c_data[0] = value
 

Modified: pypy/dist/pypy/rpython/rctypes/test/_rctypes_test.c
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/test/_rctypes_test.c	(original)
+++ pypy/dist/pypy/rpython/rctypes/test/_rctypes_test.c	Mon Aug  7 16:54:08 2006
@@ -61,6 +61,13 @@
     return (void *)&p;
 }
 
+typedef int (*fnptr_t)(point);
+
+EXPORT(fnptr_t) _testfunc_get_func(void)
+{
+    return _testfunc_struct;
+}
+
 DL_EXPORT(void)
 init_rctypes_test(void)
 {

Modified: pypy/dist/pypy/rpython/rctypes/test/test_ctypes.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/test/test_ctypes.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/test/test_ctypes.py	Mon Aug  7 16:54:08 2006
@@ -227,3 +227,9 @@
     for i, c in enumerate(struct.pack("l", 12345678)):
         p.contents[i] = ord(c)
     assert x.value == 12345678
+
+def test_cfunctype_inspection():
+    T = CFUNCTYPE(c_int, c_ubyte)
+    # T.argtypes and T.restype don't work, must use a dummy instance
+    assert list(T().argtypes) == [c_ubyte]
+    assert T().restype == c_int

Modified: pypy/dist/pypy/rpython/rctypes/test/test_rctypes.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/test/test_rctypes.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/test/test_rctypes.py	Mon Aug  7 16:54:08 2006
@@ -17,6 +17,7 @@
 
 from ctypes import cdll
 from ctypes import POINTER, Structure, c_int, byref, pointer, c_void_p
+from ctypes import CFUNCTYPE
 
 # __________ compile and load our local test C file __________
 
@@ -102,6 +103,12 @@
 testfunc_erase_type.restype = c_void_p
 testfunc_erase_type.argtypes = []
 
+# _testfunc_get_func
+testfunc_get_func = _rctypes_test._testfunc_get_func
+testfunc_get_func.restype = CFUNCTYPE(c_int, tagpoint)
+testfunc_get_func.argtypes = []
+testfunc_get_func.includes = includes
+
 
 def test_testfunc_struct():
     in_point = tagpoint()
@@ -142,6 +149,15 @@
     assert pt._z == 100
     return pt.x - pt.y                   # this test function is reused below
 
+def test_testfunc_get_func():
+    in_point = tagpoint()
+    in_point.x = -9171831
+    in_point.y = 9171873
+    fn = testfunc_get_func()
+    res = fn(in_point)
+    assert res == 42
+    return res       # this test function is reused below
+
 class Test_annotation:
     def test_annotate_struct(self):
         t = TranslationContext()
@@ -195,3 +211,7 @@
     def test_compile_swap(self):
         fn = compile(test_testfunc_swap, [])
         assert fn() == 4
+
+    def test_compile_get_func(self):
+        fn = compile(test_testfunc_get_func, [])
+        assert fn() == 42

Modified: pypy/dist/pypy/rpython/rctypes/test/test_rfunc.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/test/test_rfunc.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/test/test_rfunc.py	Mon Aug  7 16:54:08 2006
@@ -5,6 +5,7 @@
 import py
 import sys
 import pypy.rpython.rctypes.implementation
+from pypy.annotation import model as annmodel
 from pypy.annotation.annrpython import RPythonAnnotator
 from pypy.translator.translator import TranslationContext, graphof
 from pypy.rpython.test.test_llinterp import interpret
@@ -48,6 +49,15 @@
     return result
 atoi.llinterp_friendly_version = ll_atoi
 
+atol = mylib.atol
+atol.restype = c_long
+atol.argtypes = [c_char_p]
+atol.llinterp_friendly_version = ll_atoi
+
+strlen = mylib.strlen
+strlen.restype = c_long
+strlen.argtypes = [c_char_p]
+
 time_ = mylib.time
 time_.restype = c_long    # should rather use ctypes_platform.getsimpletype()
 time_.argtypes = [POINTER(c_long)]
@@ -218,6 +228,23 @@
 ##        assert a.binding(v1).knowntype == int
 ##        assert a.binding(v2).knowntype == int
 
+    def test_annotate_indirect_call(self):
+        s = '442'
+        def f(n):
+            if n > 0:
+                f = strlen
+            else:
+                f = atol
+            return f
+        a = RPythonAnnotator()
+        s = a.build_types(f, [int])
+        if conftest.option.view:
+            a.translator.view()
+        assert isinstance(s, annmodel.SomeCTypesObject)
+        sample = s.knowntype()
+        assert list(sample.argtypes) == [c_char_p]
+        assert sample.restype == c_long
+
 class Test_specialization:
     def test_specialize_labs(self):
         res = interpret(test_labs, [-11])
@@ -397,3 +424,14 @@
         fn = compile(f, [])
         assert fn() == string
 
+    def test_compile_indirect_call(self):
+        s = '442'
+        def f(n):
+            if n > 0:
+                f = strlen
+            else:
+                f = atol
+            return f(s)
+        fn = compile(f, [int])
+        assert fn(1) == 3
+        assert fn(0) == 442

Modified: pypy/dist/pypy/rpython/rctypes/test/test_rvoid_p.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/test/test_rvoid_p.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/test/test_rvoid_p.py	Mon Aug  7 16:54:08 2006
@@ -11,7 +11,7 @@
 from pypy.rpython.lltypesystem import lltype, llmemory
 from pypy.rpython.test.test_llinterp import interpret
 
-from ctypes import c_void_p, c_int, cast, pointer, POINTER
+from ctypes import c_void_p, c_int, c_long, cast, pointer, POINTER
 from ctypes import c_char, c_byte, c_char_p, create_string_buffer, CFUNCTYPE
 
 class Test_annotation:
@@ -92,3 +92,21 @@
 
         fn = compile(func, [])
         assert fn() == 12
+
+    def test_compile_funcptr_as_void_p(self):
+        from pypy.rpython.rctypes.test.test_rfunc import labs
+        UNARYFN = CFUNCTYPE(c_long, c_long)
+        def func(n):
+            if n < -100:
+                p1 = c_void_p()   # NULL void pointer - don't try!
+            else:
+                p1 = cast(labs, c_void_p)
+            p2 = cast(p1, UNARYFN)
+            return p2(n)
+
+        assert func(-41) == 41
+        assert func(72) == 72
+
+        fn = compile(func, [int])
+        assert fn(-42) == 42
+        assert fn(71) == 71



More information about the Pypy-commit mailing list