mirror of https://github.com/python/cpython
Emit METH_FASTCALL code in Argument Clinic
Issue #27810: * Modify vgetargskeywordsfast() to work on a C array of PyObject* rather than working on a tuple directly. * Add _PyArg_ParseStack() * Argument Clinic now emits code using the new METH_FASTCALL calling convention
This commit is contained in:
parent
a9efb2f56e
commit
f0ccbbbc57
|
@ -58,10 +58,13 @@ typedef struct _PyArg_Parser {
|
||||||
} _PyArg_Parser;
|
} _PyArg_Parser;
|
||||||
#ifdef PY_SSIZE_T_CLEAN
|
#ifdef PY_SSIZE_T_CLEAN
|
||||||
#define _PyArg_ParseTupleAndKeywordsFast _PyArg_ParseTupleAndKeywordsFast_SizeT
|
#define _PyArg_ParseTupleAndKeywordsFast _PyArg_ParseTupleAndKeywordsFast_SizeT
|
||||||
|
#define _PyArg_ParseStack _PyArg_ParseStack_SizeT
|
||||||
#define _PyArg_VaParseTupleAndKeywordsFast _PyArg_VaParseTupleAndKeywordsFast_SizeT
|
#define _PyArg_VaParseTupleAndKeywordsFast _PyArg_VaParseTupleAndKeywordsFast_SizeT
|
||||||
#endif
|
#endif
|
||||||
PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywordsFast(PyObject *, PyObject *,
|
PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywordsFast(PyObject *, PyObject *,
|
||||||
struct _PyArg_Parser *, ...);
|
struct _PyArg_Parser *, ...);
|
||||||
|
PyAPI_FUNC(int) _PyArg_ParseStack(PyObject **args, Py_ssize_t nargs, PyObject *kwnames,
|
||||||
|
struct _PyArg_Parser *, ...);
|
||||||
PyAPI_FUNC(int) _PyArg_VaParseTupleAndKeywordsFast(PyObject *, PyObject *,
|
PyAPI_FUNC(int) _PyArg_VaParseTupleAndKeywordsFast(PyObject *, PyObject *,
|
||||||
struct _PyArg_Parser *, va_list);
|
struct _PyArg_Parser *, va_list);
|
||||||
void _PyArg_Fini(void);
|
void _PyArg_Fini(void);
|
||||||
|
|
186
Python/getargs.c
186
Python/getargs.c
|
@ -79,6 +79,10 @@ static int vgetargskeywords(PyObject *, PyObject *,
|
||||||
const char *, char **, va_list *, int);
|
const char *, char **, va_list *, int);
|
||||||
static int vgetargskeywordsfast(PyObject *, PyObject *,
|
static int vgetargskeywordsfast(PyObject *, PyObject *,
|
||||||
struct _PyArg_Parser *, va_list *, int);
|
struct _PyArg_Parser *, va_list *, int);
|
||||||
|
static int vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs,
|
||||||
|
PyObject *keywords, PyObject *kwnames,
|
||||||
|
struct _PyArg_Parser *parser,
|
||||||
|
va_list *p_va, int flags);
|
||||||
static const char *skipitem(const char **, va_list *, int);
|
static const char *skipitem(const char **, va_list *, int);
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -1469,6 +1473,46 @@ _PyArg_ParseTupleAndKeywordsFast_SizeT(PyObject *args, PyObject *keywords,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyArg_ParseStack(PyObject **args, Py_ssize_t nargs, PyObject *kwnames,
|
||||||
|
struct _PyArg_Parser *parser, ...)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
va_list va;
|
||||||
|
|
||||||
|
if ((kwnames != NULL && !PyTuple_Check(kwnames)) ||
|
||||||
|
parser == NULL)
|
||||||
|
{
|
||||||
|
PyErr_BadInternalCall();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_start(va, parser);
|
||||||
|
retval = vgetargskeywordsfast_impl(args, nargs, NULL, kwnames, parser, &va, 0);
|
||||||
|
va_end(va);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyArg_ParseStack_SizeT(PyObject **args, Py_ssize_t nargs, PyObject *kwnames,
|
||||||
|
struct _PyArg_Parser *parser, ...)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
va_list va;
|
||||||
|
|
||||||
|
if ((kwnames != NULL && !PyTuple_Check(kwnames)) ||
|
||||||
|
parser == NULL)
|
||||||
|
{
|
||||||
|
PyErr_BadInternalCall();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_start(va, parser);
|
||||||
|
retval = vgetargskeywordsfast_impl(args, nargs, NULL, kwnames, parser, &va, FLAG_SIZE_T);
|
||||||
|
va_end(va);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyArg_VaParseTupleAndKeywordsFast(PyObject *args, PyObject *keywords,
|
_PyArg_VaParseTupleAndKeywordsFast(PyObject *args, PyObject *keywords,
|
||||||
|
@ -1899,10 +1943,37 @@ parser_clear(struct _PyArg_Parser *parser)
|
||||||
Py_CLEAR(parser->kwtuple);
|
Py_CLEAR(parser->kwtuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
find_keyword(PyObject *kwnames, PyObject **kwstack, PyObject *key)
|
||||||
|
{
|
||||||
|
Py_ssize_t i, nkwargs;
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
if (!PyUnicode_Check(kwname)) {
|
||||||
|
/* ignore non-string keyword keys:
|
||||||
|
an error will be raised above */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (_PyUnicode_EQ(kwname, key)) {
|
||||||
|
return kwstack[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vgetargskeywordsfast(PyObject *args, PyObject *keywords,
|
vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs,
|
||||||
struct _PyArg_Parser *parser,
|
PyObject *keywords, PyObject *kwnames,
|
||||||
va_list *p_va, int flags)
|
struct _PyArg_Parser *parser,
|
||||||
|
va_list *p_va, int flags)
|
||||||
{
|
{
|
||||||
PyObject *kwtuple;
|
PyObject *kwtuple;
|
||||||
char msgbuf[512];
|
char msgbuf[512];
|
||||||
|
@ -1911,17 +1982,20 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
|
||||||
const char *msg;
|
const char *msg;
|
||||||
PyObject *keyword;
|
PyObject *keyword;
|
||||||
int i, pos, len;
|
int i, pos, len;
|
||||||
Py_ssize_t nargs, nkeywords;
|
Py_ssize_t nkeywords;
|
||||||
PyObject *current_arg;
|
PyObject *current_arg;
|
||||||
freelistentry_t static_entries[STATIC_FREELIST_ENTRIES];
|
freelistentry_t static_entries[STATIC_FREELIST_ENTRIES];
|
||||||
freelist_t freelist;
|
freelist_t freelist;
|
||||||
|
PyObject **kwstack = NULL;
|
||||||
|
|
||||||
freelist.entries = static_entries;
|
freelist.entries = static_entries;
|
||||||
freelist.first_available = 0;
|
freelist.first_available = 0;
|
||||||
freelist.entries_malloced = 0;
|
freelist.entries_malloced = 0;
|
||||||
|
|
||||||
assert(args != NULL && PyTuple_Check(args));
|
|
||||||
assert(keywords == NULL || PyDict_Check(keywords));
|
assert(keywords == NULL || PyDict_Check(keywords));
|
||||||
|
assert(kwnames == NULL || PyTuple_Check(kwnames));
|
||||||
|
assert((keywords != NULL || kwnames != NULL)
|
||||||
|
|| (keywords == NULL && kwnames == NULL));
|
||||||
assert(parser != NULL);
|
assert(parser != NULL);
|
||||||
assert(p_va != NULL);
|
assert(p_va != NULL);
|
||||||
|
|
||||||
|
@ -1942,8 +2016,16 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
|
||||||
freelist.entries_malloced = 1;
|
freelist.entries_malloced = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
nargs = PyTuple_GET_SIZE(args);
|
if (keywords != NULL) {
|
||||||
nkeywords = (keywords == NULL) ? 0 : PyDict_Size(keywords);
|
nkeywords = PyDict_Size(keywords);
|
||||||
|
}
|
||||||
|
else if (kwnames != NULL) {
|
||||||
|
nkeywords = PyTuple_GET_SIZE(kwnames);
|
||||||
|
kwstack = args + nargs;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nkeywords = 0;
|
||||||
|
}
|
||||||
if (nargs + nkeywords > len) {
|
if (nargs + nkeywords > len) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"%s%s takes at most %d argument%s (%zd given)",
|
"%s%s takes at most %d argument%s (%zd given)",
|
||||||
|
@ -1976,9 +2058,14 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
|
||||||
|
|
||||||
current_arg = NULL;
|
current_arg = NULL;
|
||||||
if (nkeywords && i >= pos) {
|
if (nkeywords && i >= pos) {
|
||||||
current_arg = PyDict_GetItem(keywords, keyword);
|
if (keywords != NULL) {
|
||||||
if (!current_arg && PyErr_Occurred()) {
|
current_arg = PyDict_GetItem(keywords, keyword);
|
||||||
return cleanreturn(0, &freelist);
|
if (!current_arg && PyErr_Occurred()) {
|
||||||
|
return cleanreturn(0, &freelist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
current_arg = find_keyword(kwnames, kwstack, keyword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (current_arg) {
|
if (current_arg) {
|
||||||
|
@ -1993,7 +2080,7 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i < nargs) {
|
else if (i < nargs) {
|
||||||
current_arg = PyTuple_GET_ITEM(args, i);
|
current_arg = args[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_arg) {
|
if (current_arg) {
|
||||||
|
@ -2040,24 +2127,52 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
|
||||||
|
|
||||||
/* make sure there are no extraneous keyword arguments */
|
/* make sure there are no extraneous keyword arguments */
|
||||||
if (nkeywords > 0) {
|
if (nkeywords > 0) {
|
||||||
PyObject *key, *value;
|
if (keywords != NULL) {
|
||||||
Py_ssize_t pos = 0;
|
PyObject *key, *value;
|
||||||
while (PyDict_Next(keywords, &pos, &key, &value)) {
|
Py_ssize_t pos = 0;
|
||||||
int match;
|
while (PyDict_Next(keywords, &pos, &key, &value)) {
|
||||||
if (!PyUnicode_Check(key)) {
|
int match;
|
||||||
PyErr_SetString(PyExc_TypeError,
|
if (!PyUnicode_Check(key)) {
|
||||||
"keywords must be strings");
|
PyErr_SetString(PyExc_TypeError,
|
||||||
return cleanreturn(0, &freelist);
|
"keywords must be strings");
|
||||||
}
|
return cleanreturn(0, &freelist);
|
||||||
match = PySequence_Contains(kwtuple, key);
|
}
|
||||||
if (match <= 0) {
|
match = PySequence_Contains(kwtuple, key);
|
||||||
if (!match) {
|
if (match <= 0) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
if (!match) {
|
||||||
"'%U' is an invalid keyword "
|
PyErr_Format(PyExc_TypeError,
|
||||||
"argument for this function",
|
"'%U' is an invalid keyword "
|
||||||
key);
|
"argument for this function",
|
||||||
|
key);
|
||||||
|
}
|
||||||
|
return cleanreturn(0, &freelist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
return cleanreturn(0, &freelist);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2065,6 +2180,21 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
|
||||||
return cleanreturn(1, &freelist);
|
return cleanreturn(1, &freelist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vgetargskeywordsfast(PyObject *args, PyObject *keywords,
|
||||||
|
struct _PyArg_Parser *parser, va_list *p_va, int flags)
|
||||||
|
{
|
||||||
|
PyObject **stack;
|
||||||
|
Py_ssize_t nargs;
|
||||||
|
|
||||||
|
assert(args != NULL && PyTuple_Check(args));
|
||||||
|
|
||||||
|
stack = &PyTuple_GET_ITEM(args, 0);
|
||||||
|
nargs = PyTuple_GET_SIZE(args);
|
||||||
|
return vgetargskeywordsfast_impl(stack, nargs, keywords, NULL,
|
||||||
|
parser, p_va, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
skipitem(const char **p_format, va_list *p_va, int flags)
|
skipitem(const char **p_format, va_list *p_va, int flags)
|
||||||
|
|
|
@ -705,6 +705,11 @@ class CLanguage(Language):
|
||||||
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
|
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
parser_prototype_fastcall = normalize_snippet("""
|
||||||
|
static PyObject *
|
||||||
|
{c_basename}({self_type}{self_name}, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
|
||||||
|
""")
|
||||||
|
|
||||||
parser_prototype_varargs = normalize_snippet("""
|
parser_prototype_varargs = normalize_snippet("""
|
||||||
static PyObject *
|
static PyObject *
|
||||||
{c_basename}({self_type}{self_name}, PyObject *args)
|
{c_basename}({self_type}{self_name}, PyObject *args)
|
||||||
|
@ -845,6 +850,19 @@ class CLanguage(Language):
|
||||||
}}
|
}}
|
||||||
""", indent=4))
|
""", indent=4))
|
||||||
|
|
||||||
|
elif not new_or_init:
|
||||||
|
flags = "METH_FASTCALL"
|
||||||
|
|
||||||
|
parser_prototype = parser_prototype_fastcall
|
||||||
|
|
||||||
|
body = normalize_snippet("""
|
||||||
|
if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
|
||||||
|
{parse_arguments})) {{
|
||||||
|
goto exit;
|
||||||
|
}}
|
||||||
|
""", indent=4)
|
||||||
|
parser_definition = parser_body(parser_prototype, body)
|
||||||
|
parser_definition = insert_keywords(parser_definition)
|
||||||
else:
|
else:
|
||||||
# positional-or-keyword arguments
|
# positional-or-keyword arguments
|
||||||
flags = "METH_VARARGS|METH_KEYWORDS"
|
flags = "METH_VARARGS|METH_KEYWORDS"
|
||||||
|
|
Loading…
Reference in New Issue