Author: Carl Friedrich Bolz-Tereick <cfb...@gmx.de>
Branch: py3.7-call-changes
Changeset: r98543:e00970e45d4b
Date: 2020-01-16 13:28 +0100
http://bitbucket.org/pypy/pypy/changeset/e00970e45d4b/

Log:    limit the maximum stack size when doing calls with huge arguments

        the _make_call method is slowly becoming a mess, should probably be
        refactored

diff --git a/pypy/interpreter/astcompiler/codegen.py 
b/pypy/interpreter/astcompiler/codegen.py
--- a/pypy/interpreter/astcompiler/codegen.py
+++ b/pypy/interpreter/astcompiler/codegen.py
@@ -1422,18 +1422,20 @@
         nsubkwargs = 0
         if args is not None:
             for elt in args:
-                if isinstance(elt, ast.Starred):
-                    # A star-arg. If we've seen positional arguments,
-                    # pack the positional arguments into a tuple.
+                if (isinstance(elt, ast.Starred) or
+                        nargs_pushed > MAX_STACKDEPTH_CONTAINERS // 2):
+                    # If we see a star-arg or if the number of arguments is
+                    # huge we pack the positional arguments into a tuple.
                     if nargs_pushed:
                         self.emit_op_arg(ops.BUILD_TUPLE, nargs_pushed)
                         nsubargs += 1
                         nargs_pushed = 0
-                    elt.value.walkabout(self)
-                    nsubargs += 1
-                else:
-                    elt.walkabout(self)
-                    nargs_pushed += 1
+                    if isinstance(elt, ast.Starred):
+                        elt.value.walkabout(self)
+                        nsubargs += 1
+                        continue
+                elt.walkabout(self)
+                nargs_pushed += 1
             if nsubargs:
                 if nargs_pushed:
                     # Pack up any trailing positional arguments.
@@ -1461,26 +1463,38 @@
                 for kw in keywords:
                     if kw.arg is None:
                         # we're using CALL_FUNCTION_EX, pack up positional 
arguments first
-                        self.emit_op_arg(ops.BUILD_TUPLE, nargs_pushed)  # XXX 
use LOAD_CONST for empty tuple?
+                        if nargs_pushed == 0:
+                            self._load_constant_tuple([])
+                        else:
+                            self.emit_op_arg(ops.BUILD_TUPLE, nargs_pushed)
                         nsubargs = 1
                         break
+                if nsubargs == 0 and len(keywords) > MAX_STACKDEPTH_CONTAINERS 
// 2:
+                    if nargs_pushed == 0:
+                        self._load_constant_tuple([])
+                    else:
+                        self.emit_op_arg(ops.BUILD_TUPLE, nargs_pushed)
+                    nsubargs = 1
+
             for kw in keywords:
                 assert isinstance(kw, ast.keyword)
-                if kw.arg is None:
+                if kw.arg is None or len(keyword_names_w) > 
MAX_STACKDEPTH_CONTAINERS // 2:
+                    # if we see **args or if the number of keywords is huge,
+                    # pack up keywords on the stack so far
                     if keyword_names_w:
-                        # pack up keywords on the stack so far
                         self._load_constant_tuple(keyword_names_w)
                         # XXX use BUILD_MAP for size 1?
                         self.emit_op_arg(ops.BUILD_CONST_KEY_MAP, 
len(keyword_names_w))
                         keyword_names_w = []
                         nsubkwargs += 1
-                    kw.value.walkabout(self)
-                    nsubkwargs += 1
-                else:
-                    w_name = space.newtext(kw.arg)
-                    keyword_names_w.append(misc.intern_if_common_string(space, 
w_name))
-                    kw.value.walkabout(self)
-                    nkw += 1
+                    if kw.arg is None:
+                        kw.value.walkabout(self)
+                        nsubkwargs += 1
+                        continue
+                w_name = space.newtext(kw.arg)
+                keyword_names_w.append(misc.intern_if_common_string(space, 
w_name))
+                kw.value.walkabout(self)
+                nkw += 1
         if nsubkwargs == 0 and nsubargs == 0:
             # can use CALL_FUNCTION_KW
             assert len(keyword_names_w) > 0 # otherwise we would have used 
CALL_FUNCTION
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py 
b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -1685,12 +1685,20 @@
         counts = self.count_instructions(source)
         assert counts[ops.CALL_FUNCTION_KW] == 1
 
-        source = """def f(): x(a, b, c, *(1, 2), x=1, y=2)"""
+        source = """def f(): x(a, b, c, *(d, 2), x=1, y=2)"""
         counts = self.count_instructions(source)
+        assert counts[ops.BUILD_TUPLE] == 2
+        assert counts[ops.BUILD_TUPLE_UNPACK] == 1
         assert counts[ops.CALL_FUNCTION_EX] == 1
 
         source = """def f(): x(a, b, c, **kwargs)"""
         counts = self.count_instructions(source)
+        assert counts[ops.BUILD_TUPLE] == 1
+        assert counts[ops.CALL_FUNCTION_EX] == 1
+
+        source = """def f(): x(**kwargs)"""
+        counts = self.count_instructions(source)
+        assert ops.BUILD_TUPLE not in counts # LOAD_CONST used instead
         assert counts[ops.CALL_FUNCTION_EX] == 1
 
         source = """def f(): x.m(a, b, c)"""
@@ -1742,3 +1750,12 @@
         source = "{" + ",".join(['%s: None' % (i, ) for i in range(200)]) + 
"}\n"
         w_res = self.run_and_check_stacksize(source)
         assert self.space.unwrap(w_res) == dict.fromkeys(range(200))
+
+    def test_callargs(self):
+        source = "(lambda *args: args)(" + ", ".join([str(i) for i in 
range(200)]) + ")\n"
+        w_res = self.run_and_check_stacksize(source)
+        assert self.space.unwrap(w_res) == tuple(range(200))
+
+        source = "(lambda **args: args)(" + ", ".join(["s%s=None" % i for i in 
range(200)]) + ")\n"
+        w_res = self.run_and_check_stacksize(source)
+        assert self.space.unwrap(w_res) == dict.fromkeys(["s" + str(i) for i 
in range(200)])
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to