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

tismer at codespeak.net tismer at codespeak.net
Sun Apr 23 03:50:29 CEST 2006


Author: tismer
Date: Sun Apr 23 03:50:26 2006
New Revision: 26154

Modified:
   pypy/dist/pypy/translator/c/pyobj.py
   pypy/dist/pypy/translator/c/test/test_wrapping.py
   pypy/dist/pypy/translator/c/wrapper.py
Log:
creating instances as a side effect of __init__ was much much harder than expected.
__init__ needs to be cloned for every subclass that doesn't have its own.
Suppress wrapping of __del__ because it is supported by RPython already.

Modified: pypy/dist/pypy/translator/c/pyobj.py
==============================================================================
--- pypy/dist/pypy/translator/c/pyobj.py	(original)
+++ pypy/dist/pypy/translator/c/pyobj.py	Sun Apr 23 03:50:26 2006
@@ -2,10 +2,10 @@
 import autopath, os, sys, __builtin__, marshal, zlib
 from types import FunctionType, CodeType, InstanceType, ClassType
 
-from pypy.objspace.flow.model import Variable, Constant
+from pypy.objspace.flow.model import Variable, Constant, FunctionGraph
 from pypy.translator.gensupp import builtin_base, builtin_type_base
 from pypy.translator.c.support import log
-from pypy.translator.c.wrapper import gen_wrapper
+from pypy.translator.c.wrapper import gen_wrapper, new_method_graph
 
 from pypy.rpython.rarithmetic import r_int, r_uint
 from pypy.rpython.lltypesystem.lltype import pyobjectptr, LowLevelType
@@ -55,6 +55,8 @@
     def computenameof(self, obj):
         obj_builtin_base = builtin_base(obj)
         if obj_builtin_base in (object, int, long) and type(obj) is not obj_builtin_base:
+            if isinstance(obj, FunctionGraph):
+                return self.nameof_graph(obj)
             # assume it's a user defined thingy
             return self.nameof_instance(obj)
         else:
@@ -209,7 +211,8 @@
         if self.shouldskipfunc(func):
             return self.skipped_function(func)
 
-        fwrapper = gen_wrapper(func, self.translator, self.name_for_meth.get(func, func.__name__))
+        fwrapper = gen_wrapper(func, self.translator,
+                               newname=self.name_for_meth.get(func, func.__name__))
         pycfunctionobj = self.uniquename('gfunc_' + func.__name__)
         self.wrappers[pycfunctionobj] = func.__name__, self.getvalue(fwrapper), func.__doc__
         return pycfunctionobj
@@ -371,7 +374,8 @@
             ignore = getattr(cls, 'NOT_RPYTHON_ATTRIBUTES', [])
             for key, value in content:
                 if key.startswith('__'):
-                    if key in ['__module__', '__doc__', '__dict__',
+                    # we do not expose __del__, because it would be called twice
+                    if key in ['__module__', '__doc__', '__dict__', '__del__',
                                '__weakref__', '__repr__', '__metaclass__']:
                         continue
                     # XXX some __NAMES__ are important... nicer solution sought
@@ -534,20 +538,46 @@
         metaclass = "type"
         name = self.uniquename('gwcls_' + cls.__name__)
         basenames = [self.nameof(base) for base in cls.__bases__]
+        # we merge the class dicts for more speed
+        def merge_classdicts(cls):
+            dic = {}
+            for cls in cls.mro()[:-1]:
+                for key, value in cls.__dict__.items():
+                    if key not in dic:
+                        dic[key] = value
+            return dic
         def initclassobj():
-            content = cls.__dict__.items()
+            content = merge_classdicts(cls).items()
             content.sort()
+            init_seen = False
             for key, value in content:
                 if key.startswith('__'):
-                    if key in ['__module__', '__dict__', '__doc__',
+                    # we do not expose __del__, because it would be called twice
+                    if key in ['__module__', '__dict__', '__doc__', '__del__',
                                '__weakref__', '__repr__', '__metaclass__']:
                         continue
                 if self.shouldskipfunc(value):
                     log.WARNING("skipped class function: %r" % value)
                     continue
-                if callable(value):
-                    self.name_for_meth[value] = '%s.%s' % (cls.__name__, value.__name__)
+                if isinstance(value, FunctionType):
+                    func = value
+                    fname = '%s.%s' % (cls.__name__, func.__name__)
+                    if func.__name__ == '__init__':
+                        init_seen = True
+                        # there is the problem with exposed classes inheriting from
+                        # classes which are internal. We need to create a new wrapper
+                        # for every class which uses an inherited __init__, because
+                        # this is the context where we create the instance.
+                        ann = self.translator.annotator
+                        clsdef = ann.bookkeeper.getuniqueclassdef(cls)
+                        graph = ann.bookkeeper.getdesc(func).cachedgraph(None)
+                        if ann.binding(graph.getargs()[0]).classdef is not clsdef:
+                            value = new_method_graph(graph, clsdef, fname, self.translator)
+                    self.name_for_meth[value] = fname
                 yield '%s.%s = %s' % (name, key, self.nameof(value))
+            if not init_seen:
+                log.WARNING('No __init__ found for %s - you cannot build instances' %
+                            cls.__name__)
 
         baseargs = ", ".join(basenames)
         if baseargs:
@@ -561,3 +591,10 @@
         a('    __slots__ = ["__self__"] # for PyCObject')
         self.later(initclassobj())
         return name
+
+    def nameof_graph(self, g):
+        newname=self.name_for_meth.get(g, g.func.__name__)
+        fwrapper = gen_wrapper(g, self.translator, newname=newname)
+        pycfunctionobj = self.uniquename('gfunc_' + newname)
+        self.wrappers[pycfunctionobj] = g.func.__name__, self.getvalue(fwrapper), g.func.__doc__
+        return pycfunctionobj

Modified: pypy/dist/pypy/translator/c/test/test_wrapping.py
==============================================================================
--- pypy/dist/pypy/translator/c/test/test_wrapping.py	(original)
+++ pypy/dist/pypy/translator/c/test/test_wrapping.py	Sun Apr 23 03:50:26 2006
@@ -7,7 +7,7 @@
 from pypy.rpython import robject, rclass
 from pypy.translator.tool.cbuild import enable_fast_compilation
 
-import sys
+import sys, types
 
 P = False  # debug printing
 
@@ -22,7 +22,7 @@
     missing = [object] * (func.func_code.co_argcount - len(argstypelist))
     return missing + argstypelist
 
-def get_compiled_module(func, view=conftest.option.view, inline_threshold=0*1,
+def get_compiled_module(func, view=conftest.option.view, inline_threshold=1,
                 use_boehm=False, exports=None):
     from pypy.translator.translator import TranslationContext
     from pypy.translator.backendopt.all import backend_optimizations
@@ -35,22 +35,20 @@
     t.buildannotator()
     rtyper = t.buildrtyper()
     bk = rtyper.annotator.bookkeeper
-    t.annotator.build_types(func, get_annotation(func))
     if not exports:
         exports = []
     all = [obj.__name__ for obj in exports]
     exports = exports + [('__all__', all)]
+
+    t.annotator.build_types(func, get_annotation(func))
+
     for obj in exports:
         if isinstance(obj, type):
             clsdef = bk.getuniqueclassdef(obj)
             rtyper.add_wrapper(clsdef)
-            # pull it all out
-            for name, value in obj.__dict__.items():
-                continue # do we want that
-                if callable(value):
-                    t.annotator.build_types(value, get_annotation(value))
-        elif callable(obj):
+        elif isinstance(obj, types.FunctionType):
             t.annotator.build_types(obj, get_annotation(obj))
+
     if view:
         t.viewcg()
     rtyper.specialize()
@@ -70,10 +68,10 @@
     # explicit build of database
     db = cbuilder.build_database(exports=exports)
     cbuilder.generate_source(db)
-    cbuilder.compile()
-
     if view:
         t.viewcg()
+    cbuilder.compile()
+
     return cbuilder.import_module()
 
 def getcompiled(func, *args, **kwds):
@@ -142,10 +140,6 @@
 delmonitor = DelMonitor()
 
 class DemoBaseNotExposed(object):
-    pass
-
-# a trivial class to be exposed
-class DemoClass(DemoBaseNotExposed):
     """this is the doc string"""
     def __init__(self, a, b):
         self.a = a
@@ -157,6 +151,9 @@
         if P:print 'demo'
         return self.a + self.b
 
+
+# a trivial class to be exposed
+class DemoClass(DemoBaseNotExposed):
     def demonotcalled(self):
         return self.demo() + 42
 
@@ -283,17 +280,19 @@
     assert res == DemoClass(2, 3).demo()
 
 def t(a=int, b=int, c=DemoClass):
-    DemoClass(a, b).demo()
+    x = DemoClass(a, b)
+    x.demo()
     DemoSubclass(a, a, b).demo()
     DemoSubclass(a, a, b).demo(6)
-    DemoSubclass(a, a, b).demo(6, 'hu')
+    y = DemoSubclass(a, a, b).demo(6, 'hu')
     if isinstance(c, DemoSubclass):
         print 42
-        
+    return DemoBaseNotExposed(17, 4) # see if it works without wrapper
+
 # exposing and using classes from a generasted extension module
 def test_asd():
     m = get_compiled_module(t, use_boehm=not True, exports=[
-        DemoClass, DemoSubclass, DemoNotAnnotated, setup_new_module])
+        DemoClass, DemoSubclass, DemoNotAnnotated])
     obj = m.DemoClass(2, 3)
     res = obj.demo()
     assert res == DemoClass(2, 3).demo()

Modified: pypy/dist/pypy/translator/c/wrapper.py
==============================================================================
--- pypy/dist/pypy/translator/c/wrapper.py	(original)
+++ pypy/dist/pypy/translator/c/wrapper.py	Sun Apr 23 03:50:26 2006
@@ -1,14 +1,17 @@
 from pypy.objspace.flow.model import Variable, Constant
 from pypy.objspace.flow.model import Block, Link, FunctionGraph, checkgraph
 from pypy.rpython.lltypesystem.lltype import \
-     Ptr, PyObject, typeOf, Signed, FuncType, functionptr
+     Ptr, PyObject, typeOf, Signed, FuncType, functionptr, nullptr, Void
 from pypy.rpython.rtyper import LowLevelOpList
 from pypy.rpython.rmodel import inputconst, PyObjPtr
 from pypy.rpython.robject import pyobj_repr
 from pypy.interpreter.pycode import CO_VARARGS
 
 from pypy.rpython.typesystem import getfunctionptr
+from pypy.annotation.model import s_None, SomeInstance
+from pypy.translator.backendopt.inline import simple_inline_function
 
+ALWAYS_INLINE = False
 
 def gen_wrapper(func, translator, newname=None):
     """generate a wrapper function for 'func' that can be put in a
@@ -21,20 +24,28 @@
     # 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)
 
+    do_inline = ALWAYS_INLINE
     if translator.annotator is None:
         # get the graph from the translator, "push it back" so that it's
         # still available for further buildflowgraph() calls
         graph = translator.buildflowgraph(func)
         translator._prebuilt_graphs[func] = graph
     else:
-        bk = translator.annotator.bookkeeper
-        graph = bk.getdesc(func).cachedgraph(None)
+        if isinstance(func, FunctionGraph):
+            graph = func
+            func = graph.func
+            # in this case we want to inline for sure, because we
+            # created this extra graph with a single call-site.
+            do_inline = True
+        else:
+            bk = translator.annotator.bookkeeper
+            graph = bk.getdesc(func).cachedgraph(None)
 
     f = getfunctionptr(graph)
     FUNCTYPE = typeOf(f).TO
+    nb_positional_args = func.func_code.co_argcount
+    vararg = bool(func.func_code.co_flags & CO_VARARGS)
     assert len(FUNCTYPE.ARGS) == nb_positional_args + vararg
 
     newops = LowLevelOpList(translator.rtyper)
@@ -133,6 +144,8 @@
         # that need to be specialized now
         translator.rtyper.specialize_more_blocks()
 
+    if do_inline:
+        simple_inline_function(translator, graph, wgraph)
     return functionptr(FuncType([PyObjPtr,
                                  PyObjPtr,
                                  PyObjPtr],
@@ -140,3 +153,46 @@
                        wgraph.name,
                        graph = wgraph,
                        exception_policy = "CPython")
+
+def new_method_graph(graph, clsdef, newname, translator):
+    ann = translator.annotator
+    rtyper = translator.rtyper
+
+    f = getfunctionptr(graph)
+    FUNCTYPE = typeOf(f).TO
+
+    newops = LowLevelOpList(translator.rtyper)
+
+    callargs = graph.getargs()[:]
+    v_self_old = callargs.pop(0)
+    v_self = Variable(v_self_old.name)
+    binding = SomeInstance(clsdef)
+    v_self.concretetype = rtyper.getrepr(binding).lowleveltype
+    ann.setbinding(v_self, binding)
+    v_self_call = newops.convertvar(v_self,
+                                  r_from = rtyper.bindingrepr(v_self),
+                                    r_to = rtyper.bindingrepr(v_self_old))
+
+    vlist = [inputconst(typeOf(f), f)] + [v_self_call] + callargs
+    newops.genop('direct_call', vlist, resulttype=Void)
+
+    # "return result"
+    funcargs = [v_self] + callargs
+    block = Block(funcargs)
+    newgraph = FunctionGraph(newname, block)
+    translator.update_call_graph(newgraph, graph, object())
+    translator.graphs.append(newgraph)
+    block.operations[:] = newops
+    block.closeblock(Link([inputconst(Void, None)], newgraph.returnblock))
+
+    vres = newgraph.getreturnvar()
+    ann.setbinding(vres, s_None)
+    checkgraph(newgraph)
+    # pretend to be the same function, as we actually
+    # will become inlined.
+    newgraph.func = graph.func
+    translator.rtyper.specialize_more_blocks()
+    # not sure if we want this all the time?
+    if ALWAYS_INLINE:
+        simple_inline_function(translator, graph, newgraph)
+    return newgraph



More information about the Pypy-commit mailing list