diff -r 2954d2aa4c90 Objects/abstract.c --- a/Objects/abstract.c Sat Aug 20 01:38:00 2016 +0200 +++ b/Objects/abstract.c Sat Aug 20 02:20:48 2016 +0200 @@ -2257,9 +2257,7 @@ PyObject * 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; diff -r 2954d2aa4c90 Python/ceval.c --- a/Python/ceval.c Sat Aug 20 01:38:00 2016 +0200 +++ b/Python/ceval.c Sat Aug 20 02:20:48 2016 +0200 @@ -3784,11 +3784,121 @@ too_many_positional(PyCodeObject *co, in PyEval_EvalFrame() and PyEval_EvalCodeEx() you will need to adjust the test in the if statements in Misc/gdbinit (pystack and pystackv). */ +static int +fastlocals_dict(PyObject **fastlocals, PyCodeObject *co, + PyObject *kwdict, PyObject *kwargs) +{ + const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount; + _Py_IDENTIFIER(items); + PyObject *items, *iter; + PyObject *item = NULL; + + assert(PyDict_Check(kwargs)); + + items = _PyObject_CallMethodId((PyObject *)kwargs, &PyId_items, NULL); + if (items == NULL) { + return -1; + } + + iter = PyObject_GetIter(items); + Py_DECREF(items); + if (iter == NULL) { + goto fail; + } + + do { + PyObject **co_varnames; + PyObject *keyword, *value; + int j; + + /* Get first item */ + item = PyIter_Next(iter); + if (item == NULL) { + if (PyErr_Occurred()) { + goto fail; + } + + /* nothing more to add */ + break; + } + assert(PyTuple_CheckExact(item) && PyTuple_GET_SIZE(item) == 2); + + keyword = PyTuple_GET_ITEM(item, 0); + value = PyTuple_GET_ITEM(item, 1); + + 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_found; + } + + /* 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_found; + 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; + } + Py_DECREF(item); + continue; + + kw_found: + 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); + + Py_CLEAR(item); + } while (1); + + Py_DECREF(iter); + return 0; + +fail: + Py_DECREF(iter); + Py_XDECREF(item); + return -1; +} + static PyObject * -_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, - PyObject **args, int argcount, PyObject **kws, int kwcount, - PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure, - PyObject *name, PyObject *qualname) +_PyEval_EvalCode(PyObject *_co, PyObject *globals, PyObject *locals, + PyObject **args, int argcount, + PyObject **kws, int kwcount, PyObject *kwargs, + PyObject **defs, int defcount, + PyObject *kwdefs, PyObject *closure, + PyObject *name, PyObject *qualname) { PyCodeObject* co = (PyCodeObject*)_co; PyFrameObject *f; @@ -3801,6 +3911,9 @@ static PyObject * PyObject *kwdict; assert((kwcount == 0) || (kws != NULL)); + /* kws and kwargs are mutually exclusive + (arbitrary limit to detect bugs) */ + assert(!((kws != NULL) && (kwargs != NULL))); if (globals == NULL) { PyErr_SetString(PyExc_SystemError, @@ -3921,6 +4034,13 @@ static PyObject * SETLOCAL(j, value); } + /* Handle keyword arguments (passed as a dictionary) */ + if (kwargs != NULL) { + if (fastlocals_dict(fastlocals, co, kwdict, kwargs) < 0) { + goto fail; + } + } + /* Check the number of positional arguments */ if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) { too_many_positional(co, argcount, defcount, fastlocals); @@ -4068,12 +4188,12 @@ PyEval_EvalCodeEx(PyObject *_co, PyObjec PyObject **args, int argcount, PyObject **kws, int kwcount, PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) { - return _PyEval_EvalCodeWithName(_co, globals, locals, - args, argcount, - kws, kwcount, - defs, defcount, - kwdefs, closure, - NULL, NULL); + return _PyEval_EvalCode(_co, globals, locals, + args, argcount, + kws, kwcount, NULL, + defs, defcount, + kwdefs, closure, + NULL, NULL); } static PyObject * @@ -4870,11 +4990,11 @@ fast_function(PyObject *func, PyObject * d = NULL; nd = 0; } - return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, - stack, na, - stack + na, nk, - d, nd, kwdefs, - closure, name, qualname); + return _PyEval_EvalCode((PyObject*)co, globals, (PyObject *)NULL, + stack, na, + stack + na, nk, NULL, + d, nd, kwdefs, + closure, name, qualname); } PyObject * @@ -4890,11 +5010,11 @@ PyObject * 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 (argdefs == NULL && co->co_argcount == nargs && co->co_kwonlyargcount == 0 && + (kwargs == NULL || ((PyDictObject *)kwargs)->ma_used == 0) && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { return _PyFunction_FastCallNoKw(args, nargs, co, globals); @@ -4913,11 +5033,11 @@ PyObject * d = NULL; nd = 0; } - return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, - args, nargs, - NULL, 0, - d, nd, kwdefs, - closure, name, qualname); + return _PyEval_EvalCode((PyObject*)co, globals, (PyObject *)NULL, + args, nargs, + NULL, 0, kwargs, + d, nd, kwdefs, + closure, name, qualname); } static PyObject *