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*/
PyAPI_FUNC(PyObject *) _PyStack_AsDict(
PyObject **values,
Py_ssize_t nkwargs,
PyObject *kwnames,
PyObject *func);
PyObject *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,
argrepr = argval
elif op in hasfree:
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,
arg, argval, argrepr,
offset, starts_line, is_jump_target)
......
......@@ -118,7 +118,7 @@ Verify clearing of SF bug #733667
>>> g(*Nothing())
Traceback (most recent call last):
...
TypeError: 'Nothing' object is not iterable
TypeError: g() argument after * must be an iterable, not Nothing
>>> class Nothing:
... def __len__(self): return 5
......@@ -127,7 +127,7 @@ Verify clearing of SF bug #733667
>>> g(*Nothing())
Traceback (most recent call last):
...
TypeError: 'Nothing' object is not iterable
TypeError: g() argument after * must be an iterable, not Nothing
>>> class Nothing():
... def __len__(self): return 5
......@@ -231,32 +231,34 @@ What about willful misconduct?
>>> h(*h)
Traceback (most recent call last):
...
TypeError: 'function' object is not iterable
TypeError: h() argument after * must be an iterable, not function
>>> dir(*h)
Traceback (most recent call last):
...
TypeError: 'function' object is not iterable
TypeError: dir() argument after * must be an iterable, not function
>>> None(*h)
Traceback (most recent call last):
...
TypeError: 'function' object is not iterable
TypeError: NoneType object argument after * must be an iterable, \
not function
>>> h(**h)
Traceback (most recent call last):
...
TypeError: 'function' object is not a mapping
TypeError: h() argument after ** must be a mapping, not function
>>> dir(**h)
Traceback (most recent call last):
...
TypeError: 'function' object is not a mapping
TypeError: dir() argument after ** must be a mapping, not function
>>> None(**h)
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})
Traceback (most recent call last):
......
......@@ -304,7 +304,6 @@ class TracebackFormatTests(unittest.TestCase):
])
# issue 26823 - Shrink recursive tracebacks
@unittest.skipIf(True, "FIXME: test broken, see issue #28050")
def _check_recursive_traceback_display(self, render_exc):
# Always show full diffs when this test fails
# Note that rearranging things may require adjusting
......@@ -353,7 +352,7 @@ class TracebackFormatTests(unittest.TestCase):
# Check the recursion count is roughly as expected
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
def g(count=10):
......
......@@ -2367,9 +2367,9 @@ _PyObject_Call_Prepend(PyObject *func,
}
PyObject *
_PyStack_AsDict(PyObject **values, Py_ssize_t nkwargs, PyObject *kwnames,
PyObject *func)
_PyStack_AsDict(PyObject **values, PyObject *kwnames)
{
Py_ssize_t nkwargs = PyTuple_GET_SIZE(kwnames);
PyObject *kwdict;
Py_ssize_t i;
......@@ -2378,24 +2378,12 @@ _PyStack_AsDict(PyObject **values, Py_ssize_t nkwargs, PyObject *kwnames,
return NULL;
}
for (i=0; i < nkwargs; i++) {
int err;
for (i = 0; i < nkwargs; i++) {
PyObject *key = PyTuple_GET_ITEM(kwnames, i);
PyObject *value = *values++;
if (PyDict_GetItem(kwdict, key) != NULL) {
PyErr_Format(PyExc_TypeError,
"%.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) {
assert(PyUnicode_CheckExact(key));
assert(PyDict_GetItem(kwdict, key) == NULL);
if (PyDict_SetItem(kwdict, key, value)) {
Py_DECREF(kwdict);
return NULL;
}
......@@ -2479,7 +2467,7 @@ _PyObject_FastCallKeywords(PyObject *func, PyObject **stack, Py_ssize_t nargs,
}
if (nkwargs > 0) {
kwdict = _PyStack_AsDict(stack + nargs, nkwargs, kwnames, func);
kwdict = _PyStack_AsDict(stack + nargs, kwnames);
if (kwdict == NULL) {
return NULL;
}
......
......@@ -279,7 +279,7 @@ _PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack,
nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
if (nkwargs > 0) {
kwdict = _PyStack_AsDict(stack + nargs, nkwargs, kwnames, func);
kwdict = _PyStack_AsDict(stack + nargs, kwnames);
if (kwdict == NULL) {
return NULL;
}
......
......@@ -2513,14 +2513,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
TARGET(BUILD_LIST_UNPACK) {
int convert_to_tuple = opcode == BUILD_TUPLE_UNPACK;
Py_ssize_t i;
PyObject *sum;
PyObject *sum = PyList_New(0);
PyObject *return_value;
if (convert_to_tuple && oparg == 1 && PyTuple_CheckExact(TOP())) {
DISPATCH();
}
sum = PyList_New(0);
if (sum == NULL)
goto error;
......@@ -2708,13 +2703,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
TARGET(BUILD_MAP_UNPACK) {
int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL;
Py_ssize_t i;
PyObject *sum;
if (with_call && oparg == 1 && PyDict_CheckExact(TOP())) {
DISPATCH();
}
sum = PyDict_New();
PyObject *sum = PyDict_New();
if (sum == NULL)
goto error;
......@@ -3290,11 +3279,53 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
PCALL(PCALL_ALL);
if (oparg & 0x01) {
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));
}
callargs = POP();
assert(PyTuple_CheckExact(callargs));
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);
Py_DECREF(func);
......@@ -3796,8 +3827,8 @@ too_many_positional(PyCodeObject *co, Py_ssize_t given, Py_ssize_t defcount,
static PyObject *
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
PyObject **args, Py_ssize_t argcount,
PyObject **kws, Py_ssize_t kwcount,
PyObject *kwnames, PyObject **kwstack,
PyObject **kwnames, PyObject **kwargs,
Py_ssize_t kwcount, int kwstep,
PyObject **defs, Py_ssize_t defcount,
PyObject *kwdefs, PyObject *closure,
PyObject *name, PyObject *qualname)
......@@ -3811,9 +3842,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount;
Py_ssize_t i, n;
PyObject *kwdict;
Py_ssize_t kwcount2 = kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames);
assert((kwcount == 0) || (kws != NULL));
if (globals == NULL) {
PyErr_SetString(PyExc_SystemError,
......@@ -3873,11 +3901,12 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
}
}
/* Handle keyword arguments passed as an array of (key, value) pairs */
for (i = 0; i < kwcount; i++) {
/* Handle keyword arguments passed as two strided arrays */
kwcount *= kwstep;
for (i = 0; i < kwcount; i += kwstep) {
PyObject **co_varnames;
PyObject *keyword = kws[2*i];
PyObject *value = kws[2*i + 1];
PyObject *keyword = kwnames[i];
PyObject *value = kwargs[i];
Py_ssize_t j;
if (keyword == NULL || !PyUnicode_Check(keyword)) {
......@@ -3932,61 +3961,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
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 */
if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) {
too_many_positional(co, argcount, defcount, fastlocals);
......@@ -4138,8 +4112,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
{
return _PyEval_EvalCodeWithName(_co, globals, locals,
args, argcount,
kws, kwcount,
NULL, NULL,
kws, kws + 1, kwcount, 2,
defs, defcount,
kwdefs, closure,
NULL, NULL);
......@@ -4923,8 +4896,9 @@ fast_function(PyObject *func, PyObject **stack,
}
return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL,
stack, nargs,
NULL, 0,
kwnames, stack + nargs,
nkwargs ? &PyTuple_GET_ITEM(kwnames, 0) : NULL,
stack + nargs,
nkwargs, 1,
d, (int)nd, kwdefs,
closure, name, qualname);
}
......@@ -5014,8 +4988,7 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs,
result = _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL,
args, nargs,
k, nk,
NULL, NULL,
k, k + 1, nk, 2,
d, nd, kwdefs,
closure, name, qualname);
Py_XDECREF(kwtuple);
......
......@@ -3503,7 +3503,7 @@ compiler_call_helper(struct compiler *c,
asdl_seq *keywords)
{
Py_ssize_t i, nseen, nelts, nkwelts;
int musttupleunpack = 0, mustdictunpack = 0;
int mustdictunpack = 0;
/* the number of tuples and dictionaries on the stack */
Py_ssize_t nsubargs = 0, nsubkwargs = 0;
......@@ -3532,7 +3532,6 @@ compiler_call_helper(struct compiler *c,
}
VISIT(c, expr, elt->v.Starred.value);
nsubargs++;
musttupleunpack = 1;
}
else {
VISIT(c, expr, elt);
......@@ -3541,13 +3540,13 @@ compiler_call_helper(struct compiler *c,
}
/* Same dance again for keyword arguments */
if (musttupleunpack || mustdictunpack) {
if (nsubargs || mustdictunpack) {
if (nseen) {
/* Pack up any trailing positional arguments. */
ADDOP_I(c, BUILD_TUPLE, nseen);
nsubargs++;
}
if (musttupleunpack || nsubargs > 1) {
if (nsubargs > 1) {
/* If we ended up with more than one stararg, we need
to concatenate them into a single sequence. */
ADDOP_I(c, BUILD_TUPLE_UNPACK, nsubargs);
......@@ -3579,7 +3578,7 @@ compiler_call_helper(struct compiler *c,
return 0;
nsubkwargs++;
}
if (mustdictunpack || nsubkwargs > 1) {
if (nsubkwargs > 1) {
/* Pack it all up */
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