[pypy-svn] r12868 - in pypy/dist/pypy/translator/c: . test

arigo at codespeak.net arigo at codespeak.net
Sun May 29 16:20:58 CEST 2005


Author: arigo
Date: Sun May 29 16:20:58 2005
New Revision: 12868

Added:
   pypy/dist/pypy/translator/c/int_include.h
      - copied unchanged from r12866, pypy/dist/pypy/translator/genc/int_include.h
   pypy/dist/pypy/translator/c/wrapper.py   (contents, props changed)
Modified:
   pypy/dist/pypy/translator/c/database.py
   pypy/dist/pypy/translator/c/funcgen.py
   pypy/dist/pypy/translator/c/g_include.h
   pypy/dist/pypy/translator/c/g_support.h
   pypy/dist/pypy/translator/c/genc.py
   pypy/dist/pypy/translator/c/node.py
   pypy/dist/pypy/translator/c/pyobj.py
   pypy/dist/pypy/translator/c/test/test_database.py
   pypy/dist/pypy/translator/c/test/test_genc.py
Log:
Reintroduced the PyObject wrappers for C functions.
Now we can again write tests that compile and run C code.


Modified: pypy/dist/pypy/translator/c/database.py
==============================================================================
--- pypy/dist/pypy/translator/c/database.py	(original)
+++ pypy/dist/pypy/translator/c/database.py	Sun May 29 16:20:58 2005
@@ -13,13 +13,13 @@
 
 class LowLevelDatabase:
 
-    def __init__(self):
+    def __init__(self, rtyper=None):
         self.structdefnodes = {}
         self.structdeflist = []
         self.containernodes = {}
         self.containerlist = []
         self.namespace = CNameManager()
-        self.pyobjmaker = PyObjMaker(self.namespace, self.get)
+        self.pyobjmaker = PyObjMaker(self.namespace, self.get, rtyper)
 
     def gettypedefnode(self, T, varlength=1):
         if varlength <= 1:

Modified: pypy/dist/pypy/translator/c/funcgen.py
==============================================================================
--- pypy/dist/pypy/translator/c/funcgen.py	(original)
+++ pypy/dist/pypy/translator/c/funcgen.py	Sun May 29 16:20:58 2005
@@ -34,6 +34,7 @@
                 result.extend(block.inputargs)
                 for op in block.operations:
                     result.extend(op.args)
+                    result.append(op.result)
                 for link in block.exits:
                     result.extend(link.args)
         traverse(visit, self.graph)
@@ -61,10 +62,6 @@
             if isinstance(v, Constant):
                 yield llvalue_from_constant(v)
 
-    def decl(self, v):
-        assert isinstance(v, Variable), repr(v)
-        return cdecl(self.typemap[v], v.name)
-
     def expr(self, v):
         if isinstance(v, Variable):
             return v.name
@@ -87,7 +84,10 @@
 
         for v in self.allvariables():
             if v not in inputargset:
-                yield '%s;' % self.decl(v)
+                result = cdecl(self.typemap[v], v.name) + ';'
+                if self.lltypemap[v] == Void:
+                    result = '/*%s*/' % result
+                yield result
 
     # ____________________________________________________________
 
@@ -122,7 +122,9 @@
                     line += '\t' + self.cincref(a2)
                 yield line
             for v in has_ref:
-                yield self.cdecref(v, linklocalvars[v])
+                line = self.cdecref(v, linklocalvars[v])
+                if line:
+                    yield line
             yield 'goto block%d;' % blocknum[link.target]
 
         # collect all blocks

Modified: pypy/dist/pypy/translator/c/g_include.h
==============================================================================
--- pypy/dist/pypy/translator/c/g_include.h	(original)
+++ pypy/dist/pypy/translator/c/g_include.h	Sun May 29 16:20:58 2005
@@ -14,4 +14,5 @@
 #include "g_support.h"
 #include "g_module.h"
 
+#include "int_include.h"
 #include "pyobj_include.h"

Modified: pypy/dist/pypy/translator/c/g_support.h
==============================================================================
--- pypy/dist/pypy/translator/c/g_support.h	(original)
+++ pypy/dist/pypy/translator/c/g_support.h	Sun May 29 16:20:58 2005
@@ -9,8 +9,6 @@
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #endif /* MIN */
 
-#define MOVE(x, y)             y = x;
-
 #define FAIL_EXCEPTION(err, exc, msg) \
 	{ \
 		PyErr_SetString(exc, msg); \
@@ -336,7 +334,7 @@
 	}
 	PyErr_Format(PyExc_TypeError, "%s() got only %d argument(s)",
 		     PyString_AS_STRING(fname),
-		     position-1);
+		     position);
 	return NULL;
 }
 

Modified: pypy/dist/pypy/translator/c/genc.py
==============================================================================
--- pypy/dist/pypy/translator/c/genc.py	(original)
+++ pypy/dist/pypy/translator/c/genc.py	Sun May 29 16:20:58 2005
@@ -44,6 +44,7 @@
     #
     # PyObject support (strange) code
     #
+    pyobjmaker = database.pyobjmaker
     print >> f
     print >> f, '/***********************************************************/'
     print >> f, '/***  Table of global PyObjects                          ***/'
@@ -52,7 +53,7 @@
     for node in database.globalcontainers():
         if isinstance(node, PyObjectNode):
             name = node.name
-            if not name.startswith('gfunc_'):
+            if name not in pyobjmaker.wrappers:
                 print >> f, '\t{&%s, "%s"},' % (name, name)
     print >> f, '\t{ NULL }\t/* Sentinel */'
     print >> f, '};'
@@ -61,7 +62,14 @@
     print >> f, '/***  Table of functions                                 ***/'
     print >> f
     print >> f, 'static globalfunctiondef_t globalfunctiondefs[] = {'
-    print >> f, '\t/* XXX */'
+    wrappers = pyobjmaker.wrappers.items()
+    wrappers.sort()
+    for globalobject_name, (base_name, wrapper_name) in wrappers:
+        print >> f, ('\t{&%s, {"%s", (PyCFunction)%s, '
+                     'METH_VARARGS|METH_KEYWORDS}},' % (
+            globalobject_name,
+            base_name,
+            wrapper_name))
     print >> f, '\t{ NULL }\t/* Sentinel */'
     print >> f, '};'
     print >> f

Modified: pypy/dist/pypy/translator/c/node.py
==============================================================================
--- pypy/dist/pypy/translator/c/node.py	(original)
+++ pypy/dist/pypy/translator/c/node.py	Sun May 29 16:20:58 2005
@@ -246,6 +246,41 @@
         yield '}'
 
 
+class CExternalFuncNode(ContainerNode):
+    globalcontainer = True
+
+    def __init__(self, db, T, obj):
+        self.db = db
+        self.T = T
+        self.obj = obj
+        #self.dependencies = {}
+        self.typename = db.gettype(T)  #, who_asks=self)
+        self.name = obj._name
+        self.ptrname = self.name
+
+    def enum_dependencies(self):
+        return []
+
+    def implementation(self):
+        return []
+
+    def forward_declaration(self):
+        return []
+
+    def implementation(self):
+        return []
+
+
+def funcnodemaker(db, T, obj):
+    if hasattr(obj, 'graph'):
+        cls = FuncNode
+    elif getattr(obj, 'external', None) == 'C':
+        cls = CExternalFuncNode
+    else:
+        raise ValueError, "don't know about %r" % (obj,)
+    return cls(db, T, obj)
+
+
 class PyObjectNode(ContainerNode):
     globalcontainer = True
     typename = 'PyObject @'
@@ -271,6 +306,6 @@
     GcStruct:     StructNode,
     Array:        ArrayNode,
     GcArray:      ArrayNode,
-    FuncType:     FuncNode,
+    FuncType:     funcnodemaker,
     PyObjectType: PyObjectNode,
     }

Modified: pypy/dist/pypy/translator/c/pyobj.py
==============================================================================
--- pypy/dist/pypy/translator/c/pyobj.py	(original)
+++ pypy/dist/pypy/translator/c/pyobj.py	Sun May 29 16:20:58 2005
@@ -22,9 +22,10 @@
     reconstruct them.
     """
 
-    def __init__(self, namespace, getvalue):
+    def __init__(self, namespace, getvalue, rtyper=None):
         self.namespace = namespace
         self.getvalue = getvalue
+        self.rtyper = rtyper
         self.initcode = [      # list of lines for the module's initxxx()
             'import new, types, sys',
             ]
@@ -33,6 +34,7 @@
                                #   for later in initxxx() -- for recursive
                                #   objects
         self.debugstack = ()  # linked list of nested nameof()
+        self.wrappers = {}    # {'pycfunctionvariable': ('name', 'wrapperfn')}
 
     def nameof(self, obj, debug=None):
         if debug:
@@ -141,16 +143,25 @@
         self.initcode.append('  raise NotImplementedError')
         return name
 
-    def nameof_function(self, func, progress=['-\x08', '\\\x08',
-                                              '|\x08', '/\x08']):
-        funcdef = self.genc().getfuncdef(func)
-        if funcdef is None:
-            return self.skipped_function(func)
-        if not self.translator.frozen:
-            p = progress.pop(0)
-            sys.stderr.write(p)
-            progress.append(p)
-        return funcdef.get_globalobject()
+    def nameof_function(self, func):
+        assert self.rtyper is not None, (
+            "for now, rtyper must be specified to build a PyObject "
+            "wrapper for %r" % (func,))
+        # look for skipped functions
+        a = self.rtyper.annotator
+        if a.translator.frozen:
+            if func not in a.translator.flowgraphs:
+                return self.skipped_function(func)
+        else:
+            if (func.func_doc and
+                func.func_doc.lstrip().startswith('NOT_RPYTHON')):
+                return self.skipped_function(func)
+
+        from pypy.translator.c.wrapper import gen_wrapper
+        fwrapper = gen_wrapper(func, self.rtyper)
+        pycfunctionobj = self.uniquename('gfunc_' + func.__name__)
+        self.wrappers[pycfunctionobj] = func.__name__, self.getvalue(fwrapper)
+        return pycfunctionobj
 
     def nameof_staticmethod(self, sm):
         # XXX XXX XXXX

Modified: pypy/dist/pypy/translator/c/test/test_database.py
==============================================================================
--- pypy/dist/pypy/translator/c/test/test_database.py	(original)
+++ pypy/dist/pypy/translator/c/test/test_database.py	Sun May 29 16:20:58 2005
@@ -181,10 +181,15 @@
     db.complete()
     dump_on_stdout(db)
 
-def INPROGRESS_test_func_as_pyobject():
+def test_func_as_pyobject():
     def f(x):
         return x+1
-    db = LowLevelDatabase()
+    t = Translator(f)
+    a = t.annotate([int])
+    rtyper = RPythonTyper(t.annotator)
+    rtyper.specialize()
+
+    db = LowLevelDatabase(rtyper)
     db.get(pyobjectptr(f))
     db.complete()
     dump_on_stdout(db)

Modified: pypy/dist/pypy/translator/c/test/test_genc.py
==============================================================================
--- pypy/dist/pypy/translator/c/test/test_genc.py	(original)
+++ pypy/dist/pypy/translator/c/test/test_genc.py	Sun May 29 16:20:58 2005
@@ -1,5 +1,6 @@
-import autopath, sys, os
+import autopath, sys, os, py
 from pypy.rpython.lltype import *
+from pypy.rpython.rtyper import RPythonTyper
 from pypy.translator.translator import Translator
 from pypy.translator.c.database import LowLevelDatabase
 from pypy.translator.c.genc import gen_source
@@ -14,8 +15,9 @@
     modulename = uniquemodulename('testing')
     targetdir = udir.join(modulename).ensure(dir=1)
     gen_source(db, modulename, str(targetdir))
-    make_module_from_c(targetdir.join(modulename+'.c'),
-                       include_dirs = [os.path.dirname(autopath.this_dir)])
+    m = make_module_from_c(targetdir.join(modulename+'.c'),
+                           include_dirs = [os.path.dirname(autopath.this_dir)])
+    return m
 
 
 def test_untyped_func():
@@ -33,3 +35,26 @@
     db.get(s)
     db.complete()
     compile_db(db)
+
+
+def test_func_as_pyobject():
+    def f(x):
+        return x*2
+    t = Translator(f)
+    a = t.annotate([int])
+    rtyper = RPythonTyper(t.annotator)
+    rtyper.specialize()
+
+    db = LowLevelDatabase(rtyper)
+    entrypoint = db.get(pyobjectptr(f))
+    db.complete()
+    module = compile_db(db)
+
+    f1 = getattr(module, entrypoint)
+    assert f1(5) == 10
+    assert f1(x=5) == 10
+    assert f1(-123) == -246
+    py.test.raises(TypeError, f1, "world")  # check that it's really typed
+    py.test.raises(TypeError, f1)
+    py.test.raises(TypeError, f1, 2, 3)
+    py.test.raises(TypeError, f1, 2, x=2)

Added: pypy/dist/pypy/translator/c/wrapper.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/c/wrapper.py	Sun May 29 16:20:58 2005
@@ -0,0 +1,112 @@
+from pypy.objspace.flow.model import Variable, Constant, SpaceOperation
+from pypy.objspace.flow.model import Block, Link, FunctionGraph, checkgraph
+from pypy.annotation import model as annmodel
+from pypy.rpython.lltype import GcPtr, NonGcPtr, PyObject, typeOf, Signed, Void
+from pypy.rpython.lltype import FuncType, functionptr
+from pypy.rpython.rtyper import LowLevelOpList, inputconst
+from pypy.interpreter.pycode import CO_VARARGS
+
+
+def gen_wrapper(func, rtyper):
+    """generate a wrapper function for 'func' that can be put in a
+    PyCFunction object.  The wrapper has signature
+
+        PyObject *pyfn_xxx(PyObject *self, PyObject *args, PyObject* kw);
+    """
+    # The basic idea is to produce a flow graph from scratch, using the
+    # help of the rtyper for the conversion of the arguments after they
+    # have been decoded.
+    
+    # get the fully typed low-level pointer to the function, if available
+    nb_positional_args = func.func_code.co_argcount
+    vararg = bool(func.func_code.co_flags & CO_VARARGS)
+    f = rtyper.getfunctionptr(func)
+    FUNCTYPE = typeOf(f).TO
+    assert len(FUNCTYPE.ARGS) == nb_positional_args + vararg
+
+    newops = LowLevelOpList(rtyper)
+
+    # "def wrapper(self, args, kwds)"
+    vself = Variable('self')
+    vargs = Variable('args')
+    vkwds = Variable('kwds')
+    vfname = Constant(func.func_name)
+    # avoid incref/decref on the arguments: 'self' and 'kwds' can be NULL
+    vself.concretetype = NonGcPtr(PyObject)
+    vargs.concretetype = NonGcPtr(PyObject)
+    vkwds.concretetype = NonGcPtr(PyObject)
+
+    varguments = []
+    varnames = func.func_code.co_varnames
+    func_defaults = func.func_defaults or ()
+    for i in range(nb_positional_args):
+        # "argument_i = decode_arg(fname, i, name, vargs, vkwds)"  or
+        # "argument_i = decode_arg_def(fname, i, name, vargs, vkwds, default)"
+        vlist = [vfname,
+                 inputconst(Signed, i),
+                 Constant(varnames[i]),
+                 vargs,
+                 vkwds]
+        try:
+            default_value = func_defaults[i - nb_positional_args]
+        except IndexError:
+            opname = 'decode_arg'
+        else:
+            opname = 'decode_arg_def'
+            vlist.append(Constant(default_value))
+
+        v = newops.genop(opname, vlist, resulttype=GcPtr(PyObject))
+        v._name = 'a%d' % i
+        varguments.append(v)
+
+    if vararg:
+        # "vararg = vargs[n:]"
+        vlist = [vargs,
+                 Constant(nb_positional_args),
+                 Constant(None),
+                 ]
+        vararg = newops.genop('getslice', vlist, resulttype=GcPtr(PyObject))
+        vararg._name = 'vararg'
+        varguments.append(vararg)
+    else:
+        # "check_no_more_arg(fname, n, vargs)"
+        vlist = [vfname,
+                 inputconst(Signed, nb_positional_args),
+                 vargs,
+                 ]
+        newops.genop('check_no_more_arg', vlist)
+
+    # use the rtyper to produce the conversions
+    inputargs = f._obj.graph.getargs()
+    for i in range(len(varguments)):
+        # "argument_i = type_conversion_operations(argument_i)"
+        s_arg = rtyper.annotator.binding(inputargs[i], True)
+        if s_arg is not None:
+            varguments[i] = newops.convertvar(varguments[i],
+                                              s_from = annmodel.SomeObject(),
+                                                s_to = s_arg)
+
+    # "result = direct_call(func, argument_0, argument_1, ..)"
+    vlist = [inputconst(typeOf(f), f)] + varguments
+    vresult = newops.genop('direct_call', vlist, resulttype=FUNCTYPE.RESULT)
+
+    # convert "result" back to a PyObject
+    s_result = rtyper.annotator.binding(f._obj.graph.getreturnvar(), True)
+    if s_result is not None:
+        vresult = newops.convertvar(vresult,
+                                    s_from = s_result,
+                                      s_to = annmodel.SomeObject())
+
+    # "return result"
+    block = Block([vself, vargs, vkwds])
+    wgraph = FunctionGraph('pyfn_' + func.func_name, block)
+    block.operations[:] = newops
+    block.closeblock(Link([vresult], wgraph.returnblock))
+    checkgraph(wgraph)
+
+    return functionptr(FuncType([NonGcPtr(PyObject),
+                                 NonGcPtr(PyObject),
+                                 NonGcPtr(PyObject)],
+                                GcPtr(PyObject)),
+                       wgraph.name,
+                       graph = wgraph)



More information about the Pypy-commit mailing list