Author: Carl Friedrich Bolz-Tereick <[email protected]>
Branch: py3.7
Changeset: r98552:f20ec1e0843b
Date: 2020-01-17 21:31 +0100
http://bitbucket.org/pypy/pypy/changeset/f20ec1e0843b/
Log: merge py3.7-call-changes
implement more faithfully the bytecodes that CPython 3.7 uses for
calling functions
diff --git a/lib-python/3/opcode.py b/lib-python/3/opcode.py
--- a/lib-python/3/opcode.py
+++ b/lib-python/3/opcode.py
@@ -31,12 +31,10 @@
haslocal = []
hascompare = []
hasfree = []
-hasnargs = []
+hasnargs = [] # unused
opmap = {}
-opname = [''] * 256
-for op in range(256): opname[op] = '<%r>' % (op,)
-del op
+opname = ['<%r>' % (op,) for op in range(256)]
def def_op(name, op):
opname[op] = name
@@ -174,11 +172,10 @@
name_op('STORE_ANNOTATION', 127) # Index in name list XXX: removed in CPython
3.7
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
-def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8)
-hasnargs.append(131)
-def_op('MAKE_FUNCTION', 132) # Number of args with default values
+def_op('CALL_FUNCTION', 131) # #args
+
+def_op('MAKE_FUNCTION', 132) # Flags
def_op('BUILD_SLICE', 133) # Number of items
-def_op('MAKE_CLOSURE', 134)
def_op('LOAD_CLOSURE', 135)
hasfree.append(135)
def_op('LOAD_DEREF', 136)
@@ -188,12 +185,9 @@
def_op('DELETE_DEREF', 138)
hasfree.append(138)
-def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8)
-hasnargs.append(140)
-def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8)
-hasnargs.append(141)
-def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8)
-hasnargs.append(142)
+def_op('CALL_FUNCTION_KW', 141) # #args + #kwargs
+
+def_op('CALL_FUNCTION_EX', 142) # Flags
jrel_op('SETUP_WITH', 143)
@@ -204,7 +198,6 @@
def_op('LOAD_CLASSDEREF', 148)
hasfree.append(148)
-jrel_op('SETUP_ASYNC_WITH', 154)
def_op('EXTENDED_ARG', 144)
EXTENDED_ARG = 144
@@ -215,18 +208,20 @@
def_op('BUILD_TUPLE_UNPACK', 152)
def_op('BUILD_SET_UNPACK', 153)
-def_op('FORMAT_VALUE', 155) # in CPython 3.6, but available in PyPy from 3.5
+jrel_op('SETUP_ASYNC_WITH', 154)
+
+def_op('FORMAT_VALUE', 155)
def_op('BUILD_CONST_KEY_MAP', 156)
-def_op('BUILD_STRING', 157) # in CPython 3.6, but available in PyPy from 3.5
+def_op('BUILD_STRING', 157)
#name_op('LOAD_METHOD', 160)
-#def_op('CALL_METHOD', 161)
+def_op('CALL_METHOD', 161)
# pypy modification, experimental bytecode
def_op('LOOKUP_METHOD', 201) # Index in name list
hasname.append(201)
-def_op('CALL_METHOD', 202) # #args not including 'self'
def_op('BUILD_LIST_FROM_ARG', 203)
+def_op('CALL_METHOD_KW', 204)
def_op('LOAD_REVDB_VAR', 205) # reverse debugger (syntax example: $5)
del def_op, name_op, jrel_op, jabs_op
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -16,3 +16,8 @@
.. branch: bpo-16055
Fixes incorrect error text for ``int('1', base=1000)``
+
+.. branch py3.7-call-changes
+
+Implement the CPython 3.7 changes to the call bytecodes, including supporting
+more than 255 arguments.
diff --git a/pypy/interpreter/astcompiler/assemble.py
b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -768,9 +768,6 @@
def _compute_BUILD_MAP_UNPACK_WITH_CALL(arg):
return 1 - (arg & 0xFF)
-def _compute_MAKE_CLOSURE(arg):
- return -2 - _num_args(arg) - ((arg >> 16) & 0xFFFF)
-
def _compute_MAKE_FUNCTION(arg):
return -1 - bool(arg & 0x01) - bool(arg & 0x02) - bool(arg & 0x04) -
bool(arg & 0x08)
@@ -783,23 +780,22 @@
def _compute_RAISE_VARARGS(arg):
return -arg
-def _num_args(oparg):
- return (oparg % 256) + 2 * ((oparg // 256) % 256)
-
def _compute_CALL_FUNCTION(arg):
- return -_num_args(arg)
-
-def _compute_CALL_FUNCTION_VAR(arg):
- return -_num_args(arg) - 1
+ return -arg
def _compute_CALL_FUNCTION_KW(arg):
- return -_num_args(arg) - 1
+ return -arg - 1
-def _compute_CALL_FUNCTION_VAR_KW(arg):
- return -_num_args(arg) - 2
+def _compute_CALL_FUNCTION_EX(arg):
+ assert arg == 0 or arg == 1
+ # either -1 or -2
+ return -arg - 1
def _compute_CALL_METHOD(arg):
- return -_num_args(arg) - 1
+ return -arg - 1
+
+def _compute_CALL_METHOD_KW(arg):
+ return -arg - 2
def _compute_FORMAT_VALUE(arg):
if (arg & consts.FVS_MASK) == consts.FVS_HAVE_SPEC:
diff --git a/pypy/interpreter/astcompiler/astbuilder.py
b/pypy/interpreter/astcompiler/astbuilder.py
--- a/pypy/interpreter/astcompiler/astbuilder.py
+++ b/pypy/interpreter/astcompiler/astbuilder.py
@@ -598,8 +598,6 @@
kwdefaults = []
kwarg = None
vararg = None
- if n_pos + n_kwdonly > 255:
- self.error("more than 255 arguments", arguments_node)
# process args
i = 0
have_default = False
@@ -1113,8 +1111,6 @@
(generator_count and (keyword_count or arg_count)):
self.error("Generator expression must be parenthesized "
"if not sole argument", args_node)
- if arg_count + keyword_count + generator_count > 255:
- self.error("more than 255 arguments", args_node)
args = []
keywords = []
used_keywords = {}
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
@@ -1412,99 +1412,20 @@
self.load_const(self.space.newtext(keyword.arg))
keyword.value.walkabout(self)
- def _make_call(self, n, # args already pushed
- args, keywords):
- call_type = 0
- # the number of tuples and dictionaries on the stack
- nsubargs = 0
- nsubkwargs = 0
- nkw = 0
- nseen = 0 # the number of positional arguments on the stack
- 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 nseen:
- self.emit_op_arg(ops.BUILD_TUPLE, nseen)
- nseen = 0
- nsubargs += 1
- elt.value.walkabout(self)
- nsubargs += 1
- elif nsubargs:
- # We've seen star-args already, so we
- # count towards items-to-pack-into-tuple.
- elt.walkabout(self)
- nseen += 1
- else:
- # Positional arguments before star-arguments
- # are left on the stack.
- elt.walkabout(self)
- n += 1
- if nseen:
- # Pack up any trailing positional arguments.
- self.emit_op_arg(ops.BUILD_TUPLE, nseen)
- nsubargs += 1
- if nsubargs:
- call_type |= 1
- if nsubargs > 1:
- # If we ended up with more than one stararg, we need
- # to concatenate them into a single sequence.
- self.emit_op_arg(ops.BUILD_LIST_UNPACK, nsubargs)
+ def _load_constant_tuple(self, content_w):
+ self.load_const(self.space.newtuple(content_w[:]))
- # Repeat procedure for keyword args
- nseen = 0 # the number of keyword arguments on the stack following
- if keywords is not None:
- for kw in keywords:
- assert isinstance(kw, ast.keyword)
- if kw.arg is None:
- # A keyword argument unpacking.
- if nseen:
- self.emit_op_arg(ops.BUILD_MAP, nseen)
- nseen = 0
- nsubkwargs += 1
- kw.value.walkabout(self)
- nsubkwargs += 1
- elif nsubkwargs:
- # A keyword argument and we already have a dict.
- self.load_const(self.space.newtext(kw.arg))
- kw.value.walkabout(self)
- nseen += 1
- else:
- # keyword argument
- kw.walkabout(self)
- nkw += 1
- if nseen:
- # Pack up any trailing keyword arguments.
- self.emit_op_arg(ops.BUILD_MAP,nseen)
- nsubkwargs += 1
- if nsubkwargs:
- call_type |= 2
- if nsubkwargs > 1:
- # Pack it all up
- function_pos = n + (call_type & 1) + nkw + 1
- self.emit_op_arg(ops.BUILD_MAP_UNPACK_WITH_CALL,
(nsubkwargs | (function_pos << 8)))
-
- assert n < 1<<8
- assert nkw < 1<<24
- n |= nkw << 8;
-
- op = 0
- if call_type == 0:
- op = ops.CALL_FUNCTION
- elif call_type == 1:
- op = ops.CALL_FUNCTION_VAR
- elif call_type == 2:
- op = ops.CALL_FUNCTION_KW
- elif call_type == 3:
- op = ops.CALL_FUNCTION_VAR_KW
- self.emit_op_arg(op, n)
+ def _make_call(self, nargs_pushed, args, keywords):
+ space = self.space
+ CallCodeGenerator(self, nargs_pushed, args, keywords).emit_call()
def visit_Call(self, call):
self.update_position(call.lineno)
if self._optimize_method_call(call):
return
call.func.walkabout(self)
+ #if getattr(call.func, "id", None) == "f":
+ # import pdb; pdb.set_trace()
self._make_call(0, call.args, call.keywords)
def _call_has_no_star_args(self, call):
@@ -1523,6 +1444,7 @@
return self._call_has_no_star_args(call) and not call.keywords
def _optimize_method_call(self, call):
+ space = self.space
if not self._call_has_no_star_args(call) or \
not isinstance(call.func, ast.Attribute):
return False
@@ -1532,9 +1454,18 @@
self.emit_op_name(ops.LOOKUP_METHOD, self.names, attr_lookup.attr)
self.visit_sequence(call.args)
arg_count = len(call.args) if call.args is not None else 0
- self.visit_sequence(call.keywords)
- kwarg_count = len(call.keywords) if call.keywords is not None else 0
- self.emit_op_arg(ops.CALL_METHOD, (kwarg_count << 8) | arg_count)
+ if not call.keywords:
+ self.emit_op_arg(ops.CALL_METHOD, arg_count)
+ else:
+ keyword_names_w = []
+ for kw in call.keywords:
+ assert isinstance(kw, ast.keyword)
+ assert kw.arg # checked by self._call_has_no_star_args
+ w_name = space.newtext(kw.arg)
+ keyword_names_w.append(misc.intern_if_common_string(space,
w_name))
+ kw.value.walkabout(self)
+ self._load_constant_tuple(keyword_names_w)
+ self.emit_op_arg(ops.CALL_METHOD_KW, len(keyword_names_w) +
arg_count)
return True
def visit_ListComp(self, lc):
@@ -1946,3 +1877,127 @@
if self.scope.doc_removable:
flags |= consts.CO_KILL_DOCSTRING
return PythonCodeGenerator._get_code_flags(self) | flags
+
+
+class CallCodeGenerator(object):
+ def __init__(self, codegenerator, nargs_pushed, args, keywords):
+ self.space = codegenerator.space
+ self.codegenerator = codegenerator
+ self.nargs_pushed = nargs_pushed
+ self.args = args
+ self.keywords = keywords
+
+ # the number of tuples and dictionaries on the stack
+ self.nsubargs = 0
+ self.nsubkwargs = 0
+ self.keyword_names_w = []
+
+ def _pack_positional_into_tuple(self):
+ if self.nargs_pushed:
+ self.codegenerator.emit_op_arg(ops.BUILD_TUPLE, self.nargs_pushed)
+ self.nsubargs += 1
+ self.nargs_pushed = 0
+
+ def _push_args(self):
+ for elt in self.args:
+ if isinstance(elt, ast.Starred):
+ # we have a *arg
+ self._pack_positional_into_tuple()
+ elt.value.walkabout(self.codegenerator)
+ self.nsubargs += 1
+ continue
+ if self.nargs_pushed >= MAX_STACKDEPTH_CONTAINERS // 2:
+ # stack depth getting too big
+ self._pack_positional_into_tuple()
+ elt.walkabout(self.codegenerator)
+ self.nargs_pushed += 1
+ if self.nsubargs:
+ # Pack up any trailing positional arguments.
+ self._pack_positional_into_tuple()
+ if self.nsubargs > 1:
+ # If we ended up with more than one stararg, we need
+ # to concatenate them into a single sequence.
+ # XXX CPython uses BUILD_TUPLE_UNPACK_WITH_CALL, but I
+ # don't quite see the difference?
+ self.codegenerator.emit_op_arg(ops.BUILD_TUPLE_UNPACK,
self.nsubargs)
+
+ def _pack_kwargs_into_dict(self):
+ if self.keyword_names_w:
+ self.codegenerator._load_constant_tuple(self.keyword_names_w)
+ # XXX use BUILD_MAP for size 1?
+ self.codegenerator.emit_op_arg(ops.BUILD_CONST_KEY_MAP,
len(self.keyword_names_w))
+ self.keyword_names_w = []
+ self.nsubkwargs += 1
+
+ def _push_kwargs(self):
+ for kw in self.keywords:
+ assert isinstance(kw, ast.keyword)
+ if kw.arg is None:
+ # if we see **args or if the number of keywords is huge,
+ # pack up keywords on the stack so far
+ self._pack_kwargs_into_dict()
+ kw.value.walkabout(self.codegenerator)
+ self.nsubkwargs += 1
+ continue
+ if len(self.keyword_names_w) > MAX_STACKDEPTH_CONTAINERS // 2:
+ self._pack_kwargs_into_dict()
+ w_name = self.space.newtext(kw.arg)
+
self.keyword_names_w.append(misc.intern_if_common_string(self.space, w_name))
+ kw.value.walkabout(self.codegenerator)
+ if self.nsubkwargs:
+ self._pack_kwargs_into_dict()
+ if self.nsubkwargs > 1:
+ # Pack it all up
+ self.codegenerator.emit_op_arg(ops.BUILD_MAP_UNPACK_WITH_CALL,
self.nsubkwargs)
+
+ def _pack_positional_args_into_tuple(self):
+ if self.nargs_pushed == 0:
+ self.codegenerator._load_constant_tuple([])
+ else:
+ self.codegenerator.emit_op_arg(ops.BUILD_TUPLE, self.nargs_pushed)
+ self.nsubargs += 1
+
+ def _push_tuple_positional_args_if_necessary(self):
+ if self.nsubargs:
+ # can't use CALL_FUNCTION_KW anyway, because we already have a
+ # tuple as the positional args
+ return
+ # we might get away with using CALL_FUNCTION_KW if there are no
**kwargs
+ for kw in self.keywords:
+ assert isinstance(kw, ast.keyword)
+ if kw.arg is None:
+ # we found a **kwarg, thus we're using CALL_FUNCTION_EX, we
+ # need to pack up positional arguments first
+ self._pack_positional_args_into_tuple()
+ break
+ if self.nsubargs == 0 and len(self.keywords) >
MAX_STACKDEPTH_CONTAINERS // 2:
+ # we have a huge amount of keyword args, thus we also need to use
+ # CALL_FUNCTION_EX
+ self._pack_positional_args_into_tuple()
+
+ def emit_call(self):
+ keywords = self.keywords
+ codegenerator = self.codegenerator
+ space = self.space
+ if self.args is not None:
+ self._push_args()
+
+ # Repeat procedure for keyword args
+ if keywords is None or len(keywords) == 0:
+ if not self.nsubargs:
+ # no *args, no keyword args, no **kwargs
+ codegenerator.emit_op_arg(ops.CALL_FUNCTION, self.nargs_pushed)
+ return
+ else:
+ self._push_tuple_positional_args_if_necessary()
+ self._push_kwargs()
+
+ if self.nsubkwargs == 0 and self.nsubargs == 0:
+ # can use CALL_FUNCTION_KW
+ assert len(self.keyword_names_w) > 0 # otherwise we would have
used CALL_FUNCTION
+ codegenerator._load_constant_tuple(self.keyword_names_w)
+ codegenerator.emit_op_arg(ops.CALL_FUNCTION_KW, self.nargs_pushed
+ len(self.keyword_names_w))
+ else:
+ self._pack_kwargs_into_dict()
+ codegenerator.emit_op_arg(ops.CALL_FUNCTION_EX,
int(self.nsubkwargs > 0))
+
diff --git a/pypy/interpreter/astcompiler/test/test_astbuilder.py
b/pypy/interpreter/astcompiler/test/test_astbuilder.py
--- a/pypy/interpreter/astcompiler/test/test_astbuilder.py
+++ b/pypy/interpreter/astcompiler/test/test_astbuilder.py
@@ -570,13 +570,13 @@
for i in range(255):
fundef += "i%d, "%i
fundef += "*, key=100):\n pass\n"
- pytest.raises(SyntaxError, self.get_first_stmt, fundef)
+ self.get_first_stmt(fundef) # no crash, works since 3.7
fundef2 = "def foo(i,*,"
for i in range(255):
fundef2 += "i%d, "%i
fundef2 += "lastarg):\n pass\n"
- pytest.raises(SyntaxError, self.get_first_stmt, fundef)
+ self.get_first_stmt(fundef2) # no crash, works since 3.7
fundef3 = "def f(i,*,"
for i in range(253):
@@ -1075,8 +1075,7 @@
"sole argument"
many_args = ", ".join("x%i" % i for i in range(256))
input = "f(%s)" % (many_args,)
- exc = pytest.raises(SyntaxError, self.get_ast, input).value
- assert exc.msg == "more than 255 arguments"
+ self.get_ast(input) # doesn't crash any more
exc = pytest.raises(SyntaxError, self.get_ast, "f((a+b)=c)").value
assert exc.msg == "keyword can't be an expression"
exc = pytest.raises(SyntaxError, self.get_ast, "f(a=c, a=d)").value
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
@@ -264,6 +264,57 @@
yield self.st, decl + "x=f(5, b=2, **{'a': 8})", "x", [5, ('a', 8),
('b', 2)]
+ def test_funccalls_all_combinations(self):
+ decl = """
+def f(*args, **kwds):
+ kwds = sorted(kwds.items())
+ return list(args) + kwds
+
+class A:
+ def f(self, *args, **kwds):
+ kwds = sorted(kwds.items())
+ return ["meth"] + list(args) + kwds
+a = A()
+"""
+ allres = []
+ allcalls = []
+ for meth in [False, True]:
+ for starstarargs in [
+ [],
+ [[('x', 1), ('y', 12)]],
+ [[('w1', 1), ('w2', 12)], [('x1', 1), ('x2', -12)],
[('y1', 10), ('y2', 123)]]
+ ]:
+ for starargs in [[], [(2, 3)], [(2, 3), (4, 19), (23, 54,
123)]]:
+ for kwargs in [[], [('m', 1)], [('n', 1), ('o', 2), ('p',
3)]]:
+ for args in [(), (1, ), (1, 4, 5)]:
+ if not meth:
+ call = "f("
+ res = []
+ else:
+ call = "a.f("
+ res = ["meth"]
+ if args:
+ call += ", ".join(str(arg) for arg in args) +
","
+ res.extend(args)
+ if starargs:
+ for stararg in starargs:
+ call += "*" + str(stararg) + ","
+ res.extend(stararg)
+ if kwargs:
+ call += ", ".join("%s=%s" % (kw, arg) for (kw,
arg) in kwargs) + ", "
+ res.extend(kwargs)
+ if starstarargs:
+ for starstar in starstarargs:
+ call += "**dict(%s)" % starstar + ","
+ res.extend(sum(starstarargs, []))
+ call += ")"
+ allcalls.append(call)
+ allres.append(res)
+ print call
+ print res
+ self.st(decl + "x=[" + "\n,".join(allcalls) + "]", "x", allres)
+
+
def test_kwonly(self):
decl = py.code.Source("""
def f(a, *, b):
@@ -1276,6 +1327,24 @@
x = [y for (x, y) in dis.findlinestarts(co)]
""", 'x', [4]
+ def test_many_args(self):
+ args = ["a%i" % i for i in range(300)]
+ argdef = ", ".join(args)
+ res = "+".join(args)
+ callargs = ", ".join(str(i) for i in range(300))
+
+ source1 = """def f(%s):
+ return %s
+x = f(%s)
+ """ % (argdef, res, callargs)
+ source2 = """def f(%s):
+ return %s
+x = f(*(%s))
+ """ % (argdef, res, callargs)
+
+ yield self.simple_test, source1, 'x', sum(range(300))
+ yield self.simple_test, source2, 'x', sum(range(300))
+
class TestCompilerRevDB(BaseTestCompiler):
spaceconfig = {"translation.reverse_debugger": True}
@@ -1611,6 +1680,39 @@
counts = self.count_instructions(source)
assert counts[ops.BUILD_TUPLE] == 1
+ def test_call_bytecodes(self):
+ # check that the expected bytecodes are generated
+ source = """def f(): x(a, b, c)"""
+ counts = self.count_instructions(source)
+ assert counts[ops.CALL_FUNCTION] == 1
+
+ source = """def f(): x(a, b, c, x=1, y=2)"""
+ counts = self.count_instructions(source)
+ assert counts[ops.CALL_FUNCTION_KW] == 1
+
+ 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)"""
+ counts = self.count_instructions(source)
+ assert counts[ops.CALL_METHOD] == 1
+
+ source = """def f(): x.m(a, b, c, y=1)"""
+ counts = self.count_instructions(source)
+ assert counts[ops.CALL_METHOD_KW] == 1
class TestHugeStackDepths:
def run_and_check_stacksize(self, source):
@@ -1653,3 +1755,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)])
diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py
--- a/pypy/interpreter/pycode.py
+++ b/pypy/interpreter/pycode.py
@@ -38,7 +38,7 @@
# time you make pyc files incompatible. This value ends up in the frozen
# importlib, via MAGIC_NUMBER in module/_frozen_importlib/__init__.
-pypy_incremental_magic = 208 # bump it by 16
+pypy_incremental_magic = 224 # bump it by 16
assert pypy_incremental_magic % 16 == 0
assert pypy_incremental_magic < 3000 # the magic number of Python 3. There are
# no known magic numbers below this value
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -504,6 +504,7 @@
""" Returns 'funcname()' from either a function name fnname or a
wrapped callable w_function. If it's not a function or a method,
returns
'Classname object'"""
+ # XXX this is super annoying to compute every time we do a function
call!
# CPython has a similar function, PyEval_GetFuncName
from pypy.interpreter.function import Function, Method
if fnname is not None:
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -273,12 +273,12 @@
self.CALL_FUNCTION(oparg, next_instr)
elif opcode == opcodedesc.CALL_FUNCTION_KW.index:
self.CALL_FUNCTION_KW(oparg, next_instr)
- elif opcode == opcodedesc.CALL_FUNCTION_VAR.index:
- self.CALL_FUNCTION_VAR(oparg, next_instr)
- elif opcode == opcodedesc.CALL_FUNCTION_VAR_KW.index:
- self.CALL_FUNCTION_VAR_KW(oparg, next_instr)
+ elif opcode == opcodedesc.CALL_FUNCTION_EX.index:
+ self.CALL_FUNCTION_EX(oparg, next_instr)
elif opcode == opcodedesc.CALL_METHOD.index:
self.CALL_METHOD(oparg, next_instr)
+ elif opcode == opcodedesc.CALL_METHOD_KW.index:
+ self.CALL_METHOD_KW(oparg, next_instr)
elif opcode == opcodedesc.COMPARE_OP.index:
self.COMPARE_OP(oparg, next_instr)
elif opcode == opcodedesc.DELETE_ATTR.index:
@@ -1339,31 +1339,54 @@
self.pushvalue(w_result)
def CALL_FUNCTION(self, oparg, next_instr):
- # XXX start of hack for performance
- if (oparg >> 8) & 0xff == 0:
- # Only positional arguments
- nargs = oparg & 0xff
- w_function = self.peekvalue(nargs)
- try:
- w_result = self.space.call_valuestack(w_function, nargs, self)
- finally:
- self.dropvalues(nargs + 1)
- self.pushvalue(w_result)
- # XXX end of hack for performance
+ # Only positional arguments
+ nargs = oparg & 0xff
+ w_function = self.peekvalue(nargs)
+ try:
+ w_result = self.space.call_valuestack(w_function, nargs, self)
+ finally:
+ self.dropvalues(nargs + 1)
+ self.pushvalue(w_result)
+
+ @jit.unroll_safe
+ def CALL_FUNCTION_KW(self, n_arguments, next_instr):
+ w_tup_varnames = self.popvalue()
+ keywords_w = self.space.fixedview(w_tup_varnames)
+ n_keywords = len(keywords_w)
+ n_arguments -= n_keywords
+ keywords = [self.space.text_w(w_keyword) for w_keyword in keywords_w]
+ keywords_w = [None] * n_keywords
+ while True:
+ n_keywords -= 1
+ if n_keywords < 0:
+ break
+ w_value = self.popvalue()
+ keywords_w[n_keywords] = w_value
+ arguments = self.popvalues(n_arguments)
+ w_function = self.popvalue()
+ args = self.argument_factory(arguments, keywords, keywords_w, None,
None,
+ w_function=w_function)
+ if self.get_is_being_profiled() and
function.is_builtin_code(w_function):
+ w_result = self.space.call_args_and_c_profile(self, w_function,
+ args)
else:
- # general case
- self.call_function(oparg)
+ w_result = self.space.call_args(w_function, args)
+ self.pushvalue(w_result)
- def CALL_FUNCTION_VAR(self, oparg, next_instr):
- self.call_function(oparg, has_vararg=True)
-
- def CALL_FUNCTION_KW(self, oparg, next_instr):
- w_varkw = self.popvalue()
- self.call_function(oparg, w_varkw)
-
- def CALL_FUNCTION_VAR_KW(self, oparg, next_instr):
- w_varkw = self.popvalue()
- self.call_function(oparg, w_varkw, has_vararg=True)
+ def CALL_FUNCTION_EX(self, has_kwarg, next_instr):
+ w_kwargs = None
+ if has_kwarg:
+ w_kwargs = self.popvalue()
+ w_args = self.popvalue()
+ w_function = self.popvalue()
+ args = self.argument_factory(
+ [], None, None, w_star=w_args, w_starstar=w_kwargs,
w_function=w_function)
+ if self.get_is_being_profiled() and
function.is_builtin_code(w_function):
+ w_result = self.space.call_args_and_c_profile(self, w_function,
+ args)
+ else:
+ w_result = self.space.call_args(w_function, args)
+ self.pushvalue(w_result)
@jit.unroll_safe
def MAKE_FUNCTION(self, oparg, next_instr):
@@ -1436,6 +1459,7 @@
# overridden by faster version in the standard object space.
LOOKUP_METHOD = LOAD_ATTR
CALL_METHOD = CALL_FUNCTION
+ CALL_METHOD_KW = CALL_FUNCTION_KW
def MISSING_OPCODE(self, oparg, next_instr):
ofs = self.last_instr
@@ -1489,30 +1513,29 @@
self.pushvalue(w_set)
@jit.unroll_safe
- def list_unpack_helper(frame, itemcount):
- space = frame.space
- w_sum = space.newlist([], sizehint=itemcount)
- for i in range(itemcount, 0, -1):
- w_item = frame.peekvalue(i-1)
- w_sum.extend(w_item)
- frame.popvalues(itemcount)
- return w_sum
+ def BUILD_TUPLE_UNPACK(self, itemcount, next_instr):
+ l = []
+ for i in range(itemcount-1, -1, -1):
+ w_item = self.peekvalue(i)
+ l.extend(self.space.fixedview(w_item))
+ self.popvalues(itemcount)
+ self.pushvalue(self.space.newtuple(l[:]))
@jit.unroll_safe
- def BUILD_TUPLE_UNPACK(self, itemcount, next_instr):
- w_list = self.list_unpack_helper(itemcount)
- items = [w_obj for w_obj in w_list.getitems_unroll()]
- self.pushvalue(self.space.newtuple(items))
-
def BUILD_LIST_UNPACK(self, itemcount, next_instr):
- w_sum = self.list_unpack_helper(itemcount)
+ space = self.space
+ w_sum = space.newlist([], sizehint=itemcount)
+ for i in range(itemcount-1, -1, -1):
+ w_item = self.peekvalue(i)
+ w_sum.extend(w_item)
+ self.popvalues(itemcount)
self.pushvalue(w_sum)
def BUILD_MAP_UNPACK(self, itemcount, next_instr):
self._build_map_unpack(itemcount, with_call=False)
def BUILD_MAP_UNPACK_WITH_CALL(self, oparg, next_instr):
- num_maps = oparg & 0xff
+ num_maps = oparg # XXX CPython generates better error messages
self._build_map_unpack(num_maps, with_call=True)
@jit.unroll_safe
diff --git a/pypy/objspace/std/callmethod.py b/pypy/objspace/std/callmethod.py
--- a/pypy/objspace/std/callmethod.py
+++ b/pypy/objspace/std/callmethod.py
@@ -85,42 +85,47 @@
@jit.unroll_safe
def CALL_METHOD(f, oparg, *ignored):
# opargs contains the arg, and kwarg count, excluding the implicit 'self'
- n_args = oparg & 0xff
- n_kwargs = (oparg >> 8) & 0xff
- w_self = f.peekvalue_maybe_none(n_args + (2 * n_kwargs))
+ n_args = oparg
+ w_self = f.peekvalue_maybe_none(n_args)
n = n_args + (w_self is not None)
- if not n_kwargs:
- w_callable = f.peekvalue(n_args + (2 * n_kwargs) + 1)
- try:
- w_result = f.space.call_valuestack(
- w_callable, n, f, methodcall=w_self is not None)
- finally:
- f.dropvalues(n_args + 2)
+ w_callable = f.peekvalue(n_args + 1)
+ try:
+ w_result = f.space.call_valuestack(
+ w_callable, n, f, methodcall=w_self is not None)
+ finally:
+ f.dropvalues(n_args + 2)
+ f.pushvalue(w_result)
+
[email protected]_safe
+def CALL_METHOD_KW(f, n_arguments, *ignored):
+ # opargs contains the arg + kwarg count, excluding the implicit 'self'
+ w_self = f.peekvalue_maybe_none(n_arguments + 1)
+ w_tup_varnames = f.popvalue()
+ keywords_w = f.space.fixedview(w_tup_varnames)
+ n_keywords = len(keywords_w)
+ n_arguments -= n_keywords
+ n = n_arguments + (w_self is not None)
+ keywords = [f.space.text_w(w_keyword) for w_keyword in keywords_w]
+ keywords_w = [None] * n_keywords
+ while True:
+ n_keywords -= 1
+ if n_keywords < 0:
+ break
+ w_value = f.popvalue()
+ keywords_w[n_keywords] = w_value
+
+ arguments = f.popvalues(n) # includes w_self if it is not None
+ if w_self is None:
+ f.popvalue_maybe_none() # removes w_self, which is None
+ w_callable = f.popvalue()
+ args = f.argument_factory(
+ arguments, keywords, keywords_w, None, None,
+ methodcall=w_self is not None, w_function=w_callable)
+ if f.get_is_being_profiled() and function.is_builtin_code(w_callable):
+ w_result = f.space.call_args_and_c_profile(f, w_callable, args)
else:
- keywords = [None] * n_kwargs
- keywords_w = [None] * n_kwargs
- while True:
- n_kwargs -= 1
- if n_kwargs < 0:
- break
- w_value = f.popvalue()
- w_key = f.popvalue()
- key = f.space.text_w(w_key)
- keywords[n_kwargs] = key
- keywords_w[n_kwargs] = w_value
-
- arguments = f.popvalues(n) # includes w_self if it is not None
- if w_self is None:
- f.popvalue_maybe_none() # removes w_self, which is None
- w_callable = f.popvalue()
- args = f.argument_factory(
- arguments, keywords, keywords_w, None, None,
- methodcall=w_self is not None, w_function=w_callable)
- if f.get_is_being_profiled() and function.is_builtin_code(w_callable):
- w_result = f.space.call_args_and_c_profile(f, w_callable, args)
- else:
- w_result = f.space.call_args(w_callable, args)
+ w_result = f.space.call_args(w_callable, args)
f.pushvalue(w_result)
diff --git a/pypy/objspace/std/frame.py b/pypy/objspace/std/frame.py
--- a/pypy/objspace/std/frame.py
+++ b/pypy/objspace/std/frame.py
@@ -85,7 +85,8 @@
StdObjSpaceFrame.INPLACE_SUBTRACT = int_INPLACE_SUBTRACT
if space.config.objspace.std.optimized_list_getitem:
StdObjSpaceFrame.BINARY_SUBSCR = list_BINARY_SUBSCR
- from pypy.objspace.std.callmethod import LOOKUP_METHOD, CALL_METHOD
+ from pypy.objspace.std.callmethod import LOOKUP_METHOD, CALL_METHOD,
CALL_METHOD_KW
StdObjSpaceFrame.LOOKUP_METHOD = LOOKUP_METHOD
StdObjSpaceFrame.CALL_METHOD = CALL_METHOD
+ StdObjSpaceFrame.CALL_METHOD_KW = CALL_METHOD_KW
return StdObjSpaceFrame
diff --git a/pypy/tool/opcode3.py b/pypy/tool/opcode3.py
--- a/pypy/tool/opcode3.py
+++ b/pypy/tool/opcode3.py
@@ -39,6 +39,7 @@
del op
def def_op(name, op):
+ assert op not in opname
opname[op] = name
opmap[name] = op
@@ -173,11 +174,10 @@
haslocal.append(126)
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
-def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8)
+def_op('CALL_FUNCTION', 131) # #args
hasnargs.append(131)
def_op('MAKE_FUNCTION', 132) # Number of args with default values
def_op('BUILD_SLICE', 133) # Number of items
-def_op('MAKE_CLOSURE', 134)
def_op('LOAD_CLOSURE', 135)
hasfree.append(135)
def_op('LOAD_DEREF', 136)
@@ -187,12 +187,9 @@
def_op('DELETE_DEREF', 138)
hasfree.append(138)
-def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8)
-hasnargs.append(140)
-def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8)
+def_op('CALL_FUNCTION_KW', 141) # #args + #kwargs
hasnargs.append(141)
-def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8)
-hasnargs.append(142)
+def_op('CALL_FUNCTION_EX', 142) # Flags
jrel_op('SETUP_WITH', 143)
@@ -223,6 +220,7 @@
hasname.append(201)
def_op('CALL_METHOD', 202) # #args not including 'self'
def_op('BUILD_LIST_FROM_ARG', 203)
+def_op('CALL_METHOD_KW', 204)
name_op('LOAD_METHOD', 160)
def_op('CALL_METHOD', 161)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit