summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/abstract.h16
-rw-r--r--Include/methodobject.h4
-rw-r--r--Objects/abstract.c56
-rw-r--r--Objects/methodobject.c24
-rw-r--r--Python/getargs.c3
5 files changed, 102 insertions, 1 deletions
diff --git a/Include/abstract.h b/Include/abstract.h
index 3f398da..3e630b1 100644
--- a/Include/abstract.h
+++ b/Include/abstract.h
@@ -277,6 +277,22 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
PyObject *kwnames,
PyObject *func);
+ /* Convert (args, nargs, kwargs) into a (stack, nargs, kwnames).
+
+ Return a new stack which should be released by PyMem_Free(), or return
+ args unchanged if kwargs is NULL or an empty dictionary.
+
+ The stack uses borrowed references.
+
+ The type of keyword keys is not checked, these checks should be done
+ later (ex: _PyArg_ParseStack). */
+ PyAPI_FUNC(PyObject **) _PyStack_UnpackDict(
+ PyObject **args,
+ Py_ssize_t nargs,
+ PyObject *kwargs,
+ PyObject **kwnames,
+ PyObject *func);
+
/* Call the callable object func with the "fast call" calling convention:
args is a C array for positional arguments (nargs is the number of
positional arguments), kwargs is a dictionary for keyword arguments.
diff --git a/Include/methodobject.h b/Include/methodobject.h
index 1f4f06c..9dba58f 100644
--- a/Include/methodobject.h
+++ b/Include/methodobject.h
@@ -16,6 +16,8 @@ PyAPI_DATA(PyTypeObject) PyCFunction_Type;
#define PyCFunction_Check(op) (Py_TYPE(op) == &PyCFunction_Type)
typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
+typedef PyObject *(*_PyCFunctionFast) (PyObject *self, PyObject **args,
+ Py_ssize_t nargs, PyObject *kwnames);
typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *,
PyObject *);
typedef PyObject *(*PyNoArgsFunction)(PyObject *);
@@ -83,6 +85,8 @@ PyAPI_FUNC(PyObject *) PyCFunction_NewEx(PyMethodDef *, PyObject *,
#define METH_COEXIST 0x0040
+#define METH_FASTCALL 0x0080
+
#ifndef Py_LIMITED_API
typedef struct {
PyObject_HEAD
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 9de6b83..f9e5009 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -2403,6 +2403,62 @@ _PyStack_AsDict(PyObject **values, Py_ssize_t nkwargs, PyObject *kwnames,
return kwdict;
}
+PyObject **
+_PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs,
+ PyObject **p_kwnames, PyObject *func)
+{
+ PyObject **stack, **kwstack;
+ Py_ssize_t nkwargs;
+ Py_ssize_t pos, i;
+ PyObject *key, *value;
+ PyObject *kwnames;
+
+ assert(nargs >= 0);
+ assert(kwargs == NULL || PyDict_CheckExact(kwargs));
+
+ nkwargs = (kwargs != NULL) ? PyDict_Size(kwargs) : 0;
+ if (!nkwargs) {
+ *p_kwnames = NULL;
+ return args;
+ }
+
+ if ((size_t)nargs > PY_SSIZE_T_MAX / sizeof(stack[0]) - (size_t)nkwargs) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ stack = PyMem_Malloc((nargs + nkwargs) * sizeof(stack[0]));
+ if (stack == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ kwnames = PyTuple_New(nkwargs);
+ if (kwnames == NULL) {
+ PyMem_Free(stack);
+ return NULL;
+ }
+
+ /* Copy position arguments (borrowed references) */
+ Py_MEMCPY(stack, args, nargs * sizeof(stack[0]));
+
+ kwstack = stack + nargs;
+ pos = i = 0;
+ /* This loop doesn't support lookup function mutating the dictionary
+ to change its size. It's a deliberate choice for speed, this function is
+ called in the performance critical hot code. */
+ while (PyDict_Next(kwargs, &pos, &key, &value)) {
+ Py_INCREF(key);
+ PyTuple_SET_ITEM(kwnames, i, key);
+ /* The stack contains borrowed references */
+ kwstack[i] = value;
+ i++;
+ }
+
+ *p_kwnames = kwnames;
+ return stack;
+}
+
PyObject *
_PyObject_FastCallKeywords(PyObject *func, PyObject **stack, Py_ssize_t nargs,
PyObject *kwnames)
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
index 0fe3315..487ccd7 100644
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -97,6 +97,11 @@ PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds)
if (flags == (METH_VARARGS | METH_KEYWORDS)) {
res = (*(PyCFunctionWithKeywords)meth)(self, args, kwds);
}
+ else if (flags == METH_FASTCALL) {
+ PyObject **stack = &PyTuple_GET_ITEM(args, 0);
+ Py_ssize_t nargs = PyTuple_GET_SIZE(args);
+ res = _PyCFunction_FastCallDict(func, stack, nargs, kwds);
+ }
else {
if (kwds != NULL && PyDict_Size(kwds) != 0) {
PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
@@ -232,6 +237,25 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs,
break;
}
+ case METH_FASTCALL:
+ {
+ PyObject **stack;
+ PyObject *kwnames;
+ _PyCFunctionFast fastmeth = (_PyCFunctionFast)meth;
+
+ stack = _PyStack_UnpackDict(args, nargs, kwargs, &kwnames, func_obj);
+ if (stack == NULL) {
+ return NULL;
+ }
+
+ result = (*fastmeth) (self, stack, nargs, kwnames);
+ if (stack != args) {
+ PyMem_Free(stack);
+ }
+ Py_XDECREF(kwnames);
+ break;
+ }
+
default:
PyErr_SetString(PyExc_SystemError,
"Bad call flags in PyCFunction_Call. "
diff --git a/Python/getargs.c b/Python/getargs.c
index 0854cc4..5e85ea4 100644
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -1992,8 +1992,9 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
return cleanreturn(0, &freelist);
}
}
- else if (i < nargs)
+ else if (i < nargs) {
current_arg = PyTuple_GET_ITEM(args, i);
+ }
if (current_arg) {
msg = convertitem(current_arg, &format, p_va, flags,