diff -r ae6813d21859 Python/getargs.c --- a/Python/getargs.c Thu Sep 22 11:43:46 2016 +0300 +++ b/Python/getargs.c Mon Sep 26 20:36:17 2016 +0300 @@ -1830,17 +1830,18 @@ vgetargskeywords(PyObject *args, PyObjec /* List of static parsers. */ static struct _PyArg_Parser *static_arg_parsers = NULL; +#define kwindices kwtuple static int parser_init(struct _PyArg_Parser *parser) { const char * const *keywords; const char *format, *msg; - int i, len, min, max, nkw; - PyObject *kwtuple; + int i, len, min, max; + PyObject *kwindices; assert(parser->format != NULL); assert(parser->keywords != NULL); - if (parser->kwtuple != NULL) { + if (parser->kwindices != NULL) { return 1; } @@ -1926,22 +1927,34 @@ parser_init(struct _PyArg_Parser *parser return 0; } - nkw = len - parser->pos; - kwtuple = PyTuple_New(nkw); - if (kwtuple == NULL) { + kwindices = PyDict_New(); + if (kwindices == NULL) { return 0; } - keywords = parser->keywords + parser->pos; - for (i = 0; i < nkw; i++) { - PyObject *str = PyUnicode_FromString(keywords[i]); + for (i = parser->pos; i < len; i++) { + PyObject *str; + PyObject *index = PyLong_FromLong(i); + if (index == NULL) { + Py_DECREF(kwindices); + return 0; + } + str = PyUnicode_InternFromString(keywords[i]); if (str == NULL) { - Py_DECREF(kwtuple); + Py_DECREF(index); + Py_DECREF(kwindices); return 0; } PyUnicode_InternInPlace(&str); - PyTuple_SET_ITEM(kwtuple, i, str); + if (PyDict_SetItem(kwindices, str, index) < 0) { + Py_DECREF(index); + Py_DECREF(str); + Py_DECREF(kwindices); + return 0; + } + Py_DECREF(index); + Py_DECREF(str); } - parser->kwtuple = kwtuple; + parser->kwindices = kwindices; assert(parser->next == NULL); parser->next = static_arg_parsers; @@ -1952,33 +1965,78 @@ parser_init(struct _PyArg_Parser *parser static void parser_clear(struct _PyArg_Parser *parser) { - Py_CLEAR(parser->kwtuple); + Py_CLEAR(parser->kwindices); } -static PyObject* -find_keyword(PyObject *kwnames, PyObject **kwstack, PyObject *key) +static int +next_keyword(PyObject *keywords, PyObject *kwnames, PyObject **kwstack, + Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) { - Py_ssize_t i, nkwargs; + if (keywords) { + return PyDict_Next(keywords, ppos, pkey, pvalue); + } + else { + Py_ssize_t i = *ppos; + if (i >= PyTuple_GET_SIZE(kwnames)) + return 0; + *ppos = i + 1; + *pkey = PyTuple_GET_ITEM(kwnames, i); + *pvalue = kwstack[i]; + return 1; + } +} - nkwargs = PyTuple_GET_SIZE(kwnames); - for (i=0; i < nkwargs; i++) { - PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); - - /* ptr==ptr should match in most cases since keyword keys - should be interned strings */ - if (kwname == key) { - return kwstack[i]; +static int +unpack_keywords(PyObject *keywords, PyObject *kwnames, PyObject **kwstack, + PyObject *kwindices, + int len, int nargs, PyObject **kwargs) +{ + /* unpack keyword args using kwindices to drive process */ + PyObject *key, *value, *index; + Py_ssize_t pos = 0; + int i, maxarg = nargs; + for (i = nargs; i < len; i++) { + kwargs[i - nargs] = NULL; + } + while (next_keyword(keywords, kwnames, kwstack, &pos, &key, &value)) { + if (!PyUnicode_Check(key)) { + PyErr_SetString(PyExc_TypeError, + "keywords must be strings"); + return -1; } - if (!PyUnicode_Check(kwname)) { - /* ignore non-string keyword keys: - an error will be raised above */ - continue; + index = PyDict_GetItem(kwindices, key); + if (index == NULL) { + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, + "'%U' is an invalid keyword " + "argument for this function", + key); + } + return -1; } - if (_PyUnicode_EQ(kwname, key)) { - return kwstack[i]; + i = _PyLong_AsInt(index); + assert(i >= 0); + assert(i < len); + if (i < nargs) { + /* arg present in tuple and in dict */ + PyErr_Format(PyExc_TypeError, + "Argument given by name ('%U') " + "and position (%d)", + key, i+1); + return -1; } + if (kwargs[i - nargs] != NULL) { + PyErr_Format(PyExc_TypeError, + "Argument given by name ('%U') " + "specified twice", + key); + return -1; + } + kwargs[i - nargs] = value; + if (i >= maxarg) + maxarg = i + 1; } - return NULL; + return maxarg; } static int @@ -1987,18 +2045,18 @@ vgetargskeywordsfast_impl(PyObject **arg struct _PyArg_Parser *parser, va_list *p_va, int flags) { - PyObject *kwtuple; char msgbuf[512]; int levels[32]; const char *format; const char *msg; - PyObject *keyword; - int i, pos, len; + int i, len, maxarg; Py_ssize_t nkeywords; PyObject *current_arg; freelistentry_t static_entries[STATIC_FREELIST_ENTRIES]; freelist_t freelist; PyObject **kwstack = NULL; + PyObject *static_kwargs[STATIC_FREELIST_ENTRIES]; + PyObject **kwargs = static_kwargs; freelist.entries = static_entries; freelist.first_available = 0; @@ -2015,10 +2073,7 @@ vgetargskeywordsfast_impl(PyObject **arg return 0; } - kwtuple = parser->kwtuple; - pos = parser->pos; - len = pos + PyTuple_GET_SIZE(kwtuple); - + len = parser->pos + PyDict_GET_SIZE(parser->kwindices); if (len > STATIC_FREELIST_ENTRIES) { freelist.entries = PyMem_NEW(freelistentry_t, len); if (freelist.entries == NULL) { @@ -2027,6 +2082,7 @@ vgetargskeywordsfast_impl(PyObject **arg } freelist.entries_malloced = 1; } + memset(freelist.entries, 0, sizeof(freelistentry_t) * len); if (keywords != NULL) { nkeywords = PyDict_Size(keywords); @@ -2046,150 +2102,96 @@ vgetargskeywordsfast_impl(PyObject **arg len, (len == 1) ? "" : "s", nargs + nkeywords); - return cleanreturn(0, &freelist); + goto error; } if (parser->max < nargs) { PyErr_Format(PyExc_TypeError, "Function takes %s %d positional arguments (%d given)", (parser->min != INT_MAX) ? "at most" : "exactly", parser->max, nargs); - return cleanreturn(0, &freelist); + goto error; } + maxarg = nargs; + if (nkeywords > 0) { + if (len - nargs > STATIC_FREELIST_ENTRIES) { + kwargs = PyMem_NEW(PyObject *, len - nargs); + if (kwargs == NULL) { + PyErr_NoMemory(); + goto error; + } + } + memset(kwargs, 0, sizeof(PyObject *) * (len - nargs)); + maxarg = unpack_keywords(keywords, kwnames, kwstack, + parser->kwindices, len, + nargs, kwargs); + if (maxarg < 0) { + goto error; + } + } format = parser->format; - /* convert tuple args and keyword args in same loop, using kwtuple to drive process */ - for (i = 0; i < len; i++) { - keyword = (i >= pos) ? PyTuple_GET_ITEM(kwtuple, i - pos) : NULL; + for (i = 0; i < maxarg; i++) { if (*format == '|') { format++; } if (*format == '$') { format++; } - assert(!IS_END_OF_FORMAT(*format)); + assert(!IS_END_OF_FORMAT(*format) && (*format != '|') && (*format != '$')); - current_arg = NULL; - if (nkeywords && i >= pos) { - if (keywords != NULL) { - current_arg = PyDict_GetItem(keywords, keyword); - if (!current_arg && PyErr_Occurred()) { - return cleanreturn(0, &freelist); - } - } - else { - current_arg = find_keyword(kwnames, kwstack, keyword); - } - } - if (current_arg) { - --nkeywords; - if (i < nargs) { - /* arg present in tuple and in dict */ - PyErr_Format(PyExc_TypeError, - "Argument given by name ('%U') " - "and position (%d)", - keyword, i+1); - return cleanreturn(0, &freelist); - } - } - else if (i < nargs) { + if (i < nargs) { + /* positional argument */ current_arg = args[i]; } - - if (current_arg) { - msg = convertitem(current_arg, &format, p_va, flags, - levels, msgbuf, sizeof(msgbuf), &freelist); - if (msg) { - seterror(i+1, msg, levels, parser->fname, parser->custom_msg); - return cleanreturn(0, &freelist); - } - continue; - } - - if (i < parser->min) { - /* Less arguments than required */ - if (i < pos) { - PyErr_Format(PyExc_TypeError, - "Function takes %s %d positional arguments" - " (%d given)", - (Py_MIN(pos, parser->min) < parser->max) ? "at least" : "exactly", - Py_MIN(pos, parser->min), nargs); - } - else { - PyErr_Format(PyExc_TypeError, "Required argument " - "'%U' (pos %d) not found", - keyword, i+1); - } - return cleanreturn(0, &freelist); - } - /* current code reports success when all required args - * fulfilled and no keyword args left, with no further - * validation. XXX Maybe skip this in debug build ? - */ - if (!nkeywords) { - return cleanreturn(1, &freelist); - } - - /* We are into optional args, skip thru to any remaining - * keyword args */ - msg = skipitem(&format, p_va, flags); - assert(msg == NULL); - } - - assert(IS_END_OF_FORMAT(*format) || (*format == '|') || (*format == '$')); - - /* make sure there are no extraneous keyword arguments */ - if (nkeywords > 0) { - if (keywords != NULL) { - PyObject *key, *value; - Py_ssize_t pos = 0; - while (PyDict_Next(keywords, &pos, &key, &value)) { - int match; - if (!PyUnicode_Check(key)) { - PyErr_SetString(PyExc_TypeError, - "keywords must be strings"); - return cleanreturn(0, &freelist); + else { + /* keyword argument */ + current_arg = kwargs[i - nargs]; + if (!current_arg) { + if (i < parser->min) { + /* Less arguments than required */ + if (i < parser->pos) { + i = Py_MIN(parser->pos, parser->min); + PyErr_Format(PyExc_TypeError, + "Function takes %s %d positional arguments" + " (%d given)", + i < parser->max ? "at least" : "exactly", + i, nargs); + } + else { + PyErr_Format(PyExc_TypeError, "Required argument " + "'%s' (pos %d) not found", + parser->keywords[i], i+1); + } + goto error; } - match = PySequence_Contains(kwtuple, key); - if (match <= 0) { - if (!match) { - PyErr_Format(PyExc_TypeError, - "'%U' is an invalid keyword " - "argument for this function", - key); - } - return cleanreturn(0, &freelist); + else { + /* We are into optional args, skip thru to any remaining + * keyword args */ + msg = skipitem(&format, p_va, flags); + assert(msg == NULL); + continue; } } } - else { - Py_ssize_t j, nkwargs; - nkwargs = PyTuple_GET_SIZE(kwnames); - for (j=0; j < nkwargs; j++) { - PyObject *key = PyTuple_GET_ITEM(kwnames, j); - int match; - - if (!PyUnicode_Check(key)) { - PyErr_SetString(PyExc_TypeError, - "keywords must be strings"); - return cleanreturn(0, &freelist); - } - - match = PySequence_Contains(kwtuple, key); - if (match <= 0) { - if (!match) { - PyErr_Format(PyExc_TypeError, - "'%U' is an invalid keyword " - "argument for this function", - key); - } - return cleanreturn(0, &freelist); - } - } + msg = convertitem(current_arg, &format, p_va, flags, + levels, msgbuf, sizeof(msgbuf), &freelist); + if (msg) { + seterror(i+1, msg, levels, parser->fname, parser->custom_msg); + goto error; } } + if (kwargs != static_kwargs) { + PyMem_FREE(kwargs); + } return cleanreturn(1, &freelist); + + error: + if (kwargs != static_kwargs) { + PyMem_FREE(kwargs); + } + return cleanreturn(0, &freelist); } static int