#include "parts.h" #include // offsetof /* Test PEP 590 - Vectorcall */ static int fastcall_args(PyObject *args, PyObject ***stack, Py_ssize_t *nargs) { if (args == Py_None) { *stack = NULL; *nargs = 0; } else if (PyTuple_Check(args)) { *stack = ((PyTupleObject *)args)->ob_item; *nargs = PyTuple_GET_SIZE(args); } else { PyErr_SetString(PyExc_TypeError, "args must be None or a tuple"); return -1; } return 0; } static PyObject * test_pyobject_fastcall(PyObject *self, PyObject *args) { PyObject *func, *func_args; PyObject **stack; Py_ssize_t nargs; if (!PyArg_ParseTuple(args, "OO", &func, &func_args)) { return NULL; } if (fastcall_args(func_args, &stack, &nargs) < 0) { return NULL; } return _PyObject_FastCall(func, stack, nargs); } static PyObject * test_pyobject_fastcalldict(PyObject *self, PyObject *args) { PyObject *func, *func_args, *kwargs; PyObject **stack; Py_ssize_t nargs; if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwargs)) { return NULL; } if (fastcall_args(func_args, &stack, &nargs) < 0) { return NULL; } if (kwargs == Py_None) { kwargs = NULL; } else if (!PyDict_Check(kwargs)) { PyErr_SetString(PyExc_TypeError, "kwnames must be None or a dict"); return NULL; } return PyObject_VectorcallDict(func, stack, nargs, kwargs); } static PyObject * test_pyobject_vectorcall(PyObject *self, PyObject *args) { PyObject *func, *func_args, *kwnames = NULL; PyObject **stack; Py_ssize_t nargs, nkw; if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwnames)) { return NULL; } if (fastcall_args(func_args, &stack, &nargs) < 0) { return NULL; } if (kwnames == Py_None) { kwnames = NULL; } else if (PyTuple_Check(kwnames)) { nkw = PyTuple_GET_SIZE(kwnames); if (nargs < nkw) { PyErr_SetString(PyExc_ValueError, "kwnames longer than args"); return NULL; } nargs -= nkw; } else { PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple"); return NULL; } return PyObject_Vectorcall(func, stack, nargs, kwnames); } static PyObject * test_pyvectorcall_call(PyObject *self, PyObject *args) { PyObject *func; PyObject *argstuple; PyObject *kwargs = NULL; if (!PyArg_ParseTuple(args, "OO|O", &func, &argstuple, &kwargs)) { return NULL; } if (!PyTuple_Check(argstuple)) { PyErr_SetString(PyExc_TypeError, "args must be a tuple"); return NULL; } if (kwargs != NULL && !PyDict_Check(kwargs)) { PyErr_SetString(PyExc_TypeError, "kwargs must be a dict"); return NULL; } return PyVectorcall_Call(func, argstuple, kwargs); } static PyMethodDef TestMethods[] = { {"pyobject_fastcall", test_pyobject_fastcall, METH_VARARGS}, {"pyobject_fastcalldict", test_pyobject_fastcalldict, METH_VARARGS}, {"pyobject_vectorcall", test_pyobject_vectorcall, METH_VARARGS}, {"pyvectorcall_call", test_pyvectorcall_call, METH_VARARGS}, {NULL}, }; typedef struct { PyObject_HEAD vectorcallfunc vectorcall; } MethodDescriptorObject; static PyObject * MethodDescriptor_vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) { /* True if using the vectorcall function in MethodDescriptorObject * but False for MethodDescriptor2Object */ MethodDescriptorObject *md = (MethodDescriptorObject *)callable; return PyBool_FromLong(md->vectorcall != NULL); } static PyObject * MethodDescriptor_new(PyTypeObject* type, PyObject* args, PyObject *kw) { MethodDescriptorObject *op = (MethodDescriptorObject *)type->tp_alloc(type, 0); op->vectorcall = MethodDescriptor_vectorcall; return (PyObject *)op; } static PyObject * func_descr_get(PyObject *func, PyObject *obj, PyObject *type) { if (obj == Py_None || obj == NULL) { Py_INCREF(func); return func; } return PyMethod_New(func, obj); } static PyObject * nop_descr_get(PyObject *func, PyObject *obj, PyObject *type) { Py_INCREF(func); return func; } static PyObject * call_return_args(PyObject *self, PyObject *args, PyObject *kwargs) { Py_INCREF(args); return args; } static PyTypeObject MethodDescriptorBase_Type = { PyVarObject_HEAD_INIT(NULL, 0) "MethodDescriptorBase", sizeof(MethodDescriptorObject), .tp_new = MethodDescriptor_new, .tp_call = PyVectorcall_Call, .tp_vectorcall_offset = offsetof(MethodDescriptorObject, vectorcall), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_METHOD_DESCRIPTOR | Py_TPFLAGS_HAVE_VECTORCALL, .tp_descr_get = func_descr_get, }; static PyTypeObject MethodDescriptorDerived_Type = { PyVarObject_HEAD_INIT(NULL, 0) "MethodDescriptorDerived", .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, }; static PyTypeObject MethodDescriptorNopGet_Type = { PyVarObject_HEAD_INIT(NULL, 0) "MethodDescriptorNopGet", .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_call = call_return_args, .tp_descr_get = nop_descr_get, }; typedef struct { MethodDescriptorObject base; vectorcallfunc vectorcall; } MethodDescriptor2Object; static PyObject * MethodDescriptor2_new(PyTypeObject* type, PyObject* args, PyObject *kw) { MethodDescriptor2Object *op = PyObject_New(MethodDescriptor2Object, type); op->base.vectorcall = NULL; op->vectorcall = MethodDescriptor_vectorcall; return (PyObject *)op; } static PyTypeObject MethodDescriptor2_Type = { PyVarObject_HEAD_INIT(NULL, 0) "MethodDescriptor2", sizeof(MethodDescriptor2Object), .tp_new = MethodDescriptor2_new, .tp_call = PyVectorcall_Call, .tp_vectorcall_offset = offsetof(MethodDescriptor2Object, vectorcall), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_VECTORCALL, }; int _PyTestCapi_Init_Vectorcall(PyObject *m) { if (PyModule_AddFunctions(m, TestMethods) < 0) { return -1; } if (PyType_Ready(&MethodDescriptorBase_Type) < 0) { return -1; } if (PyModule_AddType(m, &MethodDescriptorBase_Type) < 0) { return -1; } MethodDescriptorDerived_Type.tp_base = &MethodDescriptorBase_Type; if (PyType_Ready(&MethodDescriptorDerived_Type) < 0) { return -1; } if (PyModule_AddType(m, &MethodDescriptorDerived_Type) < 0) { return -1; } MethodDescriptorNopGet_Type.tp_base = &MethodDescriptorBase_Type; if (PyType_Ready(&MethodDescriptorNopGet_Type) < 0) { return -1; } if (PyModule_AddType(m, &MethodDescriptorNopGet_Type) < 0) { return -1; } MethodDescriptor2_Type.tp_base = &MethodDescriptorBase_Type; if (PyType_Ready(&MethodDescriptor2_Type) < 0) { return -1; } if (PyModule_AddType(m, &MethodDescriptor2_Type) < 0) { return -1; } return 0; }