diff options
author | Jeroen Demeyer <J.Demeyer@UGent.be> | 2019-07-05 12:48:24 (GMT) |
---|---|---|
committer | Petr Viktorin <encukou@gmail.com> | 2019-07-05 12:48:24 (GMT) |
commit | 0d722f3cd602e5f5492f9c65c8af57ea9d3743b6 (patch) | |
tree | e64e73f12cbad55824a13f3e7871051b9b34d98c /Objects/descrobject.c | |
parent | 6e43d07324ca799118e805751a10a7eff71d5a04 (diff) | |
download | cpython-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.c | 246 |
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; } |