| /* 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; | |
| } |