From d9bcdda39c62a8c37637ecd5f82f83f6e8828243 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 7 Mar 2024 19:31:12 +0100 Subject: gh-116417: Add _testlimitedcapi C extension (#116419) Add a new C extension "_testlimitedcapi" which is only built with the limited C API. Move heaptype_relative.c and vectorcall_limited.c from Modules/_testcapi/ to Modules/_testlimitedcapi/. * configure: add _testlimitedcapi test extension. * Update generate_stdlib_module_names.py. * Update make check-c-globals. Co-authored-by: Erlend E. Aasland --- Lib/test/support/__init__.py | 3 +- Lib/test/test_call.py | 10 +- Lib/test/test_capi/test_misc.py | 54 ++-- Modules/Setup.stdlib.in | 3 +- Modules/_testcapi/clinic/vectorcall_limited.c.h | 20 -- Modules/_testcapi/heaptype_relative.c | 344 --------------------- Modules/_testcapi/parts.h | 3 - Modules/_testcapi/vectorcall_limited.c | 200 ------------ Modules/_testcapimodule.c | 6 - Modules/_testlimitedcapi.c | 36 +++ .../_testlimitedcapi/clinic/vectorcall_limited.c.h | 20 ++ Modules/_testlimitedcapi/heaptype_relative.c | 338 ++++++++++++++++++++ Modules/_testlimitedcapi/parts.h | 27 ++ Modules/_testlimitedcapi/vectorcall_limited.c | 194 ++++++++++++ PCbuild/_testcapi.vcxproj | 2 - PCbuild/_testlimitedcapi.vcxproj | 116 +++++++ PCbuild/_testlimitedcapi.vcxproj.filters | 25 ++ PCbuild/pcbuild.proj | 2 +- PCbuild/pcbuild.sln | 38 ++- PCbuild/readme.txt | 50 +-- Tools/build/generate_stdlib_module_names.py | 1 + Tools/c-analyzer/c_parser/preprocessor/gcc.py | 4 +- Tools/msi/test/test_files.wxs | 2 +- configure | 45 +++ configure.ac | 2 + 25 files changed, 915 insertions(+), 630 deletions(-) delete mode 100644 Modules/_testcapi/clinic/vectorcall_limited.c.h delete mode 100644 Modules/_testcapi/heaptype_relative.c delete mode 100644 Modules/_testcapi/vectorcall_limited.c create mode 100644 Modules/_testlimitedcapi.c create mode 100644 Modules/_testlimitedcapi/clinic/vectorcall_limited.c.h create mode 100644 Modules/_testlimitedcapi/heaptype_relative.c create mode 100644 Modules/_testlimitedcapi/parts.h create mode 100644 Modules/_testlimitedcapi/vectorcall_limited.c create mode 100644 PCbuild/_testlimitedcapi.vcxproj create mode 100644 PCbuild/_testlimitedcapi.vcxproj.filters diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 14e3766..505d4be 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1153,8 +1153,9 @@ def refcount_test(test): def requires_limited_api(test): try: import _testcapi + import _testlimitedcapi except ImportError: - return unittest.skip('needs _testcapi module')(test) + return unittest.skip('needs _testcapi and _testlimitedcapi modules')(test) return test def requires_specialization(test): diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 2a6a5d2..eaf9195 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -5,6 +5,10 @@ try: import _testcapi except ImportError: _testcapi = None +try: + import _testlimitedcapi +except ImportError: + _testlimitedcapi = None import struct import collections import itertools @@ -837,12 +841,12 @@ class TestPEP590(unittest.TestCase): @requires_limited_api def test_vectorcall_limited_incoming(self): from _testcapi import pyobject_vectorcall - obj = _testcapi.LimitedVectorCallClass() + obj = _testlimitedcapi.LimitedVectorCallClass() self.assertEqual(pyobject_vectorcall(obj, (), ()), "vectorcall called") @requires_limited_api def test_vectorcall_limited_outgoing(self): - from _testcapi import call_vectorcall + from _testlimitedcapi import call_vectorcall args_captured = [] kwargs_captured = [] @@ -858,7 +862,7 @@ class TestPEP590(unittest.TestCase): @requires_limited_api def test_vectorcall_limited_outgoing_method(self): - from _testcapi import call_vectorcall_method + from _testlimitedcapi import call_vectorcall_method args_captured = [] kwargs_captured = [] diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 67fbef4..64c0da6 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -47,6 +47,7 @@ except ModuleNotFoundError: # Skip this test if the _testcapi module isn't available. _testcapi = import_helper.import_module('_testcapi') +import _testlimitedcapi import _testinternalcapi @@ -1124,7 +1125,7 @@ class TestHeapTypeRelative(unittest.TestCase): # Test subclassing using "relative" basicsize, see PEP 697 def check(extra_base_size, extra_size): Base, Sub, instance, data_ptr, data_offset, data_size = ( - _testcapi.make_sized_heaptypes( + _testlimitedcapi.make_sized_heaptypes( extra_base_size, -extra_size)) # no alignment shenanigans when inheriting directly @@ -1152,11 +1153,11 @@ class TestHeapTypeRelative(unittest.TestCase): # we don't reserve (requested + alignment) or more data self.assertLess(data_size - extra_size, - _testcapi.ALIGNOF_MAX_ALIGN_T) + _testlimitedcapi.ALIGNOF_MAX_ALIGN_T) # The offsets/sizes we calculated should be aligned. - self.assertEqual(data_offset % _testcapi.ALIGNOF_MAX_ALIGN_T, 0) - self.assertEqual(data_size % _testcapi.ALIGNOF_MAX_ALIGN_T, 0) + self.assertEqual(data_offset % _testlimitedcapi.ALIGNOF_MAX_ALIGN_T, 0) + self.assertEqual(data_size % _testlimitedcapi.ALIGNOF_MAX_ALIGN_T, 0) sizes = sorted({0, 1, 2, 3, 4, 7, 8, 123, object.__basicsize__, @@ -1182,7 +1183,7 @@ class TestHeapTypeRelative(unittest.TestCase): object.__basicsize__+1}) for extra_size in sizes: with self.subTest(extra_size=extra_size): - Sub = _testcapi.subclass_var_heaptype( + Sub = _testlimitedcapi.subclass_var_heaptype( _testcapi.HeapCCollection, -extra_size, 0, 0) collection = Sub(1, 2, 3) collection.set_data_to_3s() @@ -1196,7 +1197,7 @@ class TestHeapTypeRelative(unittest.TestCase): with self.assertRaises(SystemError, msg="Cannot extend variable-size class without " + "Py_TPFLAGS_ITEMS_AT_END"): - _testcapi.subclass_heaptype(int, -8, 0) + _testlimitedcapi.subclass_heaptype(int, -8, 0) def test_heaptype_relative_members(self): """Test HeapCCollection subclasses work properly""" @@ -1209,7 +1210,7 @@ class TestHeapTypeRelative(unittest.TestCase): for offset in sizes: with self.subTest(extra_base_size=extra_base_size, extra_size=extra_size, offset=offset): if offset < extra_size: - Sub = _testcapi.make_heaptype_with_member( + Sub = _testlimitedcapi.make_heaptype_with_member( extra_base_size, -extra_size, offset, True) Base = Sub.mro()[1] instance = Sub() @@ -1228,29 +1229,29 @@ class TestHeapTypeRelative(unittest.TestCase): instance.set_memb_relative(0) else: with self.assertRaises(SystemError): - Sub = _testcapi.make_heaptype_with_member( + Sub = _testlimitedcapi.make_heaptype_with_member( extra_base_size, -extra_size, offset, True) with self.assertRaises(SystemError): - Sub = _testcapi.make_heaptype_with_member( + Sub = _testlimitedcapi.make_heaptype_with_member( extra_base_size, extra_size, offset, True) with self.subTest(extra_base_size=extra_base_size, extra_size=extra_size): with self.assertRaises(SystemError): - Sub = _testcapi.make_heaptype_with_member( + Sub = _testlimitedcapi.make_heaptype_with_member( extra_base_size, -extra_size, -1, True) def test_heaptype_relative_members_errors(self): with self.assertRaisesRegex( SystemError, r"With Py_RELATIVE_OFFSET, basicsize must be negative"): - _testcapi.make_heaptype_with_member(0, 1234, 0, True) + _testlimitedcapi.make_heaptype_with_member(0, 1234, 0, True) with self.assertRaisesRegex( SystemError, r"Member offset out of range \(0\.\.-basicsize\)"): - _testcapi.make_heaptype_with_member(0, -8, 1234, True) + _testlimitedcapi.make_heaptype_with_member(0, -8, 1234, True) with self.assertRaisesRegex( SystemError, r"Member offset out of range \(0\.\.-basicsize\)"): - _testcapi.make_heaptype_with_member(0, -8, -1, True) + _testlimitedcapi.make_heaptype_with_member(0, -8, -1, True) - Sub = _testcapi.make_heaptype_with_member(0, -8, 0, True) + Sub = _testlimitedcapi.make_heaptype_with_member(0, -8, 0, True) instance = Sub() with self.assertRaisesRegex( SystemError, r"PyMember_GetOne used with Py_RELATIVE_OFFSET"): @@ -2264,10 +2265,19 @@ class TestThreadState(unittest.TestCase): _testcapi.test_current_tstate_matches() +def get_test_funcs(mod, exclude_prefix=None): + funcs = {} + for name in dir(mod): + if not name.startswith('test_'): + continue + if exclude_prefix is not None and name.startswith(exclude_prefix): + continue + funcs[name] = getattr(mod, name) + return funcs + + class Test_testcapi(unittest.TestCase): - locals().update((name, getattr(_testcapi, name)) - for name in dir(_testcapi) - if name.startswith('test_')) + locals().update(get_test_funcs(_testcapi)) # Suppress warning from PyUnicode_FromUnicode(). @warnings_helper.ignore_warnings(category=DeprecationWarning) @@ -2278,11 +2288,13 @@ class Test_testcapi(unittest.TestCase): self.assertEqual(_testcapi.Py_Version, sys.hexversion) +class Test_testlimitedcapi(unittest.TestCase): + locals().update(get_test_funcs(_testlimitedcapi)) + + class Test_testinternalcapi(unittest.TestCase): - locals().update((name, getattr(_testinternalcapi, name)) - for name in dir(_testinternalcapi) - if name.startswith('test_') - and not name.startswith('test_lock_')) + locals().update(get_test_funcs(_testinternalcapi, + exclude_prefix='test_lock_')) @threading_helper.requires_working_threading() diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 73b0826..d2acbc2 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -162,7 +162,8 @@ @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/bytearray.c _testcapi/bytes.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/pyos.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c _testcapi/sys.c _testcapi/hash.c _testcapi/time.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/bytearray.c _testcapi/bytes.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/pyos.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/sys.c _testcapi/hash.c _testcapi/time.c +@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/heaptype_relative.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c diff --git a/Modules/_testcapi/clinic/vectorcall_limited.c.h b/Modules/_testcapi/clinic/vectorcall_limited.c.h deleted file mode 100644 index a233aef..0000000 --- a/Modules/_testcapi/clinic/vectorcall_limited.c.h +++ /dev/null @@ -1,20 +0,0 @@ -/*[clinic input] -preserve -[clinic start generated code]*/ - -PyDoc_STRVAR(_testcapi_call_vectorcall__doc__, -"call_vectorcall($module, callable, /)\n" -"--\n" -"\n"); - -#define _TESTCAPI_CALL_VECTORCALL_METHODDEF \ - {"call_vectorcall", (PyCFunction)_testcapi_call_vectorcall, METH_O, _testcapi_call_vectorcall__doc__}, - -PyDoc_STRVAR(_testcapi_call_vectorcall_method__doc__, -"call_vectorcall_method($module, callable, /)\n" -"--\n" -"\n"); - -#define _TESTCAPI_CALL_VECTORCALL_METHOD_METHODDEF \ - {"call_vectorcall_method", (PyCFunction)_testcapi_call_vectorcall_method, METH_O, _testcapi_call_vectorcall_method__doc__}, -/*[clinic end generated code: output=e980906a39602528 input=a9049054013a1b77]*/ diff --git a/Modules/_testcapi/heaptype_relative.c b/Modules/_testcapi/heaptype_relative.c deleted file mode 100644 index 52bda75..0000000 --- a/Modules/_testcapi/heaptype_relative.c +++ /dev/null @@ -1,344 +0,0 @@ -#include "pyconfig.h" // Py_GIL_DISABLED - -#ifndef Py_GIL_DISABLED -#define Py_LIMITED_API 0x030c0000 // 3.12 -#endif - -#include "parts.h" -#include // max_align_t -#include // memset - -static PyType_Slot empty_slots[] = { - {0, NULL}, -}; - -static PyObject * -make_sized_heaptypes(PyObject *module, PyObject *args) -{ - PyObject *base = NULL; - PyObject *sub = NULL; - PyObject *instance = NULL; - PyObject *result = NULL; - - int extra_base_size, basicsize; - - int r = PyArg_ParseTuple(args, "ii", &extra_base_size, &basicsize); - if (!r) { - goto finally; - } - - PyType_Spec base_spec = { - .name = "_testcapi.Base", - .basicsize = sizeof(PyObject) + extra_base_size, - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .slots = empty_slots, - }; - PyType_Spec sub_spec = { - .name = "_testcapi.Sub", - .basicsize = basicsize, - .flags = Py_TPFLAGS_DEFAULT, - .slots = empty_slots, - }; - - base = PyType_FromMetaclass(NULL, module, &base_spec, NULL); - if (!base) { - goto finally; - } - sub = PyType_FromMetaclass(NULL, module, &sub_spec, base); - if (!sub) { - goto finally; - } - instance = PyObject_CallNoArgs(sub); - if (!instance) { - goto finally; - } - char *data_ptr = PyObject_GetTypeData(instance, (PyTypeObject *)sub); - if (!data_ptr) { - goto finally; - } - Py_ssize_t data_size = PyType_GetTypeDataSize((PyTypeObject *)sub); - if (data_size < 0) { - goto finally; - } - - result = Py_BuildValue("OOOKnn", base, sub, instance, - (unsigned long long)data_ptr, - (Py_ssize_t)(data_ptr - (char*)instance), - data_size); - finally: - Py_XDECREF(base); - Py_XDECREF(sub); - Py_XDECREF(instance); - return result; -} - -static PyObject * -var_heaptype_set_data_to_3s( - PyObject *self, PyTypeObject *defining_class, - PyObject **args, Py_ssize_t nargs, PyObject *kwnames) -{ - void *data_ptr = PyObject_GetTypeData(self, defining_class); - if (!data_ptr) { - return NULL; - } - Py_ssize_t data_size = PyType_GetTypeDataSize(defining_class); - if (data_size < 0) { - return NULL; - } - memset(data_ptr, 3, data_size); - Py_RETURN_NONE; -} - -static PyObject * -var_heaptype_get_data(PyObject *self, PyTypeObject *defining_class, - PyObject **args, Py_ssize_t nargs, PyObject *kwnames) -{ - void *data_ptr = PyObject_GetTypeData(self, defining_class); - if (!data_ptr) { - return NULL; - } - Py_ssize_t data_size = PyType_GetTypeDataSize(defining_class); - if (data_size < 0) { - return NULL; - } - return PyBytes_FromStringAndSize(data_ptr, data_size); -} - -static PyMethodDef var_heaptype_methods[] = { - {"set_data_to_3s", _PyCFunction_CAST(var_heaptype_set_data_to_3s), - METH_METHOD | METH_FASTCALL | METH_KEYWORDS}, - {"get_data", _PyCFunction_CAST(var_heaptype_get_data), - METH_METHOD | METH_FASTCALL | METH_KEYWORDS}, - {NULL}, -}; - -static PyObject * -subclass_var_heaptype(PyObject *module, PyObject *args) -{ - PyObject *result = NULL; - - PyObject *base; // borrowed from args - int basicsize, itemsize; - long pfunc; - - int r = PyArg_ParseTuple(args, "Oiil", &base, &basicsize, &itemsize, &pfunc); - if (!r) { - goto finally; - } - - PyType_Slot slots[] = { - {Py_tp_methods, var_heaptype_methods}, - {0, NULL}, - }; - - PyType_Spec sub_spec = { - .name = "_testcapi.Sub", - .basicsize = basicsize, - .itemsize = itemsize, - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_ITEMS_AT_END, - .slots = slots, - }; - - result = PyType_FromMetaclass(NULL, module, &sub_spec, base); - finally: - return result; -} - -static PyObject * -subclass_heaptype(PyObject *module, PyObject *args) -{ - PyObject *result = NULL; - - PyObject *base; // borrowed from args - int basicsize, itemsize; - - int r = PyArg_ParseTuple(args, "Oii", &base, &basicsize, &itemsize); - if (!r) { - goto finally; - } - - PyType_Slot slots[] = { - {Py_tp_methods, var_heaptype_methods}, - {0, NULL}, - }; - - PyType_Spec sub_spec = { - .name = "_testcapi.Sub", - .basicsize = basicsize, - .itemsize = itemsize, - .flags = Py_TPFLAGS_DEFAULT, - .slots = slots, - }; - - result = PyType_FromMetaclass(NULL, module, &sub_spec, base); - finally: - return result; -} - -static PyMemberDef * -heaptype_with_member_extract_and_check_memb(PyObject *self) -{ - PyMemberDef *def = PyType_GetSlot(Py_TYPE(self), Py_tp_members); - if (!def) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_ValueError, "tp_members is NULL"); - } - return NULL; - } - if (!def[0].name) { - PyErr_SetString(PyExc_ValueError, "tp_members[0] is NULL"); - return NULL; - } - if (def[1].name) { - PyErr_SetString(PyExc_ValueError, "tp_members[1] is not NULL"); - return NULL; - } - if (strcmp(def[0].name, "memb")) { - PyErr_SetString(PyExc_ValueError, "tp_members[0] is not for `memb`"); - return NULL; - } - if (def[0].flags) { - PyErr_SetString(PyExc_ValueError, "tp_members[0] has flags set"); - return NULL; - } - return def; -} - -static PyObject * -heaptype_with_member_get_memb(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - PyMemberDef *def = heaptype_with_member_extract_and_check_memb(self); - return PyMember_GetOne((const char *)self, def); -} - -static PyObject * -heaptype_with_member_set_memb(PyObject *self, PyObject *value) -{ - PyMemberDef *def = heaptype_with_member_extract_and_check_memb(self); - int r = PyMember_SetOne((char *)self, def, value); - if (r < 0) { - return NULL; - } - Py_RETURN_NONE; -} - -static PyObject * -get_memb_offset(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - PyMemberDef *def = heaptype_with_member_extract_and_check_memb(self); - return PyLong_FromSsize_t(def->offset); -} - -static PyObject * -heaptype_with_member_get_memb_relative(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - PyMemberDef def = {"memb", Py_T_BYTE, sizeof(PyObject), Py_RELATIVE_OFFSET}; - return PyMember_GetOne((const char *)self, &def); -} - -static PyObject * -heaptype_with_member_set_memb_relative(PyObject *self, PyObject *value) -{ - PyMemberDef def = {"memb", Py_T_BYTE, sizeof(PyObject), Py_RELATIVE_OFFSET}; - int r = PyMember_SetOne((char *)self, &def, value); - if (r < 0) { - return NULL; - } - Py_RETURN_NONE; -} - -static PyMethodDef heaptype_with_member_methods[] = { - {"get_memb", heaptype_with_member_get_memb, METH_NOARGS}, - {"set_memb", heaptype_with_member_set_memb, METH_O}, - {"get_memb_offset", get_memb_offset, METH_NOARGS}, - {"get_memb_relative", heaptype_with_member_get_memb_relative, METH_NOARGS}, - {"set_memb_relative", heaptype_with_member_set_memb_relative, METH_O}, - {NULL}, -}; - -static PyObject * -make_heaptype_with_member(PyObject *module, PyObject *args) -{ - PyObject *base = NULL; - PyObject *result = NULL; - - int extra_base_size, basicsize, offset, add_flag; - - int r = PyArg_ParseTuple(args, "iiip", &extra_base_size, &basicsize, &offset, &add_flag); - if (!r) { - goto finally; - } - - PyType_Spec base_spec = { - .name = "_testcapi.Base", - .basicsize = sizeof(PyObject) + extra_base_size, - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .slots = empty_slots, - }; - base = PyType_FromMetaclass(NULL, module, &base_spec, NULL); - if (!base) { - goto finally; - } - - PyMemberDef members[] = { - {"memb", Py_T_BYTE, offset, add_flag ? Py_RELATIVE_OFFSET : 0}, - {0}, - }; - PyType_Slot slots[] = { - {Py_tp_members, members}, - {Py_tp_methods, heaptype_with_member_methods}, - {0, NULL}, - }; - - PyType_Spec sub_spec = { - .name = "_testcapi.Sub", - .basicsize = basicsize, - .flags = Py_TPFLAGS_DEFAULT, - .slots = slots, - }; - - result = PyType_FromMetaclass(NULL, module, &sub_spec, base); - finally: - Py_XDECREF(base); - return result; -} - - -static PyObject * -test_alignof_max_align_t(PyObject *module, PyObject *Py_UNUSED(ignored)) -{ - // We define ALIGNOF_MAX_ALIGN_T even if the compiler doesn't support - // max_align_t. Double-check that it's correct. - assert(ALIGNOF_MAX_ALIGN_T > 0); - assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(long long)); - assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(long double)); - assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(void*)); - assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(void (*)(void))); - - // Ensure it's a power of two - assert((ALIGNOF_MAX_ALIGN_T & (ALIGNOF_MAX_ALIGN_T - 1)) == 0); - - Py_RETURN_NONE; -} - -static PyMethodDef TestMethods[] = { - {"make_sized_heaptypes", make_sized_heaptypes, METH_VARARGS}, - {"subclass_var_heaptype", subclass_var_heaptype, METH_VARARGS}, - {"subclass_heaptype", subclass_heaptype, METH_VARARGS}, - {"make_heaptype_with_member", make_heaptype_with_member, METH_VARARGS}, - {"test_alignof_max_align_t", test_alignof_max_align_t, METH_NOARGS}, - {NULL}, -}; - -int -_PyTestCapi_Init_HeaptypeRelative(PyObject *m) { - if (PyModule_AddFunctions(m, TestMethods) < 0) { - return -1; - } - - if (PyModule_AddIntMacro(m, ALIGNOF_MAX_ALIGN_T) < 0) { - return -1; - } - - return 0; -} diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index e8cfb24..2a043cf 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -61,7 +61,4 @@ int _PyTestCapi_Init_Sys(PyObject *module); int _PyTestCapi_Init_Hash(PyObject *module); int _PyTestCapi_Init_Time(PyObject *module); -int _PyTestCapi_Init_VectorcallLimited(PyObject *module); -int _PyTestCapi_Init_HeaptypeRelative(PyObject *module); - #endif // Py_TESTCAPI_PARTS_H diff --git a/Modules/_testcapi/vectorcall_limited.c b/Modules/_testcapi/vectorcall_limited.c deleted file mode 100644 index d7070d3..0000000 --- a/Modules/_testcapi/vectorcall_limited.c +++ /dev/null @@ -1,200 +0,0 @@ -/* Test Vectorcall in the limited API */ - -#include "pyconfig.h" // Py_GIL_DISABLED - -#ifndef Py_GIL_DISABLED -#define Py_LIMITED_API 0x030c0000 // 3.12 -#endif - -#include "parts.h" -#include "clinic/vectorcall_limited.c.h" - -/*[clinic input] -module _testcapi -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/ - -static PyObject * -LimitedVectorCallClass_tpcall(PyObject *self, PyObject *args, PyObject *kwargs) { - return PyUnicode_FromString("tp_call called"); -} - -static PyObject * -LimitedVectorCallClass_vectorcall(PyObject *callable, - PyObject *const *args, - size_t nargsf, - PyObject *kwnames) { - return PyUnicode_FromString("vectorcall called"); -} - -static PyObject * -LimitedVectorCallClass_new(PyTypeObject *tp, PyTypeObject *a, PyTypeObject *kw) -{ - PyObject *self = ((allocfunc)PyType_GetSlot(tp, Py_tp_alloc))(tp, 0); - if (!self) { - return NULL; - } - *(vectorcallfunc*)((char*)self + sizeof(PyObject)) = ( - LimitedVectorCallClass_vectorcall); - return self; -} - -/*[clinic input] -_testcapi.call_vectorcall - - callable: object - / -[clinic start generated code]*/ - -static PyObject * -_testcapi_call_vectorcall(PyObject *module, PyObject *callable) -/*[clinic end generated code: output=bae81eec97fcaad7 input=55d88f92240957ee]*/ -{ - PyObject *args[3] = { NULL, NULL, NULL }; - PyObject *kwname = NULL, *kwnames = NULL, *result = NULL; - - args[1] = PyUnicode_FromString("foo"); - if (!args[1]) { - goto leave; - } - - args[2] = PyUnicode_FromString("bar"); - if (!args[2]) { - goto leave; - } - - kwname = PyUnicode_InternFromString("baz"); - if (!kwname) { - goto leave; - } - - kwnames = PyTuple_New(1); - if (!kwnames) { - goto leave; - } - - if (PyTuple_SetItem(kwnames, 0, kwname)) { - goto leave; - } - - result = PyObject_Vectorcall( - callable, - args + 1, - 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, - kwnames - ); - -leave: - Py_XDECREF(args[1]); - Py_XDECREF(args[2]); - Py_XDECREF(kwnames); - - return result; -} - -/*[clinic input] -_testcapi.call_vectorcall_method - - callable: object - / -[clinic start generated code]*/ - -static PyObject * -_testcapi_call_vectorcall_method(PyObject *module, PyObject *callable) -/*[clinic end generated code: output=e661f48dda08b6fb input=5ba81c27511395b6]*/ -{ - PyObject *args[3] = { NULL, NULL, NULL }; - PyObject *name = NULL, *kwname = NULL, - *kwnames = NULL, *result = NULL; - - name = PyUnicode_FromString("f"); - if (!name) { - goto leave; - } - - args[0] = callable; - args[1] = PyUnicode_FromString("foo"); - if (!args[1]) { - goto leave; - } - - args[2] = PyUnicode_FromString("bar"); - if (!args[2]) { - goto leave; - } - - kwname = PyUnicode_InternFromString("baz"); - if (!kwname) { - goto leave; - } - - kwnames = PyTuple_New(1); - if (!kwnames) { - goto leave; - } - - if (PyTuple_SetItem(kwnames, 0, kwname)) { - goto leave; - } - - - result = PyObject_VectorcallMethod( - name, - args, - 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, - kwnames - ); - -leave: - Py_XDECREF(name); - Py_XDECREF(args[1]); - Py_XDECREF(args[2]); - Py_XDECREF(kwnames); - - return result; -} - -static PyMemberDef LimitedVectorCallClass_members[] = { - {"__vectorcalloffset__", Py_T_PYSSIZET, sizeof(PyObject), Py_READONLY}, - {NULL} -}; - -static PyType_Slot LimitedVectorallClass_slots[] = { - {Py_tp_new, LimitedVectorCallClass_new}, - {Py_tp_call, LimitedVectorCallClass_tpcall}, - {Py_tp_members, LimitedVectorCallClass_members}, - {0}, -}; - -static PyType_Spec LimitedVectorCallClass_spec = { - .name = "_testcapi.LimitedVectorCallClass", - .basicsize = (int)(sizeof(PyObject) + sizeof(vectorcallfunc)), - .flags = Py_TPFLAGS_DEFAULT - | Py_TPFLAGS_HAVE_VECTORCALL - | Py_TPFLAGS_BASETYPE, - .slots = LimitedVectorallClass_slots, -}; - -static PyMethodDef TestMethods[] = { - _TESTCAPI_CALL_VECTORCALL_METHODDEF - _TESTCAPI_CALL_VECTORCALL_METHOD_METHODDEF - {NULL}, -}; - -int -_PyTestCapi_Init_VectorcallLimited(PyObject *m) { - if (PyModule_AddFunctions(m, TestMethods) < 0) { - return -1; - } - - PyObject *LimitedVectorCallClass = PyType_FromModuleAndSpec( - m, &LimitedVectorCallClass_spec, NULL); - if (!LimitedVectorCallClass) { - return -1; - } - if (PyModule_AddType(m, (PyTypeObject *)LimitedVectorCallClass) < 0) { - return -1; - } - Py_DECREF(LimitedVectorCallClass); - return 0; -} diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index b03f871..b653604 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -4098,12 +4098,6 @@ PyInit__testcapi(void) if (_PyTestCapi_Init_PyAtomic(m) < 0) { return NULL; } - if (_PyTestCapi_Init_VectorcallLimited(m) < 0) { - return NULL; - } - if (_PyTestCapi_Init_HeaptypeRelative(m) < 0) { - return NULL; - } if (_PyTestCapi_Init_Hash(m) < 0) { return NULL; } diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c new file mode 100644 index 0000000..da09e3f --- /dev/null +++ b/Modules/_testlimitedcapi.c @@ -0,0 +1,36 @@ +/* + * Test the limited C API. + * + * The 'test_*' functions exported by this module are run as part of the + * standard Python regression test, via Lib/test/test_capi.py. + */ + +#include "_testlimitedcapi/parts.h" + +static PyMethodDef TestMethods[] = { + {NULL, NULL} /* sentinel */ +}; + +static struct PyModuleDef _testlimitedcapimodule = { + PyModuleDef_HEAD_INIT, + .m_name = "_testlimitedcapi", + .m_size = 0, + .m_methods = TestMethods, +}; + +PyMODINIT_FUNC +PyInit__testlimitedcapi(void) +{ + PyObject *mod = PyModule_Create(&_testlimitedcapimodule); + if (mod == NULL) { + return NULL; + } + + if (_PyTestCapi_Init_VectorcallLimited(mod) < 0) { + return NULL; + } + if (_PyTestCapi_Init_HeaptypeRelative(mod) < 0) { + return NULL; + } + return mod; +} diff --git a/Modules/_testlimitedcapi/clinic/vectorcall_limited.c.h b/Modules/_testlimitedcapi/clinic/vectorcall_limited.c.h new file mode 100644 index 0000000..6631a9c --- /dev/null +++ b/Modules/_testlimitedcapi/clinic/vectorcall_limited.c.h @@ -0,0 +1,20 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +PyDoc_STRVAR(_testlimitedcapi_call_vectorcall__doc__, +"call_vectorcall($module, callable, /)\n" +"--\n" +"\n"); + +#define _TESTLIMITEDCAPI_CALL_VECTORCALL_METHODDEF \ + {"call_vectorcall", (PyCFunction)_testlimitedcapi_call_vectorcall, METH_O, _testlimitedcapi_call_vectorcall__doc__}, + +PyDoc_STRVAR(_testlimitedcapi_call_vectorcall_method__doc__, +"call_vectorcall_method($module, callable, /)\n" +"--\n" +"\n"); + +#define _TESTLIMITEDCAPI_CALL_VECTORCALL_METHOD_METHODDEF \ + {"call_vectorcall_method", (PyCFunction)_testlimitedcapi_call_vectorcall_method, METH_O, _testlimitedcapi_call_vectorcall_method__doc__}, +/*[clinic end generated code: output=5976b9b360e1ff30 input=a9049054013a1b77]*/ diff --git a/Modules/_testlimitedcapi/heaptype_relative.c b/Modules/_testlimitedcapi/heaptype_relative.c new file mode 100644 index 0000000..9878a4d --- /dev/null +++ b/Modules/_testlimitedcapi/heaptype_relative.c @@ -0,0 +1,338 @@ +#include "parts.h" +#include // max_align_t +#include // memset + +static PyType_Slot empty_slots[] = { + {0, NULL}, +}; + +static PyObject * +make_sized_heaptypes(PyObject *module, PyObject *args) +{ + PyObject *base = NULL; + PyObject *sub = NULL; + PyObject *instance = NULL; + PyObject *result = NULL; + + int extra_base_size, basicsize; + + int r = PyArg_ParseTuple(args, "ii", &extra_base_size, &basicsize); + if (!r) { + goto finally; + } + + PyType_Spec base_spec = { + .name = "_testcapi.Base", + .basicsize = sizeof(PyObject) + extra_base_size, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .slots = empty_slots, + }; + PyType_Spec sub_spec = { + .name = "_testcapi.Sub", + .basicsize = basicsize, + .flags = Py_TPFLAGS_DEFAULT, + .slots = empty_slots, + }; + + base = PyType_FromMetaclass(NULL, module, &base_spec, NULL); + if (!base) { + goto finally; + } + sub = PyType_FromMetaclass(NULL, module, &sub_spec, base); + if (!sub) { + goto finally; + } + instance = PyObject_CallNoArgs(sub); + if (!instance) { + goto finally; + } + char *data_ptr = PyObject_GetTypeData(instance, (PyTypeObject *)sub); + if (!data_ptr) { + goto finally; + } + Py_ssize_t data_size = PyType_GetTypeDataSize((PyTypeObject *)sub); + if (data_size < 0) { + goto finally; + } + + result = Py_BuildValue("OOOKnn", base, sub, instance, + (unsigned long long)data_ptr, + (Py_ssize_t)(data_ptr - (char*)instance), + data_size); + finally: + Py_XDECREF(base); + Py_XDECREF(sub); + Py_XDECREF(instance); + return result; +} + +static PyObject * +var_heaptype_set_data_to_3s( + PyObject *self, PyTypeObject *defining_class, + PyObject **args, Py_ssize_t nargs, PyObject *kwnames) +{ + void *data_ptr = PyObject_GetTypeData(self, defining_class); + if (!data_ptr) { + return NULL; + } + Py_ssize_t data_size = PyType_GetTypeDataSize(defining_class); + if (data_size < 0) { + return NULL; + } + memset(data_ptr, 3, data_size); + Py_RETURN_NONE; +} + +static PyObject * +var_heaptype_get_data(PyObject *self, PyTypeObject *defining_class, + PyObject **args, Py_ssize_t nargs, PyObject *kwnames) +{ + void *data_ptr = PyObject_GetTypeData(self, defining_class); + if (!data_ptr) { + return NULL; + } + Py_ssize_t data_size = PyType_GetTypeDataSize(defining_class); + if (data_size < 0) { + return NULL; + } + return PyBytes_FromStringAndSize(data_ptr, data_size); +} + +static PyMethodDef var_heaptype_methods[] = { + {"set_data_to_3s", _PyCFunction_CAST(var_heaptype_set_data_to_3s), + METH_METHOD | METH_FASTCALL | METH_KEYWORDS}, + {"get_data", _PyCFunction_CAST(var_heaptype_get_data), + METH_METHOD | METH_FASTCALL | METH_KEYWORDS}, + {NULL}, +}; + +static PyObject * +subclass_var_heaptype(PyObject *module, PyObject *args) +{ + PyObject *result = NULL; + + PyObject *base; // borrowed from args + int basicsize, itemsize; + long pfunc; + + int r = PyArg_ParseTuple(args, "Oiil", &base, &basicsize, &itemsize, &pfunc); + if (!r) { + goto finally; + } + + PyType_Slot slots[] = { + {Py_tp_methods, var_heaptype_methods}, + {0, NULL}, + }; + + PyType_Spec sub_spec = { + .name = "_testcapi.Sub", + .basicsize = basicsize, + .itemsize = itemsize, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_ITEMS_AT_END, + .slots = slots, + }; + + result = PyType_FromMetaclass(NULL, module, &sub_spec, base); + finally: + return result; +} + +static PyObject * +subclass_heaptype(PyObject *module, PyObject *args) +{ + PyObject *result = NULL; + + PyObject *base; // borrowed from args + int basicsize, itemsize; + + int r = PyArg_ParseTuple(args, "Oii", &base, &basicsize, &itemsize); + if (!r) { + goto finally; + } + + PyType_Slot slots[] = { + {Py_tp_methods, var_heaptype_methods}, + {0, NULL}, + }; + + PyType_Spec sub_spec = { + .name = "_testcapi.Sub", + .basicsize = basicsize, + .itemsize = itemsize, + .flags = Py_TPFLAGS_DEFAULT, + .slots = slots, + }; + + result = PyType_FromMetaclass(NULL, module, &sub_spec, base); + finally: + return result; +} + +static PyMemberDef * +heaptype_with_member_extract_and_check_memb(PyObject *self) +{ + PyMemberDef *def = PyType_GetSlot(Py_TYPE(self), Py_tp_members); + if (!def) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, "tp_members is NULL"); + } + return NULL; + } + if (!def[0].name) { + PyErr_SetString(PyExc_ValueError, "tp_members[0] is NULL"); + return NULL; + } + if (def[1].name) { + PyErr_SetString(PyExc_ValueError, "tp_members[1] is not NULL"); + return NULL; + } + if (strcmp(def[0].name, "memb")) { + PyErr_SetString(PyExc_ValueError, "tp_members[0] is not for `memb`"); + return NULL; + } + if (def[0].flags) { + PyErr_SetString(PyExc_ValueError, "tp_members[0] has flags set"); + return NULL; + } + return def; +} + +static PyObject * +heaptype_with_member_get_memb(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyMemberDef *def = heaptype_with_member_extract_and_check_memb(self); + return PyMember_GetOne((const char *)self, def); +} + +static PyObject * +heaptype_with_member_set_memb(PyObject *self, PyObject *value) +{ + PyMemberDef *def = heaptype_with_member_extract_and_check_memb(self); + int r = PyMember_SetOne((char *)self, def, value); + if (r < 0) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +get_memb_offset(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyMemberDef *def = heaptype_with_member_extract_and_check_memb(self); + return PyLong_FromSsize_t(def->offset); +} + +static PyObject * +heaptype_with_member_get_memb_relative(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyMemberDef def = {"memb", Py_T_BYTE, sizeof(PyObject), Py_RELATIVE_OFFSET}; + return PyMember_GetOne((const char *)self, &def); +} + +static PyObject * +heaptype_with_member_set_memb_relative(PyObject *self, PyObject *value) +{ + PyMemberDef def = {"memb", Py_T_BYTE, sizeof(PyObject), Py_RELATIVE_OFFSET}; + int r = PyMember_SetOne((char *)self, &def, value); + if (r < 0) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyMethodDef heaptype_with_member_methods[] = { + {"get_memb", heaptype_with_member_get_memb, METH_NOARGS}, + {"set_memb", heaptype_with_member_set_memb, METH_O}, + {"get_memb_offset", get_memb_offset, METH_NOARGS}, + {"get_memb_relative", heaptype_with_member_get_memb_relative, METH_NOARGS}, + {"set_memb_relative", heaptype_with_member_set_memb_relative, METH_O}, + {NULL}, +}; + +static PyObject * +make_heaptype_with_member(PyObject *module, PyObject *args) +{ + PyObject *base = NULL; + PyObject *result = NULL; + + int extra_base_size, basicsize, offset, add_flag; + + int r = PyArg_ParseTuple(args, "iiip", &extra_base_size, &basicsize, &offset, &add_flag); + if (!r) { + goto finally; + } + + PyType_Spec base_spec = { + .name = "_testcapi.Base", + .basicsize = sizeof(PyObject) + extra_base_size, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .slots = empty_slots, + }; + base = PyType_FromMetaclass(NULL, module, &base_spec, NULL); + if (!base) { + goto finally; + } + + PyMemberDef members[] = { + {"memb", Py_T_BYTE, offset, add_flag ? Py_RELATIVE_OFFSET : 0}, + {0}, + }; + PyType_Slot slots[] = { + {Py_tp_members, members}, + {Py_tp_methods, heaptype_with_member_methods}, + {0, NULL}, + }; + + PyType_Spec sub_spec = { + .name = "_testcapi.Sub", + .basicsize = basicsize, + .flags = Py_TPFLAGS_DEFAULT, + .slots = slots, + }; + + result = PyType_FromMetaclass(NULL, module, &sub_spec, base); + finally: + Py_XDECREF(base); + return result; +} + + +static PyObject * +test_alignof_max_align_t(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + // We define ALIGNOF_MAX_ALIGN_T even if the compiler doesn't support + // max_align_t. Double-check that it's correct. + assert(ALIGNOF_MAX_ALIGN_T > 0); + assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(long long)); + assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(long double)); + assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(void*)); + assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(void (*)(void))); + + // Ensure it's a power of two + assert((ALIGNOF_MAX_ALIGN_T & (ALIGNOF_MAX_ALIGN_T - 1)) == 0); + + Py_RETURN_NONE; +} + +static PyMethodDef TestMethods[] = { + {"make_sized_heaptypes", make_sized_heaptypes, METH_VARARGS}, + {"subclass_var_heaptype", subclass_var_heaptype, METH_VARARGS}, + {"subclass_heaptype", subclass_heaptype, METH_VARARGS}, + {"make_heaptype_with_member", make_heaptype_with_member, METH_VARARGS}, + {"test_alignof_max_align_t", test_alignof_max_align_t, METH_NOARGS}, + {NULL}, +}; + +int +_PyTestCapi_Init_HeaptypeRelative(PyObject *m) { + if (PyModule_AddFunctions(m, TestMethods) < 0) { + return -1; + } + + if (PyModule_AddIntMacro(m, ALIGNOF_MAX_ALIGN_T) < 0) { + return -1; + } + + return 0; +} diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h new file mode 100644 index 0000000..83590a7 --- /dev/null +++ b/Modules/_testlimitedcapi/parts.h @@ -0,0 +1,27 @@ +#ifndef Py_TESTLIMITEDCAPI_PARTS_H +#define Py_TESTLIMITEDCAPI_PARTS_H + +// Always enable assertions +#undef NDEBUG + +#include "pyconfig.h" // Py_GIL_DISABLED + +// Use the limited C API +#ifndef Py_GIL_DISABLED +# define Py_LIMITED_API 0x030c0000 // 3.12 +#endif + +// Make sure that the internal C API cannot be used. +#undef Py_BUILD_CORE_MODULE +#undef Py_BUILD_CORE_BUILTIN + +#include "Python.h" + +#ifdef Py_BUILD_CORE +# error "Py_BUILD_CORE macro must not be defined" +#endif + +int _PyTestCapi_Init_VectorcallLimited(PyObject *module); +int _PyTestCapi_Init_HeaptypeRelative(PyObject *module); + +#endif // Py_TESTLIMITEDCAPI_PARTS_H diff --git a/Modules/_testlimitedcapi/vectorcall_limited.c b/Modules/_testlimitedcapi/vectorcall_limited.c new file mode 100644 index 0000000..24aa5e9 --- /dev/null +++ b/Modules/_testlimitedcapi/vectorcall_limited.c @@ -0,0 +1,194 @@ +/* Test Vectorcall in the limited API */ + +#include "parts.h" +#include "clinic/vectorcall_limited.c.h" + +/*[clinic input] +module _testlimitedcapi +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=2700057f9c1135ba]*/ + +static PyObject * +LimitedVectorCallClass_tpcall(PyObject *self, PyObject *args, PyObject *kwargs) { + return PyUnicode_FromString("tp_call called"); +} + +static PyObject * +LimitedVectorCallClass_vectorcall(PyObject *callable, + PyObject *const *args, + size_t nargsf, + PyObject *kwnames) { + return PyUnicode_FromString("vectorcall called"); +} + +static PyObject * +LimitedVectorCallClass_new(PyTypeObject *tp, PyTypeObject *a, PyTypeObject *kw) +{ + PyObject *self = ((allocfunc)PyType_GetSlot(tp, Py_tp_alloc))(tp, 0); + if (!self) { + return NULL; + } + *(vectorcallfunc*)((char*)self + sizeof(PyObject)) = ( + LimitedVectorCallClass_vectorcall); + return self; +} + +/*[clinic input] +_testlimitedcapi.call_vectorcall + + callable: object + / +[clinic start generated code]*/ + +static PyObject * +_testlimitedcapi_call_vectorcall(PyObject *module, PyObject *callable) +/*[clinic end generated code: output=9cbb7832263a8eef input=0743636c12dccb28]*/ +{ + PyObject *args[3] = { NULL, NULL, NULL }; + PyObject *kwname = NULL, *kwnames = NULL, *result = NULL; + + args[1] = PyUnicode_FromString("foo"); + if (!args[1]) { + goto leave; + } + + args[2] = PyUnicode_FromString("bar"); + if (!args[2]) { + goto leave; + } + + kwname = PyUnicode_InternFromString("baz"); + if (!kwname) { + goto leave; + } + + kwnames = PyTuple_New(1); + if (!kwnames) { + goto leave; + } + + if (PyTuple_SetItem(kwnames, 0, kwname)) { + goto leave; + } + + result = PyObject_Vectorcall( + callable, + args + 1, + 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, + kwnames + ); + +leave: + Py_XDECREF(args[1]); + Py_XDECREF(args[2]); + Py_XDECREF(kwnames); + + return result; +} + +/*[clinic input] +_testlimitedcapi.call_vectorcall_method + + callable: object + / +[clinic start generated code]*/ + +static PyObject * +_testlimitedcapi_call_vectorcall_method(PyObject *module, PyObject *callable) +/*[clinic end generated code: output=4558323a46cc09eb input=a736f7dbf15f1be5]*/ +{ + PyObject *args[3] = { NULL, NULL, NULL }; + PyObject *name = NULL, *kwname = NULL, + *kwnames = NULL, *result = NULL; + + name = PyUnicode_FromString("f"); + if (!name) { + goto leave; + } + + args[0] = callable; + args[1] = PyUnicode_FromString("foo"); + if (!args[1]) { + goto leave; + } + + args[2] = PyUnicode_FromString("bar"); + if (!args[2]) { + goto leave; + } + + kwname = PyUnicode_InternFromString("baz"); + if (!kwname) { + goto leave; + } + + kwnames = PyTuple_New(1); + if (!kwnames) { + goto leave; + } + + if (PyTuple_SetItem(kwnames, 0, kwname)) { + goto leave; + } + + + result = PyObject_VectorcallMethod( + name, + args, + 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, + kwnames + ); + +leave: + Py_XDECREF(name); + Py_XDECREF(args[1]); + Py_XDECREF(args[2]); + Py_XDECREF(kwnames); + + return result; +} + +static PyMemberDef LimitedVectorCallClass_members[] = { + {"__vectorcalloffset__", Py_T_PYSSIZET, sizeof(PyObject), Py_READONLY}, + {NULL} +}; + +static PyType_Slot LimitedVectorallClass_slots[] = { + {Py_tp_new, LimitedVectorCallClass_new}, + {Py_tp_call, LimitedVectorCallClass_tpcall}, + {Py_tp_members, LimitedVectorCallClass_members}, + {0}, +}; + +static PyType_Spec LimitedVectorCallClass_spec = { + .name = "_testlimitedcapi.LimitedVectorCallClass", + .basicsize = (int)(sizeof(PyObject) + sizeof(vectorcallfunc)), + .flags = Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_HAVE_VECTORCALL + | Py_TPFLAGS_BASETYPE, + .slots = LimitedVectorallClass_slots, +}; + +static PyMethodDef TestMethods[] = { + _TESTLIMITEDCAPI_CALL_VECTORCALL_METHODDEF + _TESTLIMITEDCAPI_CALL_VECTORCALL_METHOD_METHODDEF + {NULL}, +}; + +int +_PyTestCapi_Init_VectorcallLimited(PyObject *m) { + if (PyModule_AddFunctions(m, TestMethods) < 0) { + return -1; + } + + PyObject *LimitedVectorCallClass = PyType_FromModuleAndSpec( + m, &LimitedVectorCallClass_spec, NULL); + if (!LimitedVectorCallClass) { + return -1; + } + if (PyModule_AddType(m, (PyTypeObject *)LimitedVectorCallClass) < 0) { + return -1; + } + Py_DECREF(LimitedVectorCallClass); + return 0; +} diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index 66df0a6..3ca4c5f 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -96,9 +96,7 @@ - - diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj new file mode 100644 index 0000000..1b27942 --- /dev/null +++ b/PCbuild/_testlimitedcapi.vcxproj @@ -0,0 +1,116 @@ + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + ARM + + + PGInstrument + ARM64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + ARM + + + PGUpdate + ARM64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933} + _testlimitedcapi + Win32Proj + false + + + + + DynamicLibrary + NotSet + + + + $(PyStdlibPydExt) + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + + + + + + + + + + + + {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} + false + + + {885d4898-d08d-4091-9c40-c700cfe3fc5a} + false + + + + + + \ No newline at end of file diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters b/PCbuild/_testlimitedcapi.vcxproj.filters new file mode 100644 index 0000000..c4764a5 --- /dev/null +++ b/PCbuild/_testlimitedcapi.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {a76a90d8-8e8b-4c36-8f58-8bd46abe9f5e} + + + {071b2ff4-e5a1-4e79-b0c5-cf46b0094a80} + + + + + Source Files + + + Source Files + + + + + + Resource Files + + + \ No newline at end of file diff --git a/PCbuild/pcbuild.proj b/PCbuild/pcbuild.proj index b7b78be..a2a637a 100644 --- a/PCbuild/pcbuild.proj +++ b/PCbuild/pcbuild.proj @@ -77,7 +77,7 @@ - + diff --git a/PCbuild/pcbuild.sln b/PCbuild/pcbuild.sln index a0b5fbd..d10e1c4 100644 --- a/PCbuild/pcbuild.sln +++ b/PCbuild/pcbuild.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30028.174 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34525.116 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{553EC33E-9816-4996-A660-5D6186A0B0B3}" ProjectSection(SolutionItems) = preProject @@ -159,6 +159,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_wmi", "_wmi.vcxproj", "{54 EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_testclinic_limited", "_testclinic_limited.vcxproj", "{01FDF29A-40A1-46DF-84F5-85EBBD2A2410}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_testlimitedcapi", "_testlimitedcapi.vcxproj", "{7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -1648,6 +1650,38 @@ Global {01FDF29A-40A1-46DF-84F5-85EBBD2A2410}.Release|Win32.Build.0 = Release|Win32 {01FDF29A-40A1-46DF-84F5-85EBBD2A2410}.Release|x64.ActiveCfg = Release|x64 {01FDF29A-40A1-46DF-84F5-85EBBD2A2410}.Release|x64.Build.0 = Release|x64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Debug|ARM.ActiveCfg = Debug|ARM + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Debug|ARM.Build.0 = Debug|ARM + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Debug|ARM64.Build.0 = Debug|ARM64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Debug|Win32.ActiveCfg = Debug|Win32 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Debug|Win32.Build.0 = Debug|Win32 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Debug|x64.ActiveCfg = Debug|x64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Debug|x64.Build.0 = Debug|x64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGInstrument|ARM.ActiveCfg = PGInstrument|ARM + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGInstrument|ARM.Build.0 = PGInstrument|ARM + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGInstrument|ARM64.ActiveCfg = PGInstrument|ARM64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGInstrument|ARM64.Build.0 = PGInstrument|ARM64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGUpdate|ARM.ActiveCfg = PGUpdate|ARM + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGUpdate|ARM.Build.0 = PGUpdate|ARM + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGUpdate|ARM64.ActiveCfg = PGUpdate|ARM64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGUpdate|ARM64.Build.0 = PGUpdate|ARM64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Release|ARM.ActiveCfg = Release|ARM + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Release|ARM.Build.0 = Release|ARM + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Release|ARM64.ActiveCfg = Release|ARM64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Release|ARM64.Build.0 = Release|ARM64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Release|Win32.ActiveCfg = Release|Win32 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Release|Win32.Build.0 = Release|Win32 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Release|x64.ActiveCfg = Release|x64 + {7467D86C-1CEB-4CB9-B65E-E9A54ABDC933}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index a7c2a68..1fdd8a4 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -131,29 +131,31 @@ xxlimited_35 The following sub-projects are for individual modules of the standard library which are implemented in C; each one builds a DLL (renamed to .pyd) of the same name as the project: -_asyncio -_ctypes -_ctypes_test -_zoneinfo -_decimal -_elementtree -_hashlib -_multiprocessing -_overlapped -_socket -_testbuffer -_testcapi -_testclinic -_testclinic_limited -_testconsole -_testimportmultiple -_testmultiphase -_testsinglephase -_tkinter -pyexpat -select -unicodedata -winsound + * _asyncio + * _ctypes + * _ctypes_test + * _zoneinfo + * _decimal + * _elementtree + * _hashlib + * _multiprocessing + * _overlapped + * _socket + * _testbuffer + * _testcapi + * _testlimitedcapi + * _testinternalcapi + * _testclinic + * _testclinic_limited + * _testconsole + * _testimportmultiple + * _testmultiphase + * _testsinglephase + * _tkinter + * pyexpat + * select + * unicodedata + * winsound The following Python-controlled sub-projects wrap external projects. Note that these external libraries are not necessary for a working @@ -309,6 +311,8 @@ _testclinic_limited extension, the file Modules/_testclinic_limited.c: * In PCbuild/, copy _testclinic.vcxproj to _testclinic_limited.vcxproj, replace RootNamespace value with `_testclinic_limited`, replace `_asyncio.c` with `_testclinic_limited.c`. +* In PCbuild/, copy _testclinic.vcxproj.filters to + _testclinic_limited.vcxproj.filters, edit the list of files in the new file. * Open Visual Studio, open PCbuild\pcbuild.sln solution, add the PCbuild\_testclinic_limited.vcxproj project to the solution ("add existing project). diff --git a/Tools/build/generate_stdlib_module_names.py b/Tools/build/generate_stdlib_module_names.py index 588dfda..69dc74e 100644 --- a/Tools/build/generate_stdlib_module_names.py +++ b/Tools/build/generate_stdlib_module_names.py @@ -32,6 +32,7 @@ IGNORE = { '_testconsole', '_testimportmultiple', '_testinternalcapi', + '_testlimitedcapi', '_testmultiphase', '_testsinglephase', '_testexternalinspection', diff --git a/Tools/c-analyzer/c_parser/preprocessor/gcc.py b/Tools/c-analyzer/c_parser/preprocessor/gcc.py index 6ece70c..cc3a9be 100644 --- a/Tools/c-analyzer/c_parser/preprocessor/gcc.py +++ b/Tools/c-analyzer/c_parser/preprocessor/gcc.py @@ -15,8 +15,8 @@ FILES_WITHOUT_INTERNAL_CAPI = frozenset(( # C files in the fhe following directories must not be built with # Py_BUILD_CORE. DIRS_WITHOUT_INTERNAL_CAPI = frozenset(( - # Modules/_testcapi/ - '_testcapi', + '_testcapi', # Modules/_testcapi/ + '_testlimitedcapi', # Modules/_testlimitedcapi/ )) TOOL = 'gcc' diff --git a/Tools/msi/test/test_files.wxs b/Tools/msi/test/test_files.wxs index bb9b258..6862a58 100644 --- a/Tools/msi/test/test_files.wxs +++ b/Tools/msi/test/test_files.wxs @@ -1,6 +1,6 @@ - + diff --git a/configure b/configure index e62cb2b..44bd540 100755 --- a/configure +++ b/configure @@ -671,6 +671,8 @@ MODULE__TESTBUFFER_FALSE MODULE__TESTBUFFER_TRUE MODULE__TESTINTERNALCAPI_FALSE MODULE__TESTINTERNALCAPI_TRUE +MODULE__TESTLIMITEDCAPI_FALSE +MODULE__TESTLIMITEDCAPI_TRUE MODULE__TESTCLINIC_LIMITED_FALSE MODULE__TESTCLINIC_LIMITED_TRUE MODULE__TESTCLINIC_FALSE @@ -27301,6 +27303,7 @@ SRCDIRS="\ Modules/_sre \ Modules/_testcapi \ Modules/_testinternalcapi \ + Modules/_testlimitedcapi \ Modules/_xxtestfuzz \ Modules/cjkcodecs \ Modules/expat \ @@ -30771,6 +30774,44 @@ fi printf "%s\n" "$py_cv_module__testclinic_limited" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _testlimitedcapi" >&5 +printf %s "checking for stdlib extension module _testlimitedcapi... " >&6; } + if test "$py_cv_module__testlimitedcapi" != "n/a" +then : + + if test "$TEST_MODULES" = yes +then : + if true +then : + py_cv_module__testlimitedcapi=yes +else $as_nop + py_cv_module__testlimitedcapi=missing +fi +else $as_nop + py_cv_module__testlimitedcapi=disabled +fi + +fi + as_fn_append MODULE_BLOCK "MODULE__TESTLIMITEDCAPI_STATE=$py_cv_module__testlimitedcapi$as_nl" + if test "x$py_cv_module__testlimitedcapi" = xyes +then : + + + + +fi + if test "$py_cv_module__testlimitedcapi" = yes; then + MODULE__TESTLIMITEDCAPI_TRUE= + MODULE__TESTLIMITEDCAPI_FALSE='#' +else + MODULE__TESTLIMITEDCAPI_TRUE='#' + MODULE__TESTLIMITEDCAPI_FALSE= +fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $py_cv_module__testlimitedcapi" >&5 +printf "%s\n" "$py_cv_module__testlimitedcapi" >&6; } + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _testinternalcapi" >&5 printf %s "checking for stdlib extension module _testinternalcapi... " >&6; } if test "$py_cv_module__testinternalcapi" != "n/a" @@ -31557,6 +31598,10 @@ if test -z "${MODULE__TESTCLINIC_LIMITED_TRUE}" && test -z "${MODULE__TESTCLINIC as_fn_error $? "conditional \"MODULE__TESTCLINIC_LIMITED\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${MODULE__TESTLIMITEDCAPI_TRUE}" && test -z "${MODULE__TESTLIMITEDCAPI_FALSE}"; then + as_fn_error $? "conditional \"MODULE__TESTLIMITEDCAPI\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${MODULE__TESTINTERNALCAPI_TRUE}" && test -z "${MODULE__TESTINTERNALCAPI_FALSE}"; then as_fn_error $? "conditional \"MODULE__TESTINTERNALCAPI\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/configure.ac b/configure.ac index 990215b..bf5680e 100644 --- a/configure.ac +++ b/configure.ac @@ -6813,6 +6813,7 @@ SRCDIRS="\ Modules/_sre \ Modules/_testcapi \ Modules/_testinternalcapi \ + Modules/_testlimitedcapi \ Modules/_xxtestfuzz \ Modules/cjkcodecs \ Modules/expat \ @@ -7622,6 +7623,7 @@ PY_STDLIB_MOD([_testcapi], [], [], [$LIBATOMIC]) PY_STDLIB_MOD([_testclinic], [test "$TEST_MODULES" = yes]) PY_STDLIB_MOD([_testclinic_limited], [test "$TEST_MODULES" = yes]) +PY_STDLIB_MOD([_testlimitedcapi], [test "$TEST_MODULES" = yes]) PY_STDLIB_MOD([_testinternalcapi], [test "$TEST_MODULES" = yes]) PY_STDLIB_MOD([_testbuffer], [test "$TEST_MODULES" = yes]) PY_STDLIB_MOD([_testimportmultiple], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes]) -- cgit v0.12