diff --git a/Makefile.pre.in b/Makefile.pre.in index 84c783327ed..c17c3d6067d 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2568,7 +2568,7 @@ MODULE__SHA3_DEPS=$(srcdir)/Modules/_sha3/sha3.c $(srcdir)/Modules/_sha3/sha3.h MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h MODULE__SSL_DEPS=$(srcdir)/Modules/_ssl.h $(srcdir)/Modules/_ssl/cert.c $(srcdir)/Modules/_ssl/debughelpers.c $(srcdir)/Modules/_ssl/misc.c $(srcdir)/Modules/_ssl_data.h $(srcdir)/Modules/_ssl_data_111.h $(srcdir)/Modules/_ssl_data_300.h $(srcdir)/Modules/socketmodule.h -MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/testcapi_long.h +MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/testcapi_long.h Modules/_testcapi/parts.h $(srcdir)/Modules/_testcapi/vectorcall.c MODULE__SQLITE3_DEPS=$(srcdir)/Modules/_sqlite/connection.h $(srcdir)/Modules/_sqlite/cursor.h $(srcdir)/Modules/_sqlite/microprotocols.h $(srcdir)/Modules/_sqlite/module.h $(srcdir)/Modules/_sqlite/prepare_protocol.h $(srcdir)/Modules/_sqlite/row.h $(srcdir)/Modules/_sqlite/util.h # IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 7357aacd726..581a5c7f979 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -168,7 +168,7 @@ @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c # Some testing modules MUST be built as shared libraries. *shared* diff --git a/Modules/_testcapi/README.txt b/Modules/_testcapi/README.txt new file mode 100644 index 00000000000..134b6efc638 --- /dev/null +++ b/Modules/_testcapi/README.txt @@ -0,0 +1,3 @@ +Tests in this directory are compiled into the _testcapi extension. +The main file for the extension is Modules/_testcapimodule.c, which +calls `_PyTestCapi_Init_*` from these functions. diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h new file mode 100644 index 00000000000..1111021e185 --- /dev/null +++ b/Modules/_testcapi/parts.h @@ -0,0 +1,3 @@ +#include "Python.h" + +PyAPI_FUNC(int) _PyTestCapi_Init_Vectorcall(PyObject *module); diff --git a/Modules/_testcapi/vectorcall.c b/Modules/_testcapi/vectorcall.c new file mode 100644 index 00000000000..9bc702905ca --- /dev/null +++ b/Modules/_testcapi/vectorcall.c @@ -0,0 +1,270 @@ +#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; +} diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 7d88f4f51f8..436c701a7a3 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -43,6 +43,10 @@ # error "The public headers should not include , see bpo-46748" #endif +// Several parts of this module are broken out into files in _testcapi/. +// Include definitions from there. +#include "_testcapi/parts.h" + // Forward declarations static struct PyModuleDef _testcapimodule; static PyType_Spec HeapTypeNameType_Spec; @@ -5299,128 +5303,6 @@ raise_SIGINT_then_send_None(PyObject *self, PyObject *args) } -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 PyObject* stack_pointer(PyObject *self, PyObject *args) { @@ -6403,10 +6285,6 @@ static PyMethodDef TestMethods[] = { {"tracemalloc_get_traceback", tracemalloc_get_traceback, METH_VARARGS}, {"dict_get_version", dict_get_version, METH_VARARGS}, {"raise_SIGINT_then_send_None", raise_SIGINT_then_send_None, METH_VARARGS}, - {"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}, {"stack_pointer", stack_pointer, METH_NOARGS}, #ifdef W_STOPCODE {"W_STOPCODE", py_w_stopcode, METH_VARARGS}, @@ -7009,106 +6887,6 @@ static PyTypeObject Generic_Type = { .tp_methods = generic_methods, }; - -/* Test PEP 590 */ - -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, -}; - PyDoc_STRVAR(heapdocctype__doc__, "HeapDocCType(arg1, arg2)\n" "--\n" @@ -7778,29 +7556,6 @@ PyInit__testcapi(void) Py_INCREF(&MyList_Type); PyModule_AddObject(m, "MyList", (PyObject *)&MyList_Type); - if (PyType_Ready(&MethodDescriptorBase_Type) < 0) - return NULL; - Py_INCREF(&MethodDescriptorBase_Type); - PyModule_AddObject(m, "MethodDescriptorBase", (PyObject *)&MethodDescriptorBase_Type); - - MethodDescriptorDerived_Type.tp_base = &MethodDescriptorBase_Type; - if (PyType_Ready(&MethodDescriptorDerived_Type) < 0) - return NULL; - Py_INCREF(&MethodDescriptorDerived_Type); - PyModule_AddObject(m, "MethodDescriptorDerived", (PyObject *)&MethodDescriptorDerived_Type); - - MethodDescriptorNopGet_Type.tp_base = &MethodDescriptorBase_Type; - if (PyType_Ready(&MethodDescriptorNopGet_Type) < 0) - return NULL; - Py_INCREF(&MethodDescriptorNopGet_Type); - PyModule_AddObject(m, "MethodDescriptorNopGet", (PyObject *)&MethodDescriptorNopGet_Type); - - MethodDescriptor2_Type.tp_base = &MethodDescriptorBase_Type; - if (PyType_Ready(&MethodDescriptor2_Type) < 0) - return NULL; - Py_INCREF(&MethodDescriptor2_Type); - PyModule_AddObject(m, "MethodDescriptor2", (PyObject *)&MethodDescriptor2_Type); - if (PyType_Ready(&GenericAlias_Type) < 0) return NULL; Py_INCREF(&GenericAlias_Type); @@ -7973,6 +7728,11 @@ PyInit__testcapi(void) (PyObject *) &ContainerNoGC_type) < 0) return NULL; + /* Include tests from the _testcapi/ directory */ + if (_PyTestCapi_Init_Vectorcall(m) < 0) { + return NULL; + } + PyState_AddModule(m, &_testcapimodule); return m; } @@ -8024,7 +7784,6 @@ test_buildvalue_issue38913(PyObject *self, PyObject *Py_UNUSED(ignored)) } PyErr_Clear(); - Py_RETURN_NONE; } diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index c1a19437253..07e23557e1d 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -94,6 +94,7 @@ + diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters index 53f64b7aa1e..82fce057e2e 100644 --- a/PCbuild/_testcapi.vcxproj.filters +++ b/PCbuild/_testcapi.vcxproj.filters @@ -12,6 +12,9 @@ Source Files + + Source Files + diff --git a/setup.py b/setup.py index 2edcb08b4fd..7c71ac6ec64 100644 --- a/setup.py +++ b/setup.py @@ -994,7 +994,7 @@ class PyBuildExt(build_ext): def detect_test_extensions(self): # Python C API test module - self.addext(Extension('_testcapi', ['_testcapimodule.c'])) + self.addext(Extension('_testcapi', ['_testcapimodule.c', '_testcapi/vectorcall.c'])) # Python Internal C API test module self.addext(Extension('_testinternalcapi', ['_testinternalcapi.c']))