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.
diff --git a/Objects/abstract.c b/Objects/abstract.c
index f302281..d271d94 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -2309,6 +2309,85 @@
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)
{