| // clinic/vectorcall.c.h uses internal pycore_modsupport.h API | |
| #define PYTESTCAPI_NEED_INTERNAL_API | |
| #include "parts.h" | |
| #include "clinic/vectorcall.c.h" | |
| #include <stddef.h> // offsetof | |
| /*[clinic input] | |
| module _testcapi | |
| [clinic start generated code]*/ | |
| /*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/ | |
| /* 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; | |
| } | |
| /*[clinic input] | |
| _testcapi.pyobject_fastcalldict | |
| func: object | |
| func_args: object | |
| kwargs: object | |
| / | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| _testcapi_pyobject_fastcalldict_impl(PyObject *module, PyObject *func, | |
| PyObject *func_args, PyObject *kwargs) | |
| /*[clinic end generated code: output=35902ece94de4418 input=b9c0196ca7d5f9e4]*/ | |
| { | |
| PyObject **stack; | |
| Py_ssize_t nargs; | |
| 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); | |
| } | |
| /*[clinic input] | |
| _testcapi.pyobject_vectorcall | |
| func: object | |
| func_args: object | |
| kwnames: object | |
| / | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| _testcapi_pyobject_vectorcall_impl(PyObject *module, PyObject *func, | |
| PyObject *func_args, PyObject *kwnames) | |
| /*[clinic end generated code: output=ff77245bc6afe0d8 input=a0668dfef625764c]*/ | |
| { | |
| PyObject **stack; | |
| Py_ssize_t nargs, nkw; | |
| 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 * | |
| override_vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, | |
| PyObject *kwnames) | |
| { | |
| return PyUnicode_FromString("overridden"); | |
| } | |
| static PyObject * | |
| function_setvectorcall(PyObject *self, PyObject *func) | |
| { | |
| if (!PyFunction_Check(func)) { | |
| PyErr_SetString(PyExc_TypeError, "'func' must be a function"); | |
| return NULL; | |
| } | |
| PyFunction_SetVectorcall((PyFunctionObject *)func, (vectorcallfunc)override_vectorcall); | |
| Py_RETURN_NONE; | |
| } | |
| /*[clinic input] | |
| _testcapi.pyvectorcall_call | |
| func: object | |
| argstuple: object | |
| kwargs: object = NULL | |
| / | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| _testcapi_pyvectorcall_call_impl(PyObject *module, PyObject *func, | |
| PyObject *argstuple, PyObject *kwargs) | |
| /*[clinic end generated code: output=809046fe78511306 input=4376ee7cabd698ce]*/ | |
| { | |
| 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); | |
| } | |
| PyObject * | |
| VectorCallClass_tpcall(PyObject *self, PyObject *args, PyObject *kwargs) { | |
| return PyUnicode_FromString("tp_call"); | |
| } | |
| PyObject * | |
| VectorCallClass_vectorcall(PyObject *callable, | |
| PyObject *const *args, | |
| size_t nargsf, | |
| PyObject *kwnames) { | |
| return PyUnicode_FromString("vectorcall"); | |
| } | |
| /*[clinic input] | |
| class _testcapi.VectorCallClass "PyObject *" "&PyType_Type" | |
| [clinic start generated code]*/ | |
| /*[clinic end generated code: output=da39a3ee5e6b4b0d input=95c63c1a47f9a995]*/ | |
| /*[clinic input] | |
| @permit_long_summary | |
| _testcapi.VectorCallClass.set_vectorcall | |
| type: object(subclass_of="&PyType_Type", type="PyTypeObject *") | |
| / | |
| Set self's vectorcall function for `type` to one that returns "vectorcall" | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| _testcapi_VectorCallClass_set_vectorcall_impl(PyObject *self, | |
| PyTypeObject *type) | |
| /*[clinic end generated code: output=b37f0466f15da903 input=170fefc7ee77fd36]*/ | |
| { | |
| if (!PyObject_TypeCheck(self, type)) { | |
| return PyErr_Format( | |
| PyExc_TypeError, | |
| "expected %N instance", | |
| type); | |
| } | |
| if (!type->tp_vectorcall_offset) { | |
| return PyErr_Format( | |
| PyExc_TypeError, | |
| "type %N has no vectorcall offset", | |
| type); | |
| } | |
| *(vectorcallfunc*)((char*)self + type->tp_vectorcall_offset) = ( | |
| VectorCallClass_vectorcall); | |
| Py_RETURN_NONE; | |
| } | |
| PyMethodDef VectorCallClass_methods[] = { | |
| _TESTCAPI_VECTORCALLCLASS_SET_VECTORCALL_METHODDEF | |
| {NULL, NULL} | |
| }; | |
| PyMemberDef VectorCallClass_members[] = { | |
| {"__vectorcalloffset__", Py_T_PYSSIZET, 0/* set later */, Py_READONLY}, | |
| {NULL} | |
| }; | |
| PyType_Slot VectorCallClass_slots[] = { | |
| {Py_tp_call, VectorCallClass_tpcall}, | |
| {Py_tp_members, VectorCallClass_members}, | |
| {Py_tp_methods, VectorCallClass_methods}, | |
| {0}, | |
| }; | |
| /*[clinic input] | |
| _testcapi.make_vectorcall_class | |
| base: object(subclass_of="&PyType_Type", type="PyTypeObject *") = NULL | |
| / | |
| Create a class whose instances return "tpcall" when called. | |
| When the "set_vectorcall" method is called on an instance, a vectorcall | |
| function that returns "vectorcall" will be installed. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| _testcapi_make_vectorcall_class_impl(PyObject *module, PyTypeObject *base) | |
| /*[clinic end generated code: output=16dcfc3062ddf968 input=f72e01ccf52de2b4]*/ | |
| { | |
| if (!base) { | |
| base = (PyTypeObject *)&PyBaseObject_Type; | |
| } | |
| VectorCallClass_members[0].offset = base->tp_basicsize; | |
| PyType_Spec spec = { | |
| .name = "_testcapi.VectorcallClass", | |
| .basicsize = (int)(base->tp_basicsize + sizeof(vectorcallfunc)), | |
| .flags = Py_TPFLAGS_DEFAULT | |
| | Py_TPFLAGS_HAVE_VECTORCALL | |
| | Py_TPFLAGS_BASETYPE, | |
| .slots = VectorCallClass_slots, | |
| }; | |
| return PyType_FromSpecWithBases(&spec, (PyObject *)base); | |
| } | |
| /*[clinic input] | |
| _testcapi.has_vectorcall_flag -> bool | |
| type: object(subclass_of="&PyType_Type", type="PyTypeObject *") | |
| / | |
| Return true iff Py_TPFLAGS_HAVE_VECTORCALL is set on the class. | |
| [clinic start generated code]*/ | |
| static int | |
| _testcapi_has_vectorcall_flag_impl(PyObject *module, PyTypeObject *type) | |
| /*[clinic end generated code: output=3ae8d1374388c671 input=8eee492ac548749e]*/ | |
| { | |
| return PyType_HasFeature(type, Py_TPFLAGS_HAVE_VECTORCALL); | |
| } | |
| static PyMethodDef TestMethods[] = { | |
| _TESTCAPI_PYOBJECT_FASTCALLDICT_METHODDEF | |
| _TESTCAPI_PYOBJECT_VECTORCALL_METHODDEF | |
| {"function_setvectorcall", function_setvectorcall, METH_O}, | |
| _TESTCAPI_PYVECTORCALL_CALL_METHODDEF | |
| _TESTCAPI_MAKE_VECTORCALL_CLASS_METHODDEF | |
| _TESTCAPI_HAS_VECTORCALL_FLAG_METHODDEF | |
| {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) { | |
| return Py_NewRef(func); | |
| } | |
| return PyMethod_New(func, obj); | |
| } | |
| static PyObject * | |
| nop_descr_get(PyObject *func, PyObject *obj, PyObject *type) | |
| { | |
| return Py_NewRef(func); | |
| } | |
| static PyObject * | |
| call_return_args(PyObject *self, PyObject *args, PyObject *kwargs) | |
| { | |
| return Py_NewRef(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); | |
| if (op == NULL) { | |
| return NULL; | |
| } | |
| 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; | |
| } |