Files
cpython/Modules/_testlimitedcapi/slots.c
T
2026-05-05 09:18:04 +02:00

630 lines
21 KiB
C

#define Py_LIMITED_API 0x030f0000
#include "parts.h"
PyABIInfo_VAR(abi_info);
/* Define a bunch of (mostly nonsensical) functions to put in slots, so
* Lib/test/test_capi/test_slots.py can verify they've been assigned to
* the right slots.
* This module is full of "magic constants" which simply need to match
* between the C and Python part of the tests.
*/
// getbufferproc: export buffer; increment a counter
static int
demo_getbuffer(PyObject *exporter, Py_buffer *view, int flags)
{
Py_INCREF(exporter);
// PyObject_GetTypeData & Py_TYPE: safe on non-subclassable type
int *data = PyObject_GetTypeData(exporter, Py_TYPE(exporter));
if (!data) {
return -1;
}
(*data)++;
return PyBuffer_FillInfo(view, exporter, "buf", 4, 1, flags);
}
// releasebufferproc: release buffer; decrement a counter
static void
demo_releasebuffer(PyObject *exporter, Py_buffer *view)
{
Py_DECREF(exporter);
// PyObject_GetTypeData & Py_TYPE: safe on non-subclassable type
int *data = PyObject_GetTypeData(exporter, Py_TYPE(exporter));
if (!data) {
PyErr_WriteUnraisable(exporter);
return;
}
(*data)--;
return;
}
// objobjargproc: raise KeyError
static int
demo_ass_subscript(PyObject *o, PyObject *key, PyObject *v)
{
PyErr_Format(PyExc_KeyError, "I don't like that key");
return -1;
}
// lenfunc: report 456
static Py_ssize_t
demo_length(PyObject *o)
{
return (Py_ssize_t)456;
}
// binaryfunc; return constant value
static PyObject *binop_123(PyObject* a, PyObject *b) { return PyLong_FromLong(123); }
static PyObject *binop_234(PyObject* a, PyObject *b) { return PyLong_FromLong(234); }
static PyObject *binop_345(PyObject* a, PyObject *b) { return PyLong_FromLong(345); }
static PyObject *binop_456(PyObject* a, PyObject *b) { return PyLong_FromLong(456); }
static PyObject *binop_567(PyObject* a, PyObject *b) { return PyLong_FromLong(567); }
static PyObject *binop_678(PyObject* a, PyObject *b) { return PyLong_FromLong(678); }
static PyObject *
type_from_slots(PyObject* module, PyObject *args)
{
char *case_name;
if (!PyArg_ParseTuple(args, "s", &case_name)) {
return NULL;
}
#define CASE(NAME) \
if (strcmp(case_name, NAME) == 0) { \
return PyType_FromSlots((PySlot[]) { \
PySlot_DATA(Py_tp_name, "_testlimitedcapi.MyType"), \
PySlot_DATA(Py_tp_module, module), \
/////////////////////////////////////////////////////////////////////////
#define ENDCASE() \
PySlot_END \
}); \
} \
/////////////////////////////////////////////////////////////////////////
CASE("basic")
ENDCASE()
CASE("foreign_slot")
PySlot_DATA(Py_mod_name, "this is not a module"),
ENDCASE()
CASE("basicsize")
PySlot_SIZE(Py_tp_basicsize, 256),
ENDCASE()
CASE("extra_basicsize")
PySlot_SIZE(Py_tp_extra_basicsize, 256),
ENDCASE()
CASE("itemsize")
PySlot_SIZE(Py_tp_itemsize, 16),
ENDCASE()
CASE("flags")
PySlot_UINT64(Py_tp_flags,
Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_BASETYPE),
ENDCASE()
CASE("matmul_123")
PySlot_FUNC(Py_nb_matrix_multiply, binop_123),
ENDCASE()
CASE("optional_end")
{.sl_flags=PySlot_OPTIONAL},
ENDCASE()
CASE("invalid")
{.sl_id=Py_slot_invalid},
ENDCASE()
CASE("invalid_fbad")
{.sl_id=0xfbad},
ENDCASE()
CASE("optional_invalid")
{.sl_id=Py_slot_invalid, .sl_flags=PySlot_OPTIONAL},
PySlot_SIZE(Py_tp_extra_basicsize, 256),
ENDCASE()
CASE("optional_invalid_fbad")
{.sl_id=0xfbad, .sl_flags=PySlot_OPTIONAL},
PySlot_SIZE(Py_tp_extra_basicsize, 256),
ENDCASE()
CASE("old_slot_numbers")
PySlot_FUNC(1, demo_getbuffer),
PySlot_FUNC(2, demo_releasebuffer),
PySlot_FUNC(3, demo_ass_subscript),
PySlot_FUNC(4, demo_length),
PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)),
PySlot_STATIC_DATA(Py_tp_members, ((PyMemberDef[]) {
{"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET},
{NULL},
})),
ENDCASE()
CASE("new_slot_numbers")
PySlot_FUNC(88, demo_getbuffer),
PySlot_FUNC(89, demo_releasebuffer),
PySlot_FUNC(90, demo_ass_subscript),
PySlot_FUNC(91, demo_length),
PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)),
PySlot_STATIC_DATA(Py_tp_members, ((PyMemberDef[]) {
{"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET},
{NULL},
})),
ENDCASE()
CASE("nonstatic_tp_members")
PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)),
PySlot_DATA(Py_tp_members, ((PyMemberDef[]) {
{"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET},
{NULL},
})),
ENDCASE()
CASE("intptr_flags_macro")
PySlot_PTR(Py_tp_flags, (void*)(intptr_t)Py_TPFLAGS_IMMUTABLETYPE),
ENDCASE()
CASE("intptr_flags_struct")
{.sl_id=Py_tp_flags,
.sl_flags=PySlot_INTPTR,
.sl_ptr=(void*)(intptr_t)Py_TPFLAGS_IMMUTABLETYPE,
},
ENDCASE()
CASE("intptr_static")
PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)),
PySlot_PTR_STATIC(Py_tp_members, ((PyMemberDef[]) {
{"attribute", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET},
{NULL},
})),
ENDCASE()
CASE("nested")
PySlot_FUNC(Py_nb_add, binop_123),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_FUNC(Py_nb_subtract, binop_234),
PySlot_END,
})),
ENDCASE()
CASE("nested_max")
PySlot_FUNC(Py_nb_add, binop_123),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_FUNC(Py_nb_subtract, binop_234),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_FUNC(Py_nb_multiply, binop_345),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_FUNC(Py_nb_true_divide, binop_456),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_FUNC(Py_nb_remainder, binop_567),
PySlot_END
})),
PySlot_END,
})),
PySlot_END,
})),
PySlot_END,
})),
ENDCASE()
CASE("nested_over_limit")
PySlot_FUNC(Py_nb_add, binop_123),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_FUNC(Py_nb_subtract, binop_234),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_FUNC(Py_nb_multiply, binop_345),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_FUNC(Py_nb_true_divide, binop_456),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_FUNC(Py_nb_remainder, binop_567),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_FUNC(Py_nb_xor, binop_678),
PySlot_END
})),
PySlot_END
})),
PySlot_END,
})),
PySlot_END,
})),
PySlot_END,
})),
ENDCASE()
CASE("nested_old")
PySlot_FUNC(Py_nb_add, binop_123),
PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
{Py_nb_subtract, binop_234},
{0},
})),
ENDCASE()
CASE("nested_old_max")
PySlot_FUNC(Py_nb_add, binop_123),
PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
{Py_nb_subtract, binop_234},
{Py_tp_slots, ((PyType_Slot[]) {
{Py_nb_multiply, binop_345},
{Py_tp_slots, ((PyType_Slot[]) {
{Py_nb_true_divide, binop_456},
{Py_tp_slots, ((PyType_Slot[]) {
{Py_nb_remainder, binop_567},
{0},
})},
{0},
})},
{0},
})},
{0},
})),
ENDCASE()
CASE("nested_old_over_limit")
PySlot_FUNC(Py_nb_add, binop_123),
PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
{Py_nb_subtract, binop_234},
{Py_tp_slots, ((PyType_Slot[]) {
{Py_nb_multiply, binop_345},
{Py_tp_slots, ((PyType_Slot[]) {
{Py_nb_true_divide, binop_456},
{Py_tp_slots, ((PyType_Slot[]) {
{Py_nb_remainder, binop_567},
{Py_tp_slots, ((PyType_Slot[]) {
{Py_nb_xor, binop_678},
{0},
})},
{0},
})},
{0},
})},
{0},
})},
{0},
})),
ENDCASE()
CASE("nested_pingpong")
PySlot_FUNC(Py_nb_add, binop_123),
PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
{Py_nb_subtract, binop_234},
{Py_slot_subslots, ((PySlot[]) {
PySlot_FUNC(Py_nb_multiply, binop_345),
PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
{Py_nb_true_divide, binop_456},
{Py_slot_subslots, ((PySlot[]) {
PySlot_FUNC(Py_nb_remainder, binop_567),
PySlot_END
})},
{0},
})),
PySlot_END,
})},
{0},
})),
ENDCASE()
CASE("repeat_add")
PySlot_FUNC(Py_nb_add, binop_123),
PySlot_FUNC(Py_nb_add, binop_456),
ENDCASE()
CASE("repeat_module")
PySlot_DATA(Py_tp_module, Py_True),
PySlot_DATA(Py_tp_module, Py_False),
ENDCASE()
#undef CASE
#undef ENDCASE
PyErr_Format(PyExc_SystemError, "bad case: %s", case_name);
return NULL;
}
static PyObject *
type_from_null_slot(PyObject* module, PyObject *args)
{
long slot_number;
if (!PyArg_ParseTuple(args, "l", &slot_number)) {
return NULL;
}
return PyType_FromSlots((PySlot[]) {
PySlot_DATA(Py_tp_name, "_testlimitedcapi.MyType"),
PySlot_DATA(Py_tp_module, module),
PySlot_PTR_STATIC((uint16_t)slot_number, NULL),
PySlot_END
});
}
static PyObject *
type_from_null_spec_slot(PyObject* Py_UNUSED(module), PyObject *args)
{
long slot_number;
if (!PyArg_ParseTuple(args, "l", &slot_number)) {
return NULL;
}
return PyType_FromSpec(&(PyType_Spec) {
.name = "_testlimitedcapi.MyType",
.slots = (PyType_Slot[]) {
{slot_number, NULL},
{0},
},
});
}
static PyObject *
demo_create(PyObject *spec, PyModuleDef *def)
{
assert(def == NULL);
return Py_NewRef(spec);
}
static int
demo_exec(PyObject *mod)
{
return PyModule_AddStringConstant(mod, "exec_done", "yes");
}
static PyMethodDef *TestMethods;
static PyObject *
module_from_slots(PyObject* Py_UNUSED(module), PyObject *args)
{
PyObject *spec;
char *case_name;
if (!PyArg_ParseTuple(args, "sO", &case_name, &spec)) {
return NULL;
}
PyObject *mod = NULL;
#define CASE(NAME) \
if (strcmp(case_name, NAME) == 0) { \
mod = PyModule_FromSlotsAndSpec((PySlot[]) { \
PySlot_DATA(Py_mod_abi, &abi_info), \
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), \
/////////////////////////////////////////////////////////////////////////
#define ENDCASE() \
PySlot_END \
}, spec); \
} \
/////////////////////////////////////////////////////////////////////////
CASE("basic")
ENDCASE()
CASE("foreign_slot")
PySlot_DATA(Py_tp_name, "this is not a type"),
ENDCASE()
CASE("state_size")
PySlot_SIZE(Py_mod_state_size, 42),
ENDCASE()
CASE("multi_interp")
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
ENDCASE()
CASE("exec")
PySlot_FUNC(Py_mod_exec, demo_exec),
ENDCASE()
CASE("optional_end")
{.sl_flags=PySlot_OPTIONAL},
ENDCASE()
CASE("invalid")
{.sl_id=Py_slot_invalid},
ENDCASE()
CASE("invalid_fbad")
{.sl_id=0xfbad},
ENDCASE()
CASE("optional_invalid")
{.sl_id=Py_slot_invalid, .sl_flags=PySlot_OPTIONAL},
PySlot_SIZE(Py_mod_exec, demo_exec),
ENDCASE()
CASE("optional_invalid_fbad")
{.sl_id=0xfbad, .sl_flags=PySlot_OPTIONAL},
PySlot_SIZE(Py_mod_exec, demo_exec),
ENDCASE()
CASE("old_slot_numbers")
// 1: see old_slot_number_create case
PySlot_FUNC(2, demo_exec),
PySlot_DATA(3, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
// 4: see module_from_gil_slot function
ENDCASE()
CASE("new_slot_numbers")
// 84: see new_slot_number_create case
PySlot_FUNC(85, demo_exec),
PySlot_FUNC(86, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
// 87: see module_from_gil_slot function
ENDCASE()
CASE("old_slot_number_create")
PySlot_FUNC(1, demo_create),
ENDCASE()
CASE("new_slot_number_create")
PySlot_FUNC(84, demo_create),
ENDCASE()
CASE("nonstatic_mod_methods")
PySlot_DATA(Py_mod_methods, TestMethods),
ENDCASE()
CASE("intptr_methods")
PySlot_PTR_STATIC(Py_mod_methods, TestMethods),
ENDCASE()
CASE("nested")
PySlot_FUNC(Py_mod_exec, demo_exec),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_DATA(Py_mod_doc, "doc"),
PySlot_END,
})),
ENDCASE()
CASE("nested_max")
PySlot_FUNC(Py_mod_exec, demo_exec),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_SIZE(Py_mod_state_size, 53),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_DATA(Py_mod_doc, "doc"),
PySlot_END
})),
PySlot_END,
})),
PySlot_END,
})),
PySlot_END,
})),
ENDCASE()
CASE("nested_over_limit")
PySlot_FUNC(Py_mod_exec, demo_exec),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_SIZE(Py_mod_state_size, 53),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_DATA(Py_mod_doc, "doc"),
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
PySlot_END
})),
PySlot_END
})),
PySlot_END,
})),
PySlot_END,
})),
PySlot_END,
})),
ENDCASE()
CASE("nested_old")
PySlot_FUNC(Py_mod_exec, demo_exec),
PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
{Py_mod_doc, "doc"},
{0},
})),
ENDCASE()
CASE("nested_old_max")
PySlot_FUNC(Py_mod_exec, demo_exec),
PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
{Py_mod_slots, ((PyModuleDef_Slot[]) {
{Py_mod_state_size, (void*)(intptr_t)53},
{Py_mod_slots, ((PyModuleDef_Slot[]) {
{Py_mod_slots, ((PyModuleDef_Slot[]) {
{Py_mod_doc, "doc"},
{0},
})},
{0},
})},
{0},
})},
{0},
})),
ENDCASE()
CASE("nested_old_over_limit")
PySlot_FUNC(Py_mod_exec, demo_exec),
PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
{Py_mod_slots, ((PyModuleDef_Slot[]) {
{Py_mod_state_size, (void*)(intptr_t)53},
{Py_mod_slots, ((PyModuleDef_Slot[]) {
{Py_mod_slots, ((PyModuleDef_Slot[]) {
{Py_mod_slots, ((PyModuleDef_Slot[]) {
{Py_mod_doc, "doc"},
{0},
})},
{0},
})},
{0},
})},
{0},
})},
{0},
})),
ENDCASE()
CASE("nested_pingpong")
PySlot_FUNC(Py_mod_exec, demo_exec),
PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
{Py_slot_subslots, ((PySlot[]) {
PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
{Py_mod_state_size, (void*)(intptr_t)53},
{Py_slot_subslots, ((PySlot[]) {
PySlot_DATA(Py_mod_doc, "doc"),
PySlot_END
})},
{0},
})),
PySlot_END,
})},
{0},
})),
ENDCASE()
CASE("repeat_create")
PySlot_DATA(Py_mod_create, demo_create),
PySlot_DATA(Py_mod_create, demo_create),
PySlot_DATA(Py_mod_create, demo_create),
ENDCASE()
CASE("repeat_gil")
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
ENDCASE()
CASE("repeat_exec")
PySlot_FUNC(Py_mod_exec, demo_exec),
PySlot_FUNC(Py_mod_exec, demo_exec),
ENDCASE()
#undef CASE
#undef ENDCASE
if (!mod) {
if (!PyErr_Occurred()) {
PyErr_Format(PyExc_SystemError, "bad case: %s", case_name);
return NULL;
}
return NULL;
}
if (PyModule_Check(mod)) {
Py_ssize_t size;
if (PyModule_GetStateSize(mod, &size) < 0) {
Py_DECREF(mod);
return NULL;
}
if (PyModule_AddIntConstant(mod, "state_size", (long)size) < 0) {
Py_DECREF(mod);
return NULL;
}
if (PyModule_Exec(mod) < 0) {
return NULL;
}
}
return mod;
}
static PyObject *
module_from_gil_slot(PyObject* Py_UNUSED(module), PyObject *args)
{
long slot_number;
PyObject *spec;
if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) {
return NULL;
}
return PyModule_FromSlotsAndSpec((PySlot[]) {
PySlot_DATA(Py_mod_abi, &abi_info),
PySlot_PTR_STATIC((uint16_t)slot_number, Py_MOD_GIL_NOT_USED),
PySlot_END
}, spec);
}
static PyObject *
module_from_null_slot(PyObject* Py_UNUSED(module), PyObject *args)
{
long slot_number;
PyObject *spec;
if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) {
return NULL;
}
uint16_t maybe_gil_slot = Py_mod_gil;
if ((slot_number == 4) || (slot_number == 87)) {
// Do not repeat the GIL slot
maybe_gil_slot = Py_slot_invalid;
}
return PyModule_FromSlotsAndSpec((PySlot[]) {
PySlot_DATA(Py_mod_abi, &abi_info),
PySlot_DATA(Py_mod_name, "mymod"),
PySlot_PTR_STATIC((uint16_t)slot_number, NULL),
{
.sl_id=maybe_gil_slot,
.sl_flags=PySlot_OPTIONAL,
.sl_ptr=Py_MOD_GIL_NOT_USED,
},
PySlot_END
}, spec);
}
static PyMethodDef _TestMethods[] = {
{"type_from_slots", type_from_slots, METH_VARARGS},
{"module_from_gil_slot", module_from_gil_slot, METH_VARARGS},
{"type_from_null_slot", type_from_null_slot, METH_VARARGS},
{"type_from_null_spec_slot", type_from_null_spec_slot, METH_VARARGS},
{"module_from_slots", module_from_slots, METH_VARARGS},
{"module_from_null_slot", module_from_null_slot, METH_VARARGS},
{NULL},
};
static PyMethodDef *TestMethods = _TestMethods;
int
_PyTestLimitedCAPI_Init_Slots(PyObject *m)
{
if (PyModule_AddFunctions(m, TestMethods) < 0) {
return -1;
}
return 0;
}