Author: Carl Friedrich Bolz-Tereick <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit