summaryrefslogtreecommitdiffstats
path: root/Objects/methodobject.c
diff options
context:
space:
mode:
authorJeroen Demeyer <J.Demeyer@UGent.be>2019-07-05 12:48:24 (GMT)
committerPetr Viktorin <encukou@gmail.com>2019-07-05 12:48:24 (GMT)
commit0d722f3cd602e5f5492f9c65c8af57ea9d3743b6 (patch)
treee64e73f12cbad55824a13f3e7871051b9b34d98c /Objects/methodobject.c
parent6e43d07324ca799118e805751a10a7eff71d5a04 (diff)
downloadcpython-0d722f3cd602e5f5492f9c65c8af57ea9d3743b6.zip
cpython-0d722f3cd602e5f5492f9c65c8af57ea9d3743b6.tar.gz
cpython-0d722f3cd602e5f5492f9c65c8af57ea9d3743b6.tar.bz2
bpo-36974: separate vectorcall functions for each calling convention (GH-13781)
Diffstat (limited to 'Objects/methodobject.c')
-rw-r--r--Objects/methodobject.c165
1 files changed, 157 insertions, 8 deletions
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
index c3bc018..3494f11 100644
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -19,6 +19,17 @@ static int numfree = 0;
/* undefine macro trampoline to PyCFunction_NewEx */
#undef PyCFunction_New
+/* Forward declarations */
+static PyObject * cfunction_vectorcall_FASTCALL(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject * cfunction_vectorcall_FASTCALL_KEYWORDS(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject * cfunction_vectorcall_NOARGS(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject * cfunction_vectorcall_O(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+
+
PyObject *
PyCFunction_New(PyMethodDef *ml, PyObject *self)
{
@@ -28,6 +39,33 @@ PyCFunction_New(PyMethodDef *ml, PyObject *self)
PyObject *
PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module)
{
+ /* Figure out correct vectorcall function to use */
+ vectorcallfunc vectorcall;
+ switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS))
+ {
+ case METH_VARARGS:
+ case METH_VARARGS | METH_KEYWORDS:
+ /* For METH_VARARGS functions, it's more efficient to use tp_call
+ * instead of vectorcall. */
+ vectorcall = NULL;
+ break;
+ case METH_FASTCALL:
+ vectorcall = cfunction_vectorcall_FASTCALL;
+ break;
+ case METH_FASTCALL | METH_KEYWORDS:
+ vectorcall = cfunction_vectorcall_FASTCALL_KEYWORDS;
+ break;
+ case METH_NOARGS:
+ vectorcall = cfunction_vectorcall_NOARGS;
+ break;
+ case METH_O:
+ vectorcall = cfunction_vectorcall_O;
+ break;
+ default:
+ PyErr_SetString(PyExc_SystemError, "bad call flags");
+ return NULL;
+ }
+
PyCFunctionObject *op;
op = free_list;
if (op != NULL) {
@@ -46,14 +84,7 @@ PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module)
op->m_self = self;
Py_XINCREF(module);
op->m_module = module;
- if (ml->ml_flags & METH_VARARGS) {
- /* For METH_VARARGS functions, it's more efficient to use tp_call
- * instead of vectorcall. */
- op->vectorcall = NULL;
- }
- else {
- op->vectorcall = _PyCFunction_Vectorcall;
- }
+ op->vectorcall = vectorcall;
_PyObject_GC_TRACK(op);
return (PyObject *)op;
}
@@ -333,3 +364,121 @@ _PyCFunction_DebugMallocStats(FILE *out)
"free PyCFunctionObject",
numfree, sizeof(PyCFunctionObject));
}
+
+
+/* Vectorcall functions for each of the PyCFunction calling conventions,
+ * except for METH_VARARGS (possibly combined with METH_KEYWORDS) which
+ * doesn't use vectorcall.
+ *
+ * First, common helpers
+ */
+static const char *
+get_name(PyObject *func)
+{
+ assert(PyCFunction_Check(func));
+ PyMethodDef *method = ((PyCFunctionObject *)func)->m_ml;
+ return method->ml_name;
+}
+
+typedef void (*funcptr)(void);
+
+static inline int
+cfunction_check_kwargs(PyObject *func, PyObject *kwnames)
+{
+ assert(!PyErr_Occurred());
+ assert(PyCFunction_Check(func));
+ if (kwnames && PyTuple_GET_SIZE(kwnames)) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes no keyword arguments", get_name(func));
+ return -1;
+ }
+ return 0;
+}
+
+static inline funcptr
+cfunction_enter_call(PyObject *func)
+{
+ if (Py_EnterRecursiveCall(" while calling a Python object")) {
+ return NULL;
+ }
+ return (funcptr)PyCFunction_GET_FUNCTION(func);
+}
+
+/* Now the actual vectorcall functions */
+static PyObject *
+cfunction_vectorcall_FASTCALL(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ if (cfunction_check_kwargs(func, kwnames)) {
+ return NULL;
+ }
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ _PyCFunctionFast meth = (_PyCFunctionFast)
+ cfunction_enter_call(func);
+ if (meth == NULL) {
+ return NULL;
+ }
+ PyObject *result = meth(PyCFunction_GET_SELF(func), args, nargs);
+ Py_LeaveRecursiveCall();
+ return result;
+}
+
+static PyObject *
+cfunction_vectorcall_FASTCALL_KEYWORDS(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords)
+ cfunction_enter_call(func);
+ if (meth == NULL) {
+ return NULL;
+ }
+ PyObject *result = meth(PyCFunction_GET_SELF(func), args, nargs, kwnames);
+ Py_LeaveRecursiveCall();
+ return result;
+}
+
+static PyObject *
+cfunction_vectorcall_NOARGS(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ if (cfunction_check_kwargs(func, kwnames)) {
+ return NULL;
+ }
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (nargs != 0) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes no arguments (%zd given)", get_name(func), nargs);
+ return NULL;
+ }
+ PyCFunction meth = (PyCFunction)cfunction_enter_call(func);
+ if (meth == NULL) {
+ return NULL;
+ }
+ PyObject *result = meth(PyCFunction_GET_SELF(func), NULL);
+ Py_LeaveRecursiveCall();
+ return result;
+}
+
+static PyObject *
+cfunction_vectorcall_O(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ if (cfunction_check_kwargs(func, kwnames)) {
+ return NULL;
+ }
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (nargs != 1) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes exactly one argument (%zd given)",
+ get_name(func), nargs);
+ return NULL;
+ }
+ PyCFunction meth = (PyCFunction)cfunction_enter_call(func);
+ if (meth == NULL) {
+ return NULL;
+ }
+ PyObject *result = meth(PyCFunction_GET_SELF(func), args[0]);
+ Py_LeaveRecursiveCall();
+ return result;
+}