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