[pypy-commit] pypy translation-cleanup: Allow const-folding of inner functions

rlamy noreply at buildbot.pypy.org
Fri Oct 5 19:02:21 CEST 2012


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: translation-cleanup
Changeset: r57798:92f1d72513e1
Date: 2012-10-05 06:29 +0100
http://bitbucket.org/pypy/pypy/changeset/92f1d72513e1/

Log:	Allow const-folding of inner functions

	* Implement FlowSpaceFrame.MAKE_FUNCTION
	* Add tests
	* Don't wrap code constants in HostCode

diff --git a/pypy/objspace/flow/bytecode.py b/pypy/objspace/flow/bytecode.py
--- a/pypy/objspace/flow/bytecode.py
+++ b/pypy/objspace/flow/bytecode.py
@@ -19,8 +19,7 @@
 
     def __init__(self, argcount, nlocals, stacksize, flags,
                      code, consts, names, varnames, filename,
-                     name, firstlineno, lnotab, freevars, cellvars,
-                     magic=cpython_magic):
+                     name, firstlineno, lnotab, freevars, cellvars):
         """Initialize a new code object"""
         self.co_name = name
         assert nlocals >= 0
@@ -69,19 +68,12 @@
     def _from_code(cls, code):
         """Initialize the code object from a real (CPython) one.
         """
-        newconsts = []
-        for const in code.co_consts:
-            if isinstance(const, CodeType):
-                const = cls._from_code(const)
-            newconsts.append(const)
-        # stick the underlying CPython magic value, if the code object
-        # comes from there
         return cls(code.co_argcount,
                       code.co_nlocals,
                       code.co_stacksize,
                       code.co_flags,
                       code.co_code,
-                      newconsts,
+                      list(code.co_consts),
                       list(code.co_names),
                       list(code.co_varnames),
                       code.co_filename,
@@ -89,8 +81,7 @@
                       code.co_firstlineno,
                       code.co_lnotab,
                       list(code.co_freevars),
-                      list(code.co_cellvars),
-                      cpython_magic)
+                      list(code.co_cellvars))
 
     @property
     def formalargcount(self):
diff --git a/pypy/objspace/flow/flowcontext.py b/pypy/objspace/flow/flowcontext.py
--- a/pypy/objspace/flow/flowcontext.py
+++ b/pypy/objspace/flow/flowcontext.py
@@ -676,6 +676,12 @@
         self.pushvalue(self.space.newlist([]))
         self.pushvalue(last_val)
 
+    def MAKE_FUNCTION(self, numdefaults, next_instr):
+        w_codeobj = self.popvalue()
+        defaults = self.popvalues(numdefaults)
+        fn = self.space.newfunction(w_codeobj, self.w_globals, defaults)
+        self.pushvalue(fn)
+
     # XXX Unimplemented 2.7 opcodes ----------------
 
     # Set literals, set comprehensions
diff --git a/pypy/objspace/flow/objspace.py b/pypy/objspace/flow/objspace.py
--- a/pypy/objspace/flow/objspace.py
+++ b/pypy/objspace/flow/objspace.py
@@ -98,6 +98,17 @@
         else:
             return self.w_False
 
+    def newfunction(self, w_code, w_globals, defaults_w):
+        try:
+            code = self.unwrap(w_code)
+            globals = self.unwrap(w_globals)
+            defaults = tuple([self.unwrap(value) for value in defaults_w])
+        except UnwrapException:
+            raise FlowingError(self.frame, "Dynamically created function must"
+                    " have constant default values.")
+        fn = types.FunctionType(code, globals, code.co_name, defaults)
+        return Constant(fn)
+
     def wrap(self, obj):
         if isinstance(obj, (Variable, Constant)):
             raise TypeError("already wrapped: " + repr(obj))
diff --git a/pypy/objspace/flow/test/test_objspace.py b/pypy/objspace/flow/test/test_objspace.py
--- a/pypy/objspace/flow/test/test_objspace.py
+++ b/pypy/objspace/flow/test/test_objspace.py
@@ -1063,6 +1063,32 @@
         assert len(graph.startblock.exits) == 1
         assert graph.startblock.exits[0].target == graph.returnblock
 
+    def test_lambda(self):
+        def f():
+            g = lambda m, n: n*m
+            return g
+        graph = self.codetest(f)
+        assert len(graph.startblock.exits) == 1
+        assert graph.startblock.exits[0].target == graph.returnblock
+        g = graph.startblock.exits[0].args[0].value
+        assert g(4, 4) == 16
+
+    def test_lambda_with_defaults(self):
+        def f():
+            g = lambda m, n=5: n*m
+            return g
+        graph = self.codetest(f)
+        assert len(graph.startblock.exits) == 1
+        assert graph.startblock.exits[0].target == graph.returnblock
+        g = graph.startblock.exits[0].args[0].value
+        assert g(4) == 20
+
+        def f2(x):
+            g = lambda m, n=x: n*m
+            return g
+        with py.test.raises(FlowingError):
+            self.codetest(f2)
+
 DATA = {'x': 5,
         'y': 6}
 


More information about the pypy-commit mailing list