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.
This commit is contained in:
Serhiy Storchaka 2016-09-12 00:52:40 +03:00
parent 29097d5a6a
commit b72810583e
10 changed files with 3445 additions and 3488 deletions

View File

@ -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).

View File

@ -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)

View File

@ -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
>>> 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?
>>> 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):

View File

@ -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):
# 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):

View File

@ -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,
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,
} }
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;
} }

View File

@ -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;
} }

View File

@ -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)
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)
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,
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,
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,
} }
} }
/* 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,
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,
{ {
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,
} }
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,
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);

View File

@ -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,
} }
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,
} }
/* 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,
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);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff