summaryrefslogtreecommitdiffstats
path: root/Objects/descrobject.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/descrobject.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/descrobject.c')
-rw-r--r--Objects/descrobject.c246
1 files changed, 196 insertions, 50 deletions
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index 4b98578..99855d8 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -226,80 +226,199 @@ getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value)
return -1;
}
-static PyObject *
-methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwargs)
-{
- Py_ssize_t nargs;
- PyObject *self, *result;
- /* Make sure that the first argument is acceptable as 'self' */
- assert(PyTuple_Check(args));
- nargs = PyTuple_GET_SIZE(args);
+/* Vectorcall functions for each of the PyMethodDescr calling conventions.
+ *
+ * First, common helpers
+ */
+static const char *
+get_name(PyObject *func) {
+ assert(PyObject_TypeCheck(func, &PyMethodDescr_Type));
+ return ((PyMethodDescrObject *)func)->d_method->ml_name;
+}
+
+typedef void (*funcptr)(void);
+
+static inline int
+method_check_args(PyObject *func, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ assert(!PyErr_Occurred());
+ assert(PyObject_TypeCheck(func, &PyMethodDescr_Type));
if (nargs < 1) {
PyErr_Format(PyExc_TypeError,
- "descriptor '%V' of '%.100s' "
+ "descriptor '%.200s' of '%.100s' "
"object needs an argument",
- descr_name((PyDescrObject *)descr), "?",
- PyDescr_TYPE(descr)->tp_name);
- return NULL;
+ get_name(func), PyDescr_TYPE(func)->tp_name);
+ return -1;
}
- self = PyTuple_GET_ITEM(args, 0);
+ PyObject *self = args[0];
if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self),
- (PyObject *)PyDescr_TYPE(descr))) {
+ (PyObject *)PyDescr_TYPE(func)))
+ {
PyErr_Format(PyExc_TypeError,
- "descriptor '%V' for '%.100s' objects "
+ "descriptor '%.200s' for '%.100s' objects "
"doesn't apply to a '%.100s' object",
- descr_name((PyDescrObject *)descr), "?",
- PyDescr_TYPE(descr)->tp_name,
- self->ob_type->tp_name);
+ get_name(func), PyDescr_TYPE(func)->tp_name,
+ Py_TYPE(self)->tp_name);
+ return -1;
+ }
+ 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
+method_enter_call(PyObject *func)
+{
+ if (Py_EnterRecursiveCall(" while calling a Python object")) {
return NULL;
}
+ return (funcptr)((PyMethodDescrObject *)func)->d_method->ml_meth;
+}
- result = _PyMethodDef_RawFastCallDict(descr->d_method, self,
- &_PyTuple_ITEMS(args)[1], nargs - 1,
- kwargs);
- result = _Py_CheckFunctionResult((PyObject *)descr, result, NULL);
+/* Now the actual vectorcall functions */
+static PyObject *
+method_vectorcall_VARARGS(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (method_check_args(func, args, nargs, kwnames)) {
+ return NULL;
+ }
+ PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1);
+ if (argstuple == NULL) {
+ return NULL;
+ }
+ PyCFunction meth = (PyCFunction)method_enter_call(func);
+ if (meth == NULL) {
+ Py_DECREF(argstuple);
+ return NULL;
+ }
+ PyObject *result = meth(args[0], argstuple);
+ Py_DECREF(argstuple);
+ Py_LeaveRecursiveCall();
return result;
}
-// same to methoddescr_call(), but use FASTCALL convention.
-PyObject *
-_PyMethodDescr_Vectorcall(PyObject *descrobj,
- PyObject *const *args, size_t nargsf,
- PyObject *kwnames)
+static PyObject *
+method_vectorcall_VARARGS_KEYWORDS(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
{
- assert(Py_TYPE(descrobj) == &PyMethodDescr_Type);
- PyMethodDescrObject *descr = (PyMethodDescrObject *)descrobj;
- PyObject *self, *result;
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (method_check_args(func, args, nargs, NULL)) {
+ return NULL;
+ }
+ PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1);
+ if (argstuple == NULL) {
+ return NULL;
+ }
+ PyObject *result = NULL;
+ /* Create a temporary dict for keyword arguments */
+ PyObject *kwdict = NULL;
+ if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) > 0) {
+ kwdict = _PyStack_AsDict(args + nargs, kwnames);
+ if (kwdict == NULL) {
+ goto exit;
+ }
+ }
+ PyCFunctionWithKeywords meth = (PyCFunctionWithKeywords)
+ method_enter_call(func);
+ if (meth == NULL) {
+ goto exit;
+ }
+ result = meth(args[0], argstuple, kwdict);
+ Py_LeaveRecursiveCall();
+exit:
+ Py_DECREF(argstuple);
+ Py_XDECREF(kwdict);
+ return result;
+}
+
+static PyObject *
+method_vectorcall_FASTCALL(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (method_check_args(func, args, nargs, kwnames)) {
+ return NULL;
+ }
+ _PyCFunctionFast meth = (_PyCFunctionFast)
+ method_enter_call(func);
+ if (meth == NULL) {
+ return NULL;
+ }
+ PyObject *result = meth(args[0], args+1, nargs-1);
+ Py_LeaveRecursiveCall();
+ return result;
+}
+static PyObject *
+method_vectorcall_FASTCALL_KEYWORDS(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
- /* Make sure that the first argument is acceptable as 'self' */
- if (nargs < 1) {
- PyErr_Format(PyExc_TypeError,
- "descriptor '%V' of '%.100s' "
- "object needs an argument",
- descr_name((PyDescrObject *)descr), "?",
- PyDescr_TYPE(descr)->tp_name);
+ if (method_check_args(func, args, nargs, NULL)) {
return NULL;
}
- self = args[0];
- if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self),
- (PyObject *)PyDescr_TYPE(descr))) {
+ _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords)
+ method_enter_call(func);
+ if (meth == NULL) {
+ return NULL;
+ }
+ PyObject *result = meth(args[0], args+1, nargs-1, kwnames);
+ Py_LeaveRecursiveCall();
+ return result;
+}
+
+static PyObject *
+method_vectorcall_NOARGS(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (method_check_args(func, args, nargs, kwnames)) {
+ return NULL;
+ }
+ if (nargs != 1) {
PyErr_Format(PyExc_TypeError,
- "descriptor '%V' for '%.100s' objects "
- "doesn't apply to a '%.100s' object",
- descr_name((PyDescrObject *)descr), "?",
- PyDescr_TYPE(descr)->tp_name,
- self->ob_type->tp_name);
+ "%.200s() takes no arguments (%zd given)", get_name(func), nargs-1);
+ return NULL;
+ }
+ PyCFunction meth = (PyCFunction)method_enter_call(func);
+ if (meth == NULL) {
return NULL;
}
+ PyObject *result = meth(args[0], NULL);
+ Py_LeaveRecursiveCall();
+ return result;
+}
- result = _PyMethodDef_RawFastCallKeywords(descr->d_method, self,
- args+1, nargs-1, kwnames);
- result = _Py_CheckFunctionResult((PyObject *)descr, result, NULL);
+static PyObject *
+method_vectorcall_O(
+ PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (method_check_args(func, args, nargs, kwnames)) {
+ return NULL;
+ }
+ if (nargs != 2) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes exactly one argument (%zd given)",
+ get_name(func), nargs-1);
+ return NULL;
+ }
+ PyCFunction meth = (PyCFunction)method_enter_call(func);
+ if (meth == NULL) {
+ return NULL;
+ }
+ PyObject *result = meth(args[0], args[1]);
+ Py_LeaveRecursiveCall();
return result;
}
+
/* Instances of classmethod_descriptor are unlikely to be called directly.
For one, the analogous class "classmethod" (for Python classes) is not
callable. Second, users are not likely to access a classmethod_descriptor
@@ -540,7 +659,7 @@ PyTypeObject PyMethodDescr_Type = {
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
- (ternaryfunc)methoddescr_call, /* tp_call */
+ PyVectorcall_Call, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
@@ -738,13 +857,40 @@ descr_new(PyTypeObject *descrtype, PyTypeObject *type, const char *name)
PyObject *
PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method)
{
+ /* Figure out correct vectorcall function to use */
+ vectorcallfunc vectorcall;
+ switch (method->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS))
+ {
+ case METH_VARARGS:
+ vectorcall = method_vectorcall_VARARGS;
+ break;
+ case METH_VARARGS | METH_KEYWORDS:
+ vectorcall = method_vectorcall_VARARGS_KEYWORDS;
+ break;
+ case METH_FASTCALL:
+ vectorcall = method_vectorcall_FASTCALL;
+ break;
+ case METH_FASTCALL | METH_KEYWORDS:
+ vectorcall = method_vectorcall_FASTCALL_KEYWORDS;
+ break;
+ case METH_NOARGS:
+ vectorcall = method_vectorcall_NOARGS;
+ break;
+ case METH_O:
+ vectorcall = method_vectorcall_O;
+ break;
+ default:
+ PyErr_SetString(PyExc_SystemError, "bad call flags");
+ return NULL;
+ }
+
PyMethodDescrObject *descr;
descr = (PyMethodDescrObject *)descr_new(&PyMethodDescr_Type,
type, method->ml_name);
if (descr != NULL) {
descr->d_method = method;
- descr->vectorcall = _PyMethodDescr_Vectorcall;
+ descr->vectorcall = vectorcall;
}
return (PyObject *)descr;
}