/* Common handling of type/module slots */ #include "Python.h" #include "pycore_slots.h" #include // 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; }