changeset: 102842:1aefb4c4a7b4 user: Victor Stinner date: Mon Aug 22 23:15:44 2016 +0200 files: Include/funcobject.h Include/methodobject.h Objects/abstract.c Objects/methodobject.c Python/ceval.c description: _PyFunction_FastCallDict() supports keyword args Issue #27809: * Rename _PyFunction_FastCall() to _PyFunction_FastCallDict() * Rename _PyCFunction_FastCall() to _PyCFunction_FastCallDict() * _PyFunction_FastCallDict() now supports keyword arguments diff -r 263334a652ac -r 1aefb4c4a7b4 Include/funcobject.h --- a/Include/funcobject.h Mon Aug 22 22:48:54 2016 +0200 +++ b/Include/funcobject.h Mon Aug 22 23:15:44 2016 +0200 @@ -59,7 +59,7 @@ PyAPI_FUNC(int) PyFunction_SetAnnotations(PyObject *, PyObject *); #ifndef Py_LIMITED_API -PyAPI_FUNC(PyObject *) _PyFunction_FastCall( +PyAPI_FUNC(PyObject *) _PyFunction_FastCallDict( PyObject *func, PyObject **args, int nargs, PyObject *kwargs); diff -r 263334a652ac -r 1aefb4c4a7b4 Include/methodobject.h --- a/Include/methodobject.h Mon Aug 22 22:48:54 2016 +0200 +++ b/Include/methodobject.h Mon Aug 22 23:15:44 2016 +0200 @@ -38,7 +38,7 @@ PyAPI_FUNC(PyObject *) PyCFunction_Call(PyObject *, PyObject *, PyObject *); #ifndef Py_LIMITED_API -PyAPI_FUNC(PyObject *) _PyCFunction_FastCall(PyObject *func, +PyAPI_FUNC(PyObject *) _PyCFunction_FastCallDict(PyObject *func, PyObject **args, int nargs, PyObject *kwargs); #endif diff -r 263334a652ac -r 1aefb4c4a7b4 Objects/abstract.c --- a/Objects/abstract.c Mon Aug 22 22:48:54 2016 +0200 +++ b/Objects/abstract.c Mon Aug 22 23:15:44 2016 +0200 @@ -2255,7 +2255,8 @@ } PyObject * -_PyObject_FastCallDict(PyObject *func, PyObject **args, int nargs, PyObject *kwargs) +_PyObject_FastCallDict(PyObject *func, PyObject **args, int nargs, + PyObject *kwargs) { ternaryfunc call; PyObject *result = NULL; @@ -2268,19 +2269,17 @@ assert(func != NULL); assert(nargs >= 0); assert(nargs == 0 || args != NULL); - /* issue #27128: support for keywords will come later: - _PyFunction_FastCall() doesn't support keyword arguments yet */ - assert(kwargs == NULL); + assert(kwargs == NULL || PyDict_Check(kwargs)); if (Py_EnterRecursiveCall(" while calling a Python object")) { return NULL; } if (PyFunction_Check(func)) { - result = _PyFunction_FastCall(func, args, nargs, kwargs); + result = _PyFunction_FastCallDict(func, args, nargs, kwargs); } else if (PyCFunction_Check(func)) { - result = _PyCFunction_FastCall(func, args, nargs, kwargs); + result = _PyCFunction_FastCallDict(func, args, nargs, kwargs); } else { PyObject *tuple; diff -r 263334a652ac -r 1aefb4c4a7b4 Objects/methodobject.c --- a/Objects/methodobject.c Mon Aug 22 22:48:54 2016 +0200 +++ b/Objects/methodobject.c Mon Aug 22 23:15:44 2016 +0200 @@ -146,8 +146,8 @@ } PyObject * -_PyCFunction_FastCall(PyObject *func_obj, PyObject **args, int nargs, - PyObject *kwargs) +_PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, int nargs, + PyObject *kwargs) { PyCFunctionObject* func = (PyCFunctionObject*)func_obj; PyCFunction meth = PyCFunction_GET_FUNCTION(func); @@ -155,7 +155,7 @@ PyObject *result; int flags; - /* _PyCFunction_FastCall() must not be called with an exception set, + /* _PyCFunction_FastCallDict() 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()); diff -r 263334a652ac -r 1aefb4c4a7b4 Python/ceval.c --- a/Python/ceval.c Mon Aug 22 22:48:54 2016 +0200 +++ b/Python/ceval.c Mon Aug 22 23:15:44 2016 +0200 @@ -4889,24 +4889,29 @@ } PyObject * -_PyFunction_FastCall(PyObject *func, PyObject **args, int nargs, PyObject *kwargs) +_PyFunction_FastCallDict(PyObject *func, PyObject **args, int nargs, + PyObject *kwargs) { PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); PyObject *kwdefs, *closure, *name, *qualname; + PyObject *kwtuple, **k; PyObject **d; int nd; + Py_ssize_t nk; + PyObject *result; PCALL(PCALL_FUNCTION); PCALL(PCALL_FAST_FUNCTION); - /* issue #27128: support for keywords will come later */ - assert(kwargs == NULL); - - if (co->co_kwonlyargcount == 0 && kwargs == NULL && + assert(kwargs == NULL || PyDict_Check(kwargs)); + + if (co->co_kwonlyargcount == 0 && + (kwargs == NULL || PyDict_Size(kwargs) == 0) && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { + /* Fast paths */ if (argdefs == NULL && co->co_argcount == nargs) { return _PyFunction_FastCallNoKw(co, args, nargs, globals); } @@ -4920,6 +4925,30 @@ } } + if (kwargs != NULL) { + Py_ssize_t pos, i; + nk = PyDict_Size(kwargs); + + kwtuple = PyTuple_New(2 * nk); + if (kwtuple == NULL) { + return NULL; + } + + k = &PyTuple_GET_ITEM(kwtuple, 0); + pos = i = 0; + while (PyDict_Next(kwargs, &pos, &k[i], &k[i+1])) { + Py_INCREF(k[i]); + Py_INCREF(k[i+1]); + i += 2; + } + nk = i / 2; + } + else { + kwtuple = NULL; + k = NULL; + nk = 0; + } + kwdefs = PyFunction_GET_KW_DEFAULTS(func); closure = PyFunction_GET_CLOSURE(func); name = ((PyFunctionObject *)func) -> func_name; @@ -4933,11 +4962,14 @@ d = NULL; nd = 0; } - return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, - args, nargs, - NULL, 0, - d, nd, kwdefs, - closure, name, qualname); + + result = _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, + args, nargs, + k, (int)nk, + d, nd, kwdefs, + closure, name, qualname); + Py_XDECREF(kwtuple); + return result; } static PyObject *