[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