From b9009391868f739f4ddf09fa04061f6af05228b3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 22 Aug 2016 23:15:44 +0200 Subject: _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 --- Include/funcobject.h | 2 +- Include/methodobject.h | 2 +- Objects/abstract.c | 11 +++++------ Objects/methodobject.c | 6 +++--- Python/ceval.c | 50 +++++++++++++++++++++++++++++++++++++++++--------- 5 files changed, 51 insertions(+), 20 deletions(-) diff --git a/Include/funcobject.h b/Include/funcobject.h index 908944d..24602e6 100644 --- a/Include/funcobject.h +++ b/Include/funcobject.h @@ -59,7 +59,7 @@ PyAPI_FUNC(PyObject *) PyFunction_GetAnnotations(PyObject *); 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 --git a/Include/methodobject.h b/Include/methodobject.h index 8dec00a..107a650 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -38,7 +38,7 @@ PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *); 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 --git a/Objects/abstract.c b/Objects/abstract.c index 2ce7f32..0e67693 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2255,7 +2255,8 @@ _PyStack_AsTuple(PyObject **stack, Py_ssize_t nargs) } 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 @@ _PyObject_FastCallDict(PyObject *func, PyObject **args, int nargs, PyObject *kwa 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 --git a/Objects/methodobject.c b/Objects/methodobject.c index 0e26232..edb2fc0 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -146,8 +146,8 @@ PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds) } 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 @@ _PyCFunction_FastCall(PyObject *func_obj, PyObject **args, int nargs, 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 --git a/Python/ceval.c b/Python/ceval.c index 96380bc..d656fab 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4889,24 +4889,29 @@ fast_function(PyObject *func, PyObject **stack, int n, int nargs, int nk) } 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); + assert(kwargs == NULL || PyDict_Check(kwargs)); - if (co->co_kwonlyargcount == 0 && kwargs == NULL && + 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 @@ _PyFunction_FastCall(PyObject *func, PyObject **args, int nargs, PyObject *kwarg } } + 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 @@ _PyFunction_FastCall(PyObject *func, PyObject **args, int nargs, PyObject *kwarg 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 * -- cgit v0.12