mirror of
https://github.com/python/cpython.git
synced 2026-05-06 20:51:17 -04:00
508b49845d
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
405 lines
13 KiB
C
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;
|
|
}
|