]> granicus.if.org Git - python/commitdiff
Issue #21523: Fix over-pessimistic computation of the stack effect of some opcodes...
authorAntoine Pitrou <solipsis@pitrou.net>
Fri, 23 May 2014 09:46:03 +0000 (11:46 +0200)
committerAntoine Pitrou <solipsis@pitrou.net>
Fri, 23 May 2014 09:46:03 +0000 (11:46 +0200)
This also fixes a quadratic compilation time issue noticeable when compiling
code with a large number of "and" and "or" operators.

Lib/test/test_compile.py
Misc/NEWS
Python/compile.c

index ccd08db66703e17ee93f78875913c85dc4a54cf2..b1f55bb0403d27a2248ee64c90300531e5056a03 100644 (file)
@@ -1,3 +1,4 @@
+import math
 import unittest
 import sys
 import _ast
@@ -501,8 +502,43 @@ if 1:
         check_limit("a", "*a")
 
 
-def test_main():
-    support.run_unittest(TestSpecifics)
+class TestStackSize(unittest.TestCase):
+    # These tests check that the computed stack size for a code object
+    # stays within reasonable bounds (see issue #21523 for an example
+    # dysfunction).
+    N = 100
+
+    def check_stack_size(self, code):
+        # To assert that the alleged stack size is not O(N), we
+        # check that it is smaller than log(N).
+        if isinstance(code, str):
+            code = compile(code, "<foo>", "single")
+        max_size = math.ceil(math.log(len(code.co_code)))
+        self.assertLessEqual(code.co_stacksize, max_size)
+
+    def test_and(self):
+        self.check_stack_size("x and " * self.N + "x")
+
+    def test_or(self):
+        self.check_stack_size("x or " * self.N + "x")
+
+    def test_and_or(self):
+        self.check_stack_size("x and x or " * self.N + "x")
+
+    def test_chained_comparison(self):
+        self.check_stack_size("x < " * self.N + "x")
+
+    def test_if_else(self):
+        self.check_stack_size("x if x else " * self.N + "x")
+
+    def test_binop(self):
+        self.check_stack_size("x + " * self.N + "x")
+
+    def test_func_and(self):
+        code = "def f(x):\n"
+        code += "   x and x\n" * self.N
+        self.check_stack_size(code)
+
 
 if __name__ == "__main__":
-    test_main()
+    unittest.main()
index 4b746096fcd5458fcdd16cb8b721f5d0e531dd97..c1033868581962c4d35a2c5a649c0f72b9d93acf 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -2,6 +2,20 @@
 Python News
 +++++++++++
 
+What's New in Python 3.4.2?
+===========================
+
+Release date: XXXX-XX-XX
+
+Core and Builtins
+-----------------
+
+- Issue #21523: Fix over-pessimistic computation of the stack effect of
+  some opcodes in the compiler.  This also fixes a quadratic compilation
+  time issue noticeable when compiling code with a large number of "and"
+  and "or" operators.
+
+
 What's New in Python 3.4.1?
 ===========================
 
index 9978eb3a1df5ff07e1ba1509663a8a1e28351bb7..69419ecddc47a94fd0cbc3b05c357202f526eae1 100644 (file)
@@ -3856,12 +3856,16 @@ stackdepth_walk(struct compiler *c, basicblock *b, int depth, int maxdepth)
             target_depth = depth;
             if (instr->i_opcode == FOR_ITER) {
                 target_depth = depth-2;
-            } else if (instr->i_opcode == SETUP_FINALLY ||
-                       instr->i_opcode == SETUP_EXCEPT) {
+            }
+            else if (instr->i_opcode == SETUP_FINALLY ||
+                     instr->i_opcode == SETUP_EXCEPT) {
                 target_depth = depth+3;
                 if (target_depth > maxdepth)
                     maxdepth = target_depth;
             }
+            else if (instr->i_opcode == JUMP_IF_TRUE_OR_POP ||
+                     instr->i_opcode == JUMP_IF_FALSE_OR_POP)
+                depth = depth - 1;
             maxdepth = stackdepth_walk(c, instr->i_target,
                                        target_depth, maxdepth);
             if (instr->i_opcode == JUMP_ABSOLUTE ||