Commit cfb2e090 authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #27213: Fixed different issues with reworked CALL_FUNCTION* opcodes.

* BUILD_TUPLE_UNPACK and BUILD_MAP_UNPACK_WITH_CALL no longer generated with
  single tuple or dict.
* Restored more informative error messages for incorrect var-positional and
  var-keyword arguments.
* Removed code duplications in _PyEval_EvalCodeWithName().
* Removed redundant runtime checks and parameters in _PyStack_AsDict().
* Added a workaround and enabled previously disabled test in test_traceback.
* Removed dead code from the dis module.
parent 90250c40
...@@ -275,9 +275,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ ...@@ -275,9 +275,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
PyAPI_FUNC(PyObject *) _PyStack_AsDict( PyAPI_FUNC(PyObject *) _PyStack_AsDict(
PyObject **values, PyObject **values,
Py_ssize_t nkwargs, PyObject *kwnames);
PyObject *kwnames,
PyObject *func);
/* Convert (args, nargs, kwargs) into a (stack, nargs, kwnames). /* Convert (args, nargs, kwargs) into a (stack, nargs, kwnames).
......
...@@ -314,8 +314,6 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None, ...@@ -314,8 +314,6 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None,
argrepr = argval argrepr = argval
elif op in hasfree: elif op in hasfree:
argval, argrepr = _get_name_info(arg, cells) argval, argrepr = _get_name_info(arg, cells)
elif op in hasnargs: # unused
argrepr = "%d positional, %d keyword pair" % (arg%256, arg//256)
yield Instruction(opname[op], op, yield Instruction(opname[op], op,
arg, argval, argrepr, arg, argval, argrepr,
offset, starts_line, is_jump_target) offset, starts_line, is_jump_target)
......
...@@ -118,7 +118,7 @@ Verify clearing of SF bug #733667 ...@@ -118,7 +118,7 @@ Verify clearing of SF bug #733667
>>> g(*Nothing()) >>> g(*Nothing())
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: 'Nothing' object is not iterable TypeError: g() argument after * must be an iterable, not Nothing
>>> class Nothing: >>> class Nothing:
... def __len__(self): return 5 ... def __len__(self): return 5
...@@ -127,7 +127,7 @@ Verify clearing of SF bug #733667 ...@@ -127,7 +127,7 @@ Verify clearing of SF bug #733667
>>> g(*Nothing()) >>> g(*Nothing())
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: 'Nothing' object is not iterable TypeError: g() argument after * must be an iterable, not Nothing
>>> class Nothing(): >>> class Nothing():
... def __len__(self): return 5 ... def __len__(self): return 5
...@@ -231,32 +231,34 @@ What about willful misconduct? ...@@ -231,32 +231,34 @@ What about willful misconduct?
>>> h(*h) >>> h(*h)
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: 'function' object is not iterable TypeError: h() argument after * must be an iterable, not function
>>> dir(*h) >>> dir(*h)
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: 'function' object is not iterable TypeError: dir() argument after * must be an iterable, not function
>>> None(*h) >>> None(*h)
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: 'function' object is not iterable TypeError: NoneType object argument after * must be an iterable, \
not function
>>> h(**h) >>> h(**h)
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: 'function' object is not a mapping TypeError: h() argument after ** must be a mapping, not function
>>> dir(**h) >>> dir(**h)
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: 'function' object is not a mapping TypeError: dir() argument after ** must be a mapping, not function
>>> None(**h) >>> None(**h)
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: 'function' object is not a mapping TypeError: NoneType object argument after ** must be a mapping, \
not function
>>> dir(b=1, **{'b': 1}) >>> dir(b=1, **{'b': 1})
Traceback (most recent call last): Traceback (most recent call last):
......
...@@ -304,7 +304,6 @@ class TracebackFormatTests(unittest.TestCase): ...@@ -304,7 +304,6 @@ class TracebackFormatTests(unittest.TestCase):
]) ])
# issue 26823 - Shrink recursive tracebacks # issue 26823 - Shrink recursive tracebacks
@unittest.skipIf(True, "FIXME: test broken, see issue #28050")
def _check_recursive_traceback_display(self, render_exc): def _check_recursive_traceback_display(self, render_exc):
# Always show full diffs when this test fails # Always show full diffs when this test fails
# Note that rearranging things may require adjusting # Note that rearranging things may require adjusting
...@@ -353,7 +352,7 @@ class TracebackFormatTests(unittest.TestCase): ...@@ -353,7 +352,7 @@ class TracebackFormatTests(unittest.TestCase):
# Check the recursion count is roughly as expected # Check the recursion count is roughly as expected
rec_limit = sys.getrecursionlimit() rec_limit = sys.getrecursionlimit()
self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-50, rec_limit)) self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit))
# Check a known (limited) number of recursive invocations # Check a known (limited) number of recursive invocations
def g(count=10): def g(count=10):
......
...@@ -2367,9 +2367,9 @@ _PyObject_Call_Prepend(PyObject *func, ...@@ -2367,9 +2367,9 @@ _PyObject_Call_Prepend(PyObject *func,
} }
PyObject * PyObject *
_PyStack_AsDict(PyObject **values, Py_ssize_t nkwargs, PyObject *kwnames, _PyStack_AsDict(PyObject **values, PyObject *kwnames)
PyObject *func)
{ {
Py_ssize_t nkwargs = PyTuple_GET_SIZE(kwnames);
PyObject *kwdict; PyObject *kwdict;
Py_ssize_t i; Py_ssize_t i;
...@@ -2378,24 +2378,12 @@ _PyStack_AsDict(PyObject **values, Py_ssize_t nkwargs, PyObject *kwnames, ...@@ -2378,24 +2378,12 @@ _PyStack_AsDict(PyObject **values, Py_ssize_t nkwargs, PyObject *kwnames,
return NULL; return NULL;
} }
for (i=0; i < nkwargs; i++) { for (i = 0; i < nkwargs; i++) {
int err;
PyObject *key = PyTuple_GET_ITEM(kwnames, i); PyObject *key = PyTuple_GET_ITEM(kwnames, i);
PyObject *value = *values++; PyObject *value = *values++;
assert(PyUnicode_CheckExact(key));
if (PyDict_GetItem(kwdict, key) != NULL) { assert(PyDict_GetItem(kwdict, key) == NULL);
PyErr_Format(PyExc_TypeError, if (PyDict_SetItem(kwdict, key, value)) {
"%.200s%s got multiple values "
"for keyword argument '%U'",
PyEval_GetFuncName(func),
PyEval_GetFuncDesc(func),
key);
Py_DECREF(kwdict);
return NULL;
}
err = PyDict_SetItem(kwdict, key, value);
if (err) {
Py_DECREF(kwdict); Py_DECREF(kwdict);
return NULL; return NULL;
} }
...@@ -2479,7 +2467,7 @@ _PyObject_FastCallKeywords(PyObject *func, PyObject **stack, Py_ssize_t nargs, ...@@ -2479,7 +2467,7 @@ _PyObject_FastCallKeywords(PyObject *func, PyObject **stack, Py_ssize_t nargs,
} }
if (nkwargs > 0) { if (nkwargs > 0) {
kwdict = _PyStack_AsDict(stack + nargs, nkwargs, kwnames, func); kwdict = _PyStack_AsDict(stack + nargs, kwnames);
if (kwdict == NULL) { if (kwdict == NULL) {
return NULL; return NULL;
} }
......
...@@ -279,7 +279,7 @@ _PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack, ...@@ -279,7 +279,7 @@ _PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack,
nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
if (nkwargs > 0) { if (nkwargs > 0) {
kwdict = _PyStack_AsDict(stack + nargs, nkwargs, kwnames, func); kwdict = _PyStack_AsDict(stack + nargs, kwnames);
if (kwdict == NULL) { if (kwdict == NULL) {
return NULL; return NULL;
} }
......
...@@ -2513,14 +2513,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) ...@@ -2513,14 +2513,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
TARGET(BUILD_LIST_UNPACK) { TARGET(BUILD_LIST_UNPACK) {
int convert_to_tuple = opcode == BUILD_TUPLE_UNPACK; int convert_to_tuple = opcode == BUILD_TUPLE_UNPACK;
Py_ssize_t i; Py_ssize_t i;
PyObject *sum; PyObject *sum = PyList_New(0);
PyObject *return_value; PyObject *return_value;
if (convert_to_tuple && oparg == 1 && PyTuple_CheckExact(TOP())) {
DISPATCH();
}
sum = PyList_New(0);
if (sum == NULL) if (sum == NULL)
goto error; goto error;
...@@ -2708,13 +2703,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) ...@@ -2708,13 +2703,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
TARGET(BUILD_MAP_UNPACK) { TARGET(BUILD_MAP_UNPACK) {
int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL; int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL;
Py_ssize_t i; Py_ssize_t i;
PyObject *sum; PyObject *sum = PyDict_New();
if (with_call && oparg == 1 && PyDict_CheckExact(TOP())) {
DISPATCH();
}
sum = PyDict_New();
if (sum == NULL) if (sum == NULL)
goto error; goto error;
...@@ -3290,11 +3279,53 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) ...@@ -3290,11 +3279,53 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
PCALL(PCALL_ALL); PCALL(PCALL_ALL);
if (oparg & 0x01) { if (oparg & 0x01) {
kwargs = POP(); kwargs = POP();
if (!PyDict_CheckExact(kwargs)) {
PyObject *d = PyDict_New();
if (d == NULL)
goto error;
if (PyDict_Update(d, kwargs) != 0) {
Py_DECREF(d);
/* PyDict_Update raises attribute
* error (percolated from an attempt
* to get 'keys' attribute) instead of
* a type error if its second argument
* is not a mapping.
*/
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
func = SECOND();
PyErr_Format(PyExc_TypeError,
"%.200s%.200s argument after ** "
"must be a mapping, not %.200s",
PyEval_GetFuncName(func),
PyEval_GetFuncDesc(func),
kwargs->ob_type->tp_name);
}
goto error;
}
Py_DECREF(kwargs);
kwargs = d;
}
assert(PyDict_CheckExact(kwargs)); assert(PyDict_CheckExact(kwargs));
} }
callargs = POP(); callargs = POP();
assert(PyTuple_CheckExact(callargs));
func = TOP(); func = TOP();
if (!PyTuple_Check(callargs)) {
if (Py_TYPE(callargs)->tp_iter == NULL &&
!PySequence_Check(callargs)) {
PyErr_Format(PyExc_TypeError,
"%.200s%.200s argument after * "
"must be an iterable, not %.200s",
PyEval_GetFuncName(func),
PyEval_GetFuncDesc(func),
callargs->ob_type->tp_name);
goto error;
}
Py_SETREF(callargs, PySequence_Tuple(callargs));
if (callargs == NULL) {
goto error;
}
}
assert(PyTuple_Check(callargs));
result = do_call_core(func, callargs, kwargs); result = do_call_core(func, callargs, kwargs);
Py_DECREF(func); Py_DECREF(func);
...@@ -3796,8 +3827,8 @@ too_many_positional(PyCodeObject *co, Py_ssize_t given, Py_ssize_t defcount, ...@@ -3796,8 +3827,8 @@ too_many_positional(PyCodeObject *co, Py_ssize_t given, Py_ssize_t defcount,
static PyObject * static PyObject *
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
PyObject **args, Py_ssize_t argcount, PyObject **args, Py_ssize_t argcount,
PyObject **kws, Py_ssize_t kwcount, PyObject **kwnames, PyObject **kwargs,
PyObject *kwnames, PyObject **kwstack, Py_ssize_t kwcount, int kwstep,
PyObject **defs, Py_ssize_t defcount, PyObject **defs, Py_ssize_t defcount,
PyObject *kwdefs, PyObject *closure, PyObject *kwdefs, PyObject *closure,
PyObject *name, PyObject *qualname) PyObject *name, PyObject *qualname)
...@@ -3811,9 +3842,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, ...@@ -3811,9 +3842,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount; const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount;
Py_ssize_t i, n; Py_ssize_t i, n;
PyObject *kwdict; PyObject *kwdict;
Py_ssize_t kwcount2 = kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames);
assert((kwcount == 0) || (kws != NULL));
if (globals == NULL) { if (globals == NULL) {
PyErr_SetString(PyExc_SystemError, PyErr_SetString(PyExc_SystemError,
...@@ -3873,11 +3901,12 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, ...@@ -3873,11 +3901,12 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
} }
} }
/* Handle keyword arguments passed as an array of (key, value) pairs */ /* Handle keyword arguments passed as two strided arrays */
for (i = 0; i < kwcount; i++) { kwcount *= kwstep;
for (i = 0; i < kwcount; i += kwstep) {
PyObject **co_varnames; PyObject **co_varnames;
PyObject *keyword = kws[2*i]; PyObject *keyword = kwnames[i];
PyObject *value = kws[2*i + 1]; PyObject *value = kwargs[i];
Py_ssize_t j; Py_ssize_t j;
if (keyword == NULL || !PyUnicode_Check(keyword)) { if (keyword == NULL || !PyUnicode_Check(keyword)) {
...@@ -3932,61 +3961,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, ...@@ -3932,61 +3961,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
SETLOCAL(j, value); SETLOCAL(j, value);
} }
/* Handle keyword arguments passed as keys tuple + values array */
for (i = 0; i < kwcount2; i++) {
PyObject **co_varnames;
PyObject *keyword = PyTuple_GET_ITEM(kwnames, i);
PyObject *value = kwstack[i];
int j;
if (keyword == NULL || !PyUnicode_Check(keyword)) {
PyErr_Format(PyExc_TypeError,
"%U() keywords must be strings",
co->co_name);
goto fail;
}
/* Speed hack: do raw pointer compares. As names are
normally interned this should almost always hit. */
co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item;
for (j = 0; j < total_args; j++) {
PyObject *nm = co_varnames[j];
if (nm == keyword)
goto kw_found2;
}
/* Slow fallback, just in case */
for (j = 0; j < total_args; j++) {
PyObject *nm = co_varnames[j];
int cmp = PyObject_RichCompareBool(
keyword, nm, Py_EQ);
if (cmp > 0)
goto kw_found2;
else if (cmp < 0)
goto fail;
}
if (j >= total_args && kwdict == NULL) {
PyErr_Format(PyExc_TypeError,
"%U() got an unexpected "
"keyword argument '%S'",
co->co_name,
keyword);
goto fail;
}
if (PyDict_SetItem(kwdict, keyword, value) == -1) {
goto fail;
}
continue;
kw_found2:
if (GETLOCAL(j) != NULL) {
PyErr_Format(PyExc_TypeError,
"%U() got multiple "
"values for argument '%S'",
co->co_name,
keyword);
goto fail;
}
Py_INCREF(value);
SETLOCAL(j, value);
}
/* Check the number of positional arguments */ /* Check the number of positional arguments */
if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) { if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) {
too_many_positional(co, argcount, defcount, fastlocals); too_many_positional(co, argcount, defcount, fastlocals);
...@@ -4138,8 +4112,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, ...@@ -4138,8 +4112,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
{ {
return _PyEval_EvalCodeWithName(_co, globals, locals, return _PyEval_EvalCodeWithName(_co, globals, locals,
args, argcount, args, argcount,
kws, kwcount, kws, kws + 1, kwcount, 2,
NULL, NULL,
defs, defcount, defs, defcount,
kwdefs, closure, kwdefs, closure,
NULL, NULL); NULL, NULL);
...@@ -4923,8 +4896,9 @@ fast_function(PyObject *func, PyObject **stack, ...@@ -4923,8 +4896,9 @@ fast_function(PyObject *func, PyObject **stack,
} }
return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL,
stack, nargs, stack, nargs,
NULL, 0, nkwargs ? &PyTuple_GET_ITEM(kwnames, 0) : NULL,
kwnames, stack + nargs, stack + nargs,
nkwargs, 1,
d, (int)nd, kwdefs, d, (int)nd, kwdefs,
closure, name, qualname); closure, name, qualname);
} }
...@@ -5014,8 +4988,7 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, ...@@ -5014,8 +4988,7 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs,
result = _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, result = _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL,
args, nargs, args, nargs,
k, nk, k, k + 1, nk, 2,
NULL, NULL,
d, nd, kwdefs, d, nd, kwdefs,
closure, name, qualname); closure, name, qualname);
Py_XDECREF(kwtuple); Py_XDECREF(kwtuple);
......
...@@ -3503,7 +3503,7 @@ compiler_call_helper(struct compiler *c, ...@@ -3503,7 +3503,7 @@ compiler_call_helper(struct compiler *c,
asdl_seq *keywords) asdl_seq *keywords)
{ {
Py_ssize_t i, nseen, nelts, nkwelts; Py_ssize_t i, nseen, nelts, nkwelts;
int musttupleunpack = 0, mustdictunpack = 0; int mustdictunpack = 0;
/* the number of tuples and dictionaries on the stack */ /* the number of tuples and dictionaries on the stack */
Py_ssize_t nsubargs = 0, nsubkwargs = 0; Py_ssize_t nsubargs = 0, nsubkwargs = 0;
...@@ -3532,7 +3532,6 @@ compiler_call_helper(struct compiler *c, ...@@ -3532,7 +3532,6 @@ compiler_call_helper(struct compiler *c,
} }
VISIT(c, expr, elt->v.Starred.value); VISIT(c, expr, elt->v.Starred.value);
nsubargs++; nsubargs++;
musttupleunpack = 1;
} }
else { else {
VISIT(c, expr, elt); VISIT(c, expr, elt);
...@@ -3541,13 +3540,13 @@ compiler_call_helper(struct compiler *c, ...@@ -3541,13 +3540,13 @@ compiler_call_helper(struct compiler *c,
} }
/* Same dance again for keyword arguments */ /* Same dance again for keyword arguments */
if (musttupleunpack || mustdictunpack) { if (nsubargs || mustdictunpack) {
if (nseen) { if (nseen) {
/* Pack up any trailing positional arguments. */ /* Pack up any trailing positional arguments. */
ADDOP_I(c, BUILD_TUPLE, nseen); ADDOP_I(c, BUILD_TUPLE, nseen);
nsubargs++; nsubargs++;
} }
if (musttupleunpack || nsubargs > 1) { if (nsubargs > 1) {
/* If we ended up with more than one stararg, we need /* If we ended up with more than one stararg, we need
to concatenate them into a single sequence. */ to concatenate them into a single sequence. */
ADDOP_I(c, BUILD_TUPLE_UNPACK, nsubargs); ADDOP_I(c, BUILD_TUPLE_UNPACK, nsubargs);
...@@ -3579,7 +3578,7 @@ compiler_call_helper(struct compiler *c, ...@@ -3579,7 +3578,7 @@ compiler_call_helper(struct compiler *c,
return 0; return 0;
nsubkwargs++; nsubkwargs++;
} }
if (mustdictunpack || nsubkwargs > 1) { if (nsubkwargs > 1) {
/* Pack it all up */ /* Pack it all up */
ADDOP_I(c, BUILD_MAP_UNPACK_WITH_CALL, nsubkwargs); ADDOP_I(c, BUILD_MAP_UNPACK_WITH_CALL, nsubkwargs);
} }
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment