Add _PyObject_FastCallKeywords()

Issue #27830: Similar to _PyObject_FastCallDict(), but keyword arguments are
also passed in the same C array than positional arguments, rather than being
passed as a Python dict.
This commit is contained in:
Victor Stinner 2016-08-25 00:29:32 +02:00
parent 53868aaabb
commit 577e1f8cb4
4 changed files with 116 additions and 9 deletions

View file

@ -2309,6 +2309,85 @@ exit:
return result;
}
static PyObject *
_PyStack_AsDict(PyObject **stack, Py_ssize_t nkwargs, PyObject *func)
{
PyObject *kwdict;
kwdict = PyDict_New();
if (kwdict == NULL) {
return NULL;
}
while (--nkwargs >= 0) {
int err;
PyObject *key = *stack++;
PyObject *value = *stack++;
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) {
Py_DECREF(kwdict);
return NULL;
}
}
return kwdict;
}
PyObject *
_PyObject_FastCallKeywords(PyObject *func, PyObject **stack, Py_ssize_t nargs,
Py_ssize_t nkwargs)
{
PyObject *args, *kwdict, *result;
/* _PyObject_FastCallKeywords() must not be called with an exception set,
because it may clear it (directly or indirectly) and so the
caller loses its exception */
assert(!PyErr_Occurred());
assert(func != NULL);
assert(nargs >= 0);
assert(nkwargs >= 0);
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
if (PyFunction_Check(func)) {
/* Fast-path: avoid temporary tuple or dict */
return _PyFunction_FastCallKeywords(func, stack, nargs, nkwargs);
}
if (PyCFunction_Check(func) && nkwargs == 0) {
return _PyCFunction_FastCallDict(func, args, nargs, NULL);
}
/* Slow-path: build temporary tuple and/or dict */
args = _PyStack_AsTuple(stack, nargs);
if (nkwargs > 0) {
kwdict = _PyStack_AsDict(stack + nargs, nkwargs, func);
if (kwdict == NULL) {
Py_DECREF(args);
return NULL;
}
}
else {
kwdict = NULL;
}
result = PyObject_Call(func, args, kwdict);
Py_DECREF(args);
Py_XDECREF(kwdict);
return result;
}
static PyObject*
call_function_tail(PyObject *callable, PyObject *args)
{