[pypy-commit] pypy sepcomp2: Add support for passing RPython instances between modules.

amauryfa noreply at buildbot.pypy.org
Wed Feb 22 00:34:14 CET 2012


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: sepcomp2
Changeset: r52749:162e6879b761
Date: 2012-02-22 00:33 +0100
http://bitbucket.org/pypy/pypy/changeset/162e6879b761/

Log:	Add support for passing RPython instances between modules. The
	constructor is also exported.

	FIXME: I had to disable a check in the ExceptionTransformer, there
	are probably objects that we should clean up somehow.

diff --git a/pypy/translator/c/exportinfo.py b/pypy/translator/c/exportinfo.py
--- a/pypy/translator/c/exportinfo.py
+++ b/pypy/translator/c/exportinfo.py
@@ -1,7 +1,13 @@
 from pypy.annotation import model, description
 from pypy.rpython.typesystem import getfunctionptr
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rpython.controllerentry import (
+    Controller, ControllerEntry, SomeControlledInstance)
+from pypy.rpython.extregistry import ExtRegistryEntry
+from pypy.rlib.objectmodel import instantiate
+from pypy.rlib.unroll import unrolling_iterable
+from pypy.tool.sourcetools import func_with_new_name
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
-from pypy.rpython.lltypesystem import lltype, rffi
 import py
 import sys
 import types
@@ -45,6 +51,52 @@
         return func
 
 
+class ClassExportInfo:
+    def __init__(self, name, cls):
+        self.name = name
+        self.cls = cls
+
+    def make_constructor(self):
+        self.constructor_name = "__new__%s" % (self.name,)
+        nbargs = len(self.cls.__init__.argtypes)
+        args = ', '.join(['arg%d' % d for d in range(nbargs)])
+        source = py.code.Source(r"""
+            def %s(%s):
+                obj = instantiate(cls)
+                obj.__init__(%s)
+                return obj
+            """ % (self.constructor_name, args, args))
+        miniglobals = {'cls': self.cls, 'instantiate': instantiate}
+        exec source.compile() in miniglobals
+        constructor = miniglobals[self.constructor_name]
+        constructor._annspecialcase_ = 'specialize:ll'
+        constructor._always_inline_ = True
+        constructor.argtypes = self.cls.__init__.argtypes
+        return constructor
+        
+    def make_repr(self, module, rtyper):
+        """Returns the class repr, but also installs a Controller that
+        will intercept all operations on the class."""
+        bookkeeper = rtyper.annotator.bookkeeper
+        classdef = bookkeeper.getuniqueclassdef(self.cls)
+        classrepr = rtyper.getrepr(model.SomeInstance(classdef)).lowleveltype
+        STRUCTPTR = classrepr
+
+        constructor = getattr(module, self.constructor_name)
+
+        class ClassController(Controller):
+            knowntype = STRUCTPTR
+
+            def new(self, *args):
+                return constructor(*args)
+
+        class Entry(ControllerEntry):
+            _about_ = STRUCTPTR
+            _controller_ = ClassController
+
+        return STRUCTPTR
+
+
 class ModuleExportInfo:
     """Translates and builds a library, and returns an 'import Module'
     which can be used in another translation.
@@ -54,17 +106,27 @@
     """
     def __init__(self):
         self.functions = {}
+        self.classes = {}
 
     def add_function(self, name, func):
         """Adds a function to export."""
         self.functions[name] = func
 
+    def add_class(self, name, cls):
+        """Adds a class to export."""
+        self.classes[name] = ClassExportInfo(name, cls)
+
     def annotate(self, annotator):
         """Annotate all exported functions."""
         bk = annotator.bookkeeper
 
+        # annotate constructors of exported classes
+        for name, class_info in self.classes.items():
+            constructor = class_info.make_constructor()
+            self.functions[constructor.__name__] = constructor
+
         # annotate functions with signatures
-        for funcname, func in self.functions.items():
+        for name, func in self.functions.items():
             if hasattr(func, 'argtypes'):
                 annotator.build_types(func, func.argtypes,
                                       complete_now=False)
@@ -136,13 +198,54 @@
             import_name = node_names[funcname]
             func = make_llexternal_function(import_name, funcptr, import_eci)
             setattr(mod, funcname, func)
+        for clsname, class_info in self.classes.items():
+            structptr = class_info.make_repr(mod, rtyper)
+            setattr(mod, clsname, structptr)
+            
         return mod
 
+def make_ll_import_arg_converter(TARGET):
+    from pypy.annotation import model
+
+    def convert(x):
+        UNUSED
+
+    class Entry(ExtRegistryEntry):
+        _about_ = convert
+
+        def compute_result_annotation(self, s_arg):
+            if not (isinstance(s_arg, SomeControlledInstance) and
+                    s_arg.s_real_obj.ll_ptrtype == TARGET):
+                raise TypeError("Expected a proxy for %s" % (TARGET,))
+            return model.lltype_to_annotation(TARGET)
+
+        def specialize_call(self, hop):
+            [v_instance] = hop.inputargs(*hop.args_r)
+            return hop.genop('force_cast', [v_instance],
+                             resulttype=TARGET)
+
+    return convert
+make_ll_import_arg_converter._annspecialcase_ = 'specialize:memo'
+
 def make_llexternal_function(name, funcptr, eci):
     functype = lltype.typeOf(funcptr)
     imported_func = rffi.llexternal(
         name, functype.TO.ARGS, functype.TO.RESULT,
         compilation_info=eci,
         )
-    return imported_func
+    ARGS = functype.TO.ARGS
+    unrolling_ARGS = unrolling_iterable(enumerate(ARGS))
+    def wrapper(*args):
+        real_args = ()
+        for i, TARGET in unrolling_ARGS:
+            arg = args[i]
+            if isinstance(TARGET, lltype.Ptr): # XXX more precise check?
+                arg = make_ll_import_arg_converter(TARGET)(arg)
 
+            real_args = real_args + (arg,)
+        res = imported_func(*real_args)
+        return res
+    wrapper._annspecialcase_ = 'specialize:ll'
+    wrapper._always_inline_ = True
+    return func_with_new_name(wrapper, name)
+
diff --git a/pypy/translator/c/src/g_include.h b/pypy/translator/c/src/g_include.h
--- a/pypy/translator/c/src/g_include.h
+++ b/pypy/translator/c/src/g_include.h
@@ -50,8 +50,11 @@
 #  include "src/ll_strtod.h"
 #endif
 
+#ifndef PYPY_CPYTHON_EXTENSION
+#  include "src/allocator.h"
+#endif
+
 #ifdef PYPY_STANDALONE
-#  include "src/allocator.h"
 #  include "src/main.h"
 #endif
 
diff --git a/pypy/translator/c/src/g_prerequisite.h b/pypy/translator/c/src/g_prerequisite.h
--- a/pypy/translator/c/src/g_prerequisite.h
+++ b/pypy/translator/c/src/g_prerequisite.h
@@ -4,6 +4,7 @@
 
 
 #ifdef PYPY_STANDALONE
+//#ifndef PYPY_CPYTHON_EXTENSION
 #  include "src/commondefs.h"
 #endif
 
diff --git a/pypy/translator/c/test/test_export.py b/pypy/translator/c/test/test_export.py
--- a/pypy/translator/c/test/test_export.py
+++ b/pypy/translator/c/test/test_export.py
@@ -2,7 +2,9 @@
 from pypy.translator.c.exportinfo import export, ModuleExportInfo
 from pypy.translator.c.dlltool import CLibraryBuilder
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
+from pypy.translator.backendopt.all import backend_optimizations
 import sys
+import types
 
 class TestExportFunctions:
     def setup_method(self, method):
@@ -14,12 +16,16 @@
         modulename += self.module_suffix
         export_info = ModuleExportInfo()
         for name, obj in exports.items():
-            export_info.add_function(name, obj)
+            if isinstance(obj, (type, types.ClassType)):
+                export_info.add_class(name, obj)
+            else:
+                export_info.add_function(name, obj)
 
         t = TranslationContext()
         t.buildannotator()
         export_info.annotate(t.annotator)
         t.buildrtyper().specialize()
+        backend_optimizations(t)
 
         functions = [(f, None) for f in export_info.functions.values()]
         builder = CLibraryBuilder(t, None, config=t.config,
@@ -71,3 +77,27 @@
 
         assert secondmodule.g() == 42.5
 
+    def test_pass_structure(self):
+        class Struct:
+            @export(float)
+            def __init__(self, x):
+                self.x = x + 27.4
+        @export(Struct, Struct, int)
+        def f(s, t, v):
+            return s.x + t.x + v
+        firstmodule = self.compile_module("first", f=f, S=Struct)
+        
+        S = firstmodule.S
+        @export()
+        def g():
+            s = S(3.0)
+            t = S(5.5)
+            return firstmodule.f(s, t, 7)
+        secondmodule = self.compile_module("second", g=g)
+        assert secondmodule.g() == 70.3
+
+        @export()
+        def g2():
+            # Bad argument type, should not translate
+            return firstmodule.f(1, 2, 3)
+        raises(TypeError, self.compile_module, "third", g2=g2)
diff --git a/pypy/translator/exceptiontransform.py b/pypy/translator/exceptiontransform.py
--- a/pypy/translator/exceptiontransform.py
+++ b/pypy/translator/exceptiontransform.py
@@ -194,7 +194,8 @@
         from the current graph with a special value (False/-1/-1.0/null).
         Because of the added exitswitch we need an additional block.
         """
-        if hasattr(graph, 'exceptiontransformed'):
+        # FIXME: Why do we have a graph with an old ExceptionTransform info?
+        if 0 and hasattr(graph, 'exceptiontransformed'):
             assert self.same_obj(self.exc_data_ptr, graph.exceptiontransformed)
             return
         else:


More information about the pypy-commit mailing list