Files
2026-05-05 09:18:04 +02:00

405 lines
13 KiB
C

/* Common handling of type/module slots
*/
#include "Python.h"
#include "pycore_slots.h"
#include <stdio.h>
// Iterating through a recursive structure doesn't look great in a debugger.
// Flip the #if to 1 to get a trace on stderr.
// (The messages can also serve as code comments.)
#if 0
#define MSG(...) { \
fprintf(stderr, "slotiter: " __VA_ARGS__); fprintf(stderr, "\n");}
#else
#define MSG(...)
#endif
static char*
kind_name(_PySlot_KIND kind)
{
switch (kind) {
case _PySlot_KIND_TYPE: return "type";
case _PySlot_KIND_MOD: return "module";
case _PySlot_KIND_COMPAT: return "compat";
case _PySlot_KIND_SLOT: return "generic slot";
}
Py_UNREACHABLE();
}
static void
init_with_kind(_PySlotIterator *it, const void *slots,
_PySlot_KIND result_kind,
_PySlot_KIND slot_struct_kind)
{
MSG("");
MSG("init (%s slot iterator)", kind_name(result_kind));
it->state = it->states;
it->state->any_slot = slots;
it->state->slot_struct_kind = slot_struct_kind;
it->kind = result_kind;
it->name = NULL;
it->recursion_level = 0;
it->is_at_end = false;
it->is_first_run = true;
it->current.sl_id = 0;
memset(it->seen, 0, sizeof(it->seen));
}
void
_PySlotIterator_Init(_PySlotIterator *it, const PySlot *slots,
_PySlot_KIND result_kind)
{
init_with_kind(it, slots, result_kind, _PySlot_KIND_SLOT);
}
void
_PySlotIterator_InitLegacy(_PySlotIterator *it, const void *slots,
_PySlot_KIND kind)
{
init_with_kind(it, slots, kind, kind);
}
void
_PySlotIterator_Rewind(_PySlotIterator *it, const void *slots)
{
MSG("");
MSG("rewind (%s slot iterator)", kind_name(it->kind));
assert (it->is_at_end);
assert (it->recursion_level == 0);
assert (it->state == it->states);
it->is_at_end = false;
it->state->any_slot = slots;
it->is_first_run = false;
}
static Py_ssize_t
seen_index(uint16_t id)
{
return id / _PySlot_SEEN_ENTRY_BITS;
}
static unsigned int
seen_mask(uint16_t id)
{
return ((unsigned int)1) << (id % _PySlot_SEEN_ENTRY_BITS);
}
bool
_PySlotIterator_SawSlot(_PySlotIterator *it, int id)
{
assert (id > 0);
assert (id < _Py_slot_COUNT);
return it->seen[seen_index(id)] & seen_mask(id);
}
// Advance `it` to the next entry. Currently cannot fail.
static void
advance(_PySlotIterator *it)
{
MSG("advance (at level %d)", (int)it->recursion_level);
switch (it->state->slot_struct_kind) {
case _PySlot_KIND_SLOT: it->state->slot++; break;
case _PySlot_KIND_TYPE: it->state->tp_slot++; break;
case _PySlot_KIND_MOD: it->state->mod_slot++; break;
default:
Py_UNREACHABLE();
}
}
static int handle_first_run(_PySlotIterator *it);
bool
_PySlotIterator_Next(_PySlotIterator *it)
{
MSG("next");
assert(it);
assert(!it->is_at_end);
assert(!PyErr_Occurred());
it->current.sl_id = -1;
while (true) {
if (it->state->slot == NULL) {
if (it->recursion_level == 0) {
MSG("end (initial nesting level done)");
it->is_at_end = true;
return 0;
}
MSG("pop nesting level %d", (int)it->recursion_level);
it->recursion_level--;
it->state = &it->states[it->recursion_level];
advance(it);
continue;
}
switch (it->state->slot_struct_kind) {
case _PySlot_KIND_SLOT: {
MSG("copying PySlot structure");
it->current = *it->state->slot;
} break;
case _PySlot_KIND_TYPE: {
MSG("converting PyType_Slot structure");
memset(&it->current, 0, sizeof(it->current));
it->current.sl_id = (uint16_t)it->state->tp_slot->slot;
it->current.sl_flags = PySlot_INTPTR;
it->current.sl_ptr = (void*)it->state->tp_slot->pfunc;
} break;
case _PySlot_KIND_MOD: {
MSG("converting PyModuleDef_Slot structure");
memset(&it->current, 0, sizeof(it->current));
it->current.sl_id = (uint16_t)it->state->mod_slot->slot;
it->current.sl_flags = PySlot_INTPTR;
it->current.sl_ptr = (void*)it->state->mod_slot->value;
} break;
default: {
Py_UNREACHABLE();
} break;
}
/* shorter local names */
PySlot *const result = &it->current;
uint16_t flags = result->sl_flags;
MSG("slot %d, flags 0x%x, from %p",
(int)result->sl_id, (unsigned)flags, it->state->slot);
uint16_t orig_id = result->sl_id;
switch (it->kind) {
case _PySlot_KIND_TYPE:
result->sl_id = _PySlot_resolve_type_slot(result->sl_id);
break;
case _PySlot_KIND_MOD:
result->sl_id = _PySlot_resolve_mod_slot(result->sl_id);
break;
default:
Py_UNREACHABLE();
}
MSG("resolved to slot %d (%s)",
(int)result->sl_id, _PySlot_GetName(result->sl_id));
if (result->sl_id == Py_slot_invalid) {
MSG("error (unknown/invalid slot)");
if (flags & PySlot_OPTIONAL) {
advance(it);
continue;
}
_PySlot_err_bad_slot(kind_name(it->kind), orig_id);
goto error;
}
if (result->sl_id == Py_slot_end) {
MSG("sentinel slot, flags %x", (unsigned)flags);
if (flags & PySlot_OPTIONAL) {
MSG("error (bad flags on sentinel)");
PyErr_Format(PyExc_SystemError,
"invalid flags for Py_slot_end: 0x%x",
(unsigned int)flags);
goto error;
}
it->state->slot = NULL;
continue;
}
if (result->sl_id == Py_slot_subslots
|| result->sl_id == Py_tp_slots
|| result->sl_id == Py_mod_slots
) {
if (result->sl_ptr == NULL) {
MSG("NULL subslots; skipping");
advance(it);
continue;
}
if ((it->states[0].slot_struct_kind == _PySlot_KIND_MOD)
&& (it->state->slot_struct_kind == _PySlot_KIND_SLOT)
&& !(result->sl_flags & PySlot_STATIC))
{
PyErr_Format(PyExc_SystemError,
"slots included from PyModuleDef must be static");
goto error;
}
it->recursion_level++;
MSG("recursing into level %d", it->recursion_level);
if (it->recursion_level >= _PySlot_MAX_NESTING) {
MSG("error (too much nesting)");
PyErr_Format(PyExc_SystemError,
"%s (slot %d): too many levels of nested slots",
_PySlot_GetName(result->sl_id), orig_id);
goto error;
}
it->state = &it->states[it->recursion_level];
memset(it->state, 0, sizeof(_PySlotIterator_state));
it->state->slot = result->sl_ptr;
switch (result->sl_id) {
case Py_slot_subslots:
it->state->slot_struct_kind = _PySlot_KIND_SLOT; break;
case Py_tp_slots:
it->state->slot_struct_kind = _PySlot_KIND_TYPE; break;
case Py_mod_slots:
it->state->slot_struct_kind = _PySlot_KIND_MOD; break;
}
continue;
}
if (flags & PySlot_INTPTR) {
MSG("casting from intptr");
/* this should compile to nothing on common architectures */
switch (_PySlot_get_dtype(result->sl_id)) {
case _PySlot_DTYPE_SIZE: {
result->sl_size = (Py_ssize_t)(intptr_t)result->sl_ptr;
} break;
case _PySlot_DTYPE_INT64: {
result->sl_int64 = (int64_t)(intptr_t)result->sl_ptr;
} break;
case _PySlot_DTYPE_UINT64: {
result->sl_uint64 = (uint64_t)(intptr_t)result->sl_ptr;
} break;
case _PySlot_DTYPE_PTR:
case _PySlot_DTYPE_FUNC:
case _PySlot_DTYPE_VOID:
break;
}
}
advance(it);
switch (_PySlot_get_dtype(result->sl_id)) {
case _PySlot_DTYPE_VOID:
case _PySlot_DTYPE_PTR:
MSG("result: %d (%s): %p",
(int)result->sl_id, _PySlot_GetName(result->sl_id),
(void*)result->sl_ptr);
break;
case _PySlot_DTYPE_FUNC:
MSG("result: %d (%s): %p",
(int)result->sl_id, _PySlot_GetName(result->sl_id),
(void*)result->sl_func);
break;
case _PySlot_DTYPE_SIZE:
MSG("result: %d (%s): %zd",
(int)result->sl_id, _PySlot_GetName(result->sl_id),
(Py_ssize_t)result->sl_size);
break;
case _PySlot_DTYPE_INT64:
MSG("result: %d (%s): %ld",
(int)result->sl_id, _PySlot_GetName(result->sl_id),
(long)result->sl_int64);
break;
case _PySlot_DTYPE_UINT64:
MSG("result: %d (%s): %lu (0x%lx)",
(int)result->sl_id, _PySlot_GetName(result->sl_id),
(unsigned long)result->sl_int64,
(unsigned long)result->sl_int64);
break;
}
assert (result->sl_id > 0);
assert (result->sl_id <= _Py_slot_COUNT);
if (it->is_first_run && (handle_first_run(it) < 0)) {
goto error;
}
return result->sl_id != Py_slot_end;
}
Py_UNREACHABLE();
error:
it->current.sl_id = Py_slot_invalid;
return true;
}
/* Validate current slot, and do bookkeeping */
static int
handle_first_run(_PySlotIterator *it)
{
int id = it->current.sl_id;
if (_PySlot_get_must_be_static(id)) {
if (!(it->current.sl_flags & PySlot_STATIC)
&& (it->state->slot_struct_kind == _PySlot_KIND_SLOT))
{
PyErr_Format(
PyExc_SystemError,
"%s requires PySlot_STATIC",
_PySlot_GetName(id));
return -1;
}
}
_PySlot_PROBLEM_HANDLING null_handling = _PySlot_get_null_handling(id);
if (null_handling != _PySlot_PROBLEM_ALLOW) {
bool is_null = false;
switch (_PySlot_get_dtype(id)) {
case _PySlot_DTYPE_PTR: {
is_null = it->current.sl_ptr == NULL;
} break;
case _PySlot_DTYPE_FUNC: {
is_null = it->current.sl_func == NULL;
} break;
default: {
//Py_UNREACHABLE();
} break;
}
if (is_null) {
MSG("slot is NULL but shouldn't");
if (null_handling == _PySlot_PROBLEM_REJECT) {
MSG("error (NULL rejected)");
PyErr_Format(PyExc_SystemError,
"NULL not allowed for slot %s",
_PySlot_GetName(id));
return -1;
}
if (it->states[0].slot_struct_kind == _PySlot_KIND_SLOT) {
MSG("deprecated NULL");
if (PyErr_WarnFormat(
PyExc_DeprecationWarning,
1,
"NULL value in slot %s is deprecated",
_PySlot_GetName(id)) < 0)
{
return -1;
}
}
else {
MSG("unwanted NULL in legacy struct");
}
}
}
_PySlot_PROBLEM_HANDLING duplicate_handling = _PySlot_get_duplicate_handling(id);
if (duplicate_handling != _PySlot_PROBLEM_ALLOW) {
if (_PySlotIterator_SawSlot(it, id)) {
MSG("slot was seen before but shouldn't be duplicated");
if (duplicate_handling == _PySlot_PROBLEM_REJECT) {
MSG("error (duplicate rejected)");
PyErr_Format(
PyExc_SystemError,
"%s%s%s has multiple %s (%d) slots",
kind_name(it->kind),
it->name ? " " : "",
it->name ? it->name : "",
_PySlot_GetName(id),
(int)it->current.sl_id);
return -1;
}
if (it->states[0].slot_struct_kind == _PySlot_KIND_SLOT) {
MSG("deprecated duplicate");
if (PyErr_WarnFormat(
PyExc_DeprecationWarning,
0,
"%s%s%s has multiple %s (%d) slots. This is deprecated.",
kind_name(it->kind),
it->name ? " " : "",
it->name ? it->name : "",
_PySlot_GetName(id),
(int)it->current.sl_id) < 0) {
return -1;
}
}
else {
MSG("unwanted duplicate in legacy struct");
}
}
}
it->seen[seen_index(id)] |= seen_mask(id);
return 0;
}