Coverage Report

Created: 2026-05-30 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Python/slots.c
Line
Count
Source
1
/* Common handling of type/module slots
2
 */
3
4
#include "Python.h"
5
6
#include "pycore_slots.h"
7
8
#include <stdio.h>
9
10
// Iterating through a recursive structure doesn't look great in a debugger.
11
// Flip the #if to 1 to get a trace on stderr.
12
// (The messages can also serve as code comments.)
13
#if 0
14
#define MSG(...) { \
15
    fprintf(stderr, "slotiter: " __VA_ARGS__); fprintf(stderr, "\n");}
16
#else
17
#define MSG(...)
18
#endif
19
20
static char*
21
kind_name(_PySlot_KIND kind)
22
0
{
23
0
    switch (kind) {
24
0
        case _PySlot_KIND_TYPE: return "type";
25
0
        case _PySlot_KIND_MOD: return "module";
26
0
        case _PySlot_KIND_COMPAT: return "compat";
27
0
        case _PySlot_KIND_SLOT: return "generic slot";
28
0
    }
29
0
    Py_UNREACHABLE();
30
0
}
31
32
static void
33
init_with_kind(_PySlotIterator *it, const void *slots,
34
               _PySlot_KIND result_kind,
35
               _PySlot_KIND slot_struct_kind)
36
4.95k
{
37
4.95k
    MSG("");
38
4.95k
    MSG("init (%s slot iterator)", kind_name(result_kind));
39
4.95k
    it->state = it->states;
40
4.95k
    it->state->any_slot = slots;
41
4.95k
    it->state->slot_struct_kind = slot_struct_kind;
42
4.95k
    it->kind = result_kind;
43
4.95k
    it->name = NULL;
44
4.95k
    it->recursion_level = 0;
45
4.95k
    it->is_at_end = false;
46
4.95k
    it->is_first_run = true;
47
4.95k
    it->current.sl_id = 0;
48
4.95k
    memset(it->seen, 0, sizeof(it->seen));
49
4.95k
}
50
51
void
52
_PySlotIterator_Init(_PySlotIterator *it, const PySlot *slots,
53
                     _PySlot_KIND result_kind)
54
0
{
55
0
    init_with_kind(it, slots, result_kind, _PySlot_KIND_SLOT);
56
0
}
57
58
void
59
_PySlotIterator_InitLegacy(_PySlotIterator *it, const void *slots,
60
                           _PySlot_KIND kind)
61
4.95k
{
62
4.95k
    init_with_kind(it, slots, kind, kind);
63
4.95k
}
64
65
void
66
_PySlotIterator_Rewind(_PySlotIterator *it, const void *slots)
67
3.02k
{
68
3.02k
    MSG("");
69
3.02k
    MSG("rewind (%s slot iterator)", kind_name(it->kind));
70
3.02k
    assert (it->is_at_end);
71
3.02k
    assert (it->recursion_level == 0);
72
3.02k
    assert (it->state == it->states);
73
3.02k
    it->is_at_end = false;
74
3.02k
    it->state->any_slot = slots;
75
3.02k
    it->is_first_run = false;
76
3.02k
}
77
78
static Py_ssize_t
79
seen_index(uint16_t id)
80
62.4k
{
81
62.4k
    return id / _PySlot_SEEN_ENTRY_BITS;
82
62.4k
}
83
84
static unsigned int
85
seen_mask(uint16_t id)
86
62.4k
{
87
62.4k
    return ((unsigned int)1) << (id % _PySlot_SEEN_ENTRY_BITS);
88
62.4k
}
89
90
bool
91
_PySlotIterator_SawSlot(_PySlotIterator *it, int id)
92
30.8k
{
93
30.8k
    assert (id > 0);
94
30.8k
    assert (id < _Py_slot_COUNT);
95
30.8k
    return it->seen[seen_index(id)] & seen_mask(id);
96
30.8k
}
97
98
// Advance `it` to the next entry. Currently cannot fail.
99
static void
100
advance(_PySlotIterator *it)
101
55.5k
{
102
55.5k
    MSG("advance (at level %d)", (int)it->recursion_level);
103
55.5k
    switch (it->state->slot_struct_kind) {
104
0
        case _PySlot_KIND_SLOT: it->state->slot++; break;
105
47.9k
        case _PySlot_KIND_TYPE: it->state->tp_slot++; break;
106
7.60k
        case _PySlot_KIND_MOD: it->state->mod_slot++; break;
107
0
        default:
108
0
            Py_UNREACHABLE();
109
55.5k
    }
110
55.5k
}
111
112
static int handle_first_run(_PySlotIterator *it);
113
114
bool
115
_PySlotIterator_Next(_PySlotIterator *it)
116
63.5k
{
117
63.5k
    MSG("next");
118
63.5k
    assert(it);
119
63.5k
    assert(!it->is_at_end);
120
63.5k
    assert(!PyErr_Occurred());
121
122
63.5k
    it->current.sl_id = -1;
123
124
71.5k
    while (true) {
125
71.5k
        if (it->state->slot == NULL) {
126
7.97k
            if (it->recursion_level == 0) {
127
7.97k
                MSG("end (initial nesting level done)");
128
7.97k
                it->is_at_end = true;
129
7.97k
                return 0;
130
7.97k
            }
131
0
            MSG("pop nesting level %d", (int)it->recursion_level);
132
0
            it->recursion_level--;
133
0
            it->state = &it->states[it->recursion_level];
134
0
            advance(it);
135
0
            continue;
136
7.97k
        }
137
138
63.5k
        switch (it->state->slot_struct_kind) {
139
0
            case _PySlot_KIND_SLOT: {
140
0
                MSG("copying PySlot structure");
141
0
                it->current = *it->state->slot;
142
0
            } break;
143
54.0k
            case _PySlot_KIND_TYPE: {
144
54.0k
                MSG("converting PyType_Slot structure");
145
54.0k
                memset(&it->current, 0, sizeof(it->current));
146
54.0k
                it->current.sl_id = (uint16_t)it->state->tp_slot->slot;
147
54.0k
                it->current.sl_flags = PySlot_INTPTR;
148
54.0k
                it->current.sl_ptr = (void*)it->state->tp_slot->pfunc;
149
54.0k
            } break;
150
9.53k
            case _PySlot_KIND_MOD: {
151
9.53k
                MSG("converting PyModuleDef_Slot structure");
152
9.53k
                memset(&it->current, 0, sizeof(it->current));
153
9.53k
                it->current.sl_id = (uint16_t)it->state->mod_slot->slot;
154
9.53k
                it->current.sl_flags = PySlot_INTPTR;
155
9.53k
                it->current.sl_ptr = (void*)it->state->mod_slot->value;
156
9.53k
            } break;
157
0
            default: {
158
0
                Py_UNREACHABLE();
159
0
            } break;
160
63.5k
        }
161
162
        /* shorter local names */
163
63.5k
        PySlot *const result = &it->current;
164
63.5k
        uint16_t flags = result->sl_flags;
165
166
63.5k
        MSG("slot %d, flags 0x%x, from %p",
167
63.5k
            (int)result->sl_id, (unsigned)flags, it->state->slot);
168
169
63.5k
        uint16_t orig_id = result->sl_id;
170
63.5k
        switch (it->kind) {
171
54.0k
            case _PySlot_KIND_TYPE:
172
54.0k
                result->sl_id = _PySlot_resolve_type_slot(result->sl_id);
173
54.0k
                break;
174
9.53k
            case _PySlot_KIND_MOD:
175
9.53k
                result->sl_id = _PySlot_resolve_mod_slot(result->sl_id);
176
9.53k
                break;
177
0
            default:
178
0
                Py_UNREACHABLE();
179
63.5k
        }
180
63.5k
        MSG("resolved to slot %d (%s)",
181
63.5k
            (int)result->sl_id, _PySlot_GetName(result->sl_id));
182
183
63.5k
        if (result->sl_id == Py_slot_invalid) {
184
0
            MSG("error (unknown/invalid slot)");
185
0
            if (flags & PySlot_OPTIONAL) {
186
0
                advance(it);
187
0
                continue;
188
0
            }
189
0
            _PySlot_err_bad_slot(kind_name(it->kind), orig_id);
190
0
            goto error;
191
0
        }
192
63.5k
        if (result->sl_id == Py_slot_end) {
193
7.97k
            MSG("sentinel slot, flags %x", (unsigned)flags);
194
7.97k
            if (flags & PySlot_OPTIONAL) {
195
0
                MSG("error (bad flags on sentinel)");
196
0
                PyErr_Format(PyExc_SystemError,
197
0
                            "invalid flags for Py_slot_end: 0x%x",
198
0
                             (unsigned int)flags);
199
0
                goto error;
200
0
            }
201
7.97k
            it->state->slot = NULL;
202
7.97k
            continue;
203
7.97k
        }
204
205
55.5k
        if (result->sl_id == Py_slot_subslots
206
55.5k
            || result->sl_id == Py_tp_slots
207
55.5k
            || result->sl_id == Py_mod_slots
208
55.5k
        ) {
209
0
            if (result->sl_ptr == NULL) {
210
0
                MSG("NULL subslots; skipping");
211
0
                advance(it);
212
0
                continue;
213
0
            }
214
0
            if ((it->states[0].slot_struct_kind == _PySlot_KIND_MOD)
215
0
                && (it->state->slot_struct_kind == _PySlot_KIND_SLOT)
216
0
                && !(result->sl_flags & PySlot_STATIC))
217
0
            {
218
0
                PyErr_Format(PyExc_SystemError,
219
0
                             "slots included from PyModuleDef must be static");
220
0
                goto error;
221
0
            }
222
0
            it->recursion_level++;
223
0
            MSG("recursing into level %d", it->recursion_level);
224
0
            if (it->recursion_level >= _PySlot_MAX_NESTING) {
225
0
                MSG("error (too much nesting)");
226
0
                PyErr_Format(PyExc_SystemError,
227
0
                            "%s (slot %d): too many levels of nested slots",
228
0
                            _PySlot_GetName(result->sl_id), orig_id);
229
0
                goto error;
230
0
            }
231
0
            it->state = &it->states[it->recursion_level];
232
0
            memset(it->state, 0, sizeof(_PySlotIterator_state));
233
0
            it->state->slot = result->sl_ptr;
234
0
            switch (result->sl_id) {
235
0
                case Py_slot_subslots:
236
0
                    it->state->slot_struct_kind = _PySlot_KIND_SLOT; break;
237
0
                case Py_tp_slots:
238
0
                    it->state->slot_struct_kind = _PySlot_KIND_TYPE; break;
239
0
                case Py_mod_slots:
240
0
                    it->state->slot_struct_kind = _PySlot_KIND_MOD; break;
241
0
            }
242
0
            continue;
243
0
        }
244
245
55.5k
        if (flags & PySlot_INTPTR) {
246
55.5k
            MSG("casting from intptr");
247
            /* this should compile to nothing on common architectures */
248
55.5k
            switch (_PySlot_get_dtype(result->sl_id)) {
249
0
                case _PySlot_DTYPE_SIZE: {
250
0
                    result->sl_size = (Py_ssize_t)(intptr_t)result->sl_ptr;
251
0
                } break;
252
0
                case _PySlot_DTYPE_INT64: {
253
0
                    result->sl_int64 = (int64_t)(intptr_t)result->sl_ptr;
254
0
                } break;
255
3.86k
                case _PySlot_DTYPE_UINT64: {
256
3.86k
                    result->sl_uint64 = (uint64_t)(intptr_t)result->sl_ptr;
257
3.86k
                } break;
258
16.2k
                case _PySlot_DTYPE_PTR:
259
51.7k
                case _PySlot_DTYPE_FUNC:
260
51.7k
                case _PySlot_DTYPE_VOID:
261
51.7k
                    break;
262
55.5k
            }
263
55.5k
        }
264
265
55.5k
        advance(it);
266
55.5k
        switch (_PySlot_get_dtype(result->sl_id)) {
267
0
            case _PySlot_DTYPE_VOID:
268
16.2k
            case _PySlot_DTYPE_PTR:
269
16.2k
                MSG("result: %d (%s): %p",
270
16.2k
                    (int)result->sl_id, _PySlot_GetName(result->sl_id),
271
16.2k
                    (void*)result->sl_ptr);
272
16.2k
                break;
273
35.5k
            case _PySlot_DTYPE_FUNC:
274
35.5k
                MSG("result: %d (%s): %p",
275
35.5k
                    (int)result->sl_id, _PySlot_GetName(result->sl_id),
276
35.5k
                    (void*)result->sl_func);
277
35.5k
                break;
278
0
            case _PySlot_DTYPE_SIZE:
279
0
                MSG("result: %d (%s): %zd",
280
0
                    (int)result->sl_id, _PySlot_GetName(result->sl_id),
281
0
                    (Py_ssize_t)result->sl_size);
282
0
                break;
283
0
            case _PySlot_DTYPE_INT64:
284
0
                MSG("result: %d (%s): %ld",
285
0
                    (int)result->sl_id,  _PySlot_GetName(result->sl_id),
286
0
                    (long)result->sl_int64);
287
0
                break;
288
3.86k
            case _PySlot_DTYPE_UINT64:
289
3.86k
                MSG("result: %d (%s): %lu (0x%lx)",
290
3.86k
                    (int)result->sl_id, _PySlot_GetName(result->sl_id),
291
3.86k
                    (unsigned long)result->sl_int64,
292
3.86k
                    (unsigned long)result->sl_int64);
293
3.86k
                break;
294
55.5k
        }
295
55.5k
        assert (result->sl_id > 0);
296
55.5k
        assert (result->sl_id <= _Py_slot_COUNT);
297
55.5k
        if (it->is_first_run && (handle_first_run(it) < 0)) {
298
0
            goto error;
299
0
        }
300
55.5k
        return result->sl_id != Py_slot_end;
301
55.5k
    }
302
63.5k
    Py_UNREACHABLE();
303
304
0
error:
305
0
    it->current.sl_id = Py_slot_invalid;
306
0
    return true;
307
63.5k
}
308
309
/* Validate current slot, and do bookkeeping */
310
static int
311
handle_first_run(_PySlotIterator *it)
312
31.5k
{
313
31.5k
    int id = it->current.sl_id;
314
315
31.5k
    if (_PySlot_get_must_be_static(id)) {
316
4.78k
        if (!(it->current.sl_flags & PySlot_STATIC)
317
4.78k
            && (it->state->slot_struct_kind == _PySlot_KIND_SLOT))
318
0
        {
319
0
            PyErr_Format(
320
0
                PyExc_SystemError,
321
0
                "%s requires PySlot_STATIC",
322
0
                _PySlot_GetName(id));
323
0
            return -1;
324
0
        }
325
4.78k
    }
326
327
31.5k
    _PySlot_PROBLEM_HANDLING null_handling = _PySlot_get_null_handling(id);
328
31.5k
    if (null_handling != _PySlot_PROBLEM_ALLOW) {
329
25.2k
        bool is_null = false;
330
25.2k
        switch (_PySlot_get_dtype(id)) {
331
6.55k
            case _PySlot_DTYPE_PTR: {
332
6.55k
                is_null = it->current.sl_ptr == NULL;
333
6.55k
            } break;
334
18.7k
            case _PySlot_DTYPE_FUNC: {
335
18.7k
                is_null = it->current.sl_func == NULL;
336
18.7k
            } break;
337
0
            default: {
338
                //Py_UNREACHABLE();
339
0
            } break;
340
25.2k
        }
341
25.2k
        if (is_null) {
342
0
            MSG("slot is NULL but shouldn't");
343
0
            if (null_handling == _PySlot_PROBLEM_REJECT) {
344
0
                MSG("error (NULL rejected)");
345
0
                PyErr_Format(PyExc_SystemError,
346
0
                             "NULL not allowed for slot %s",
347
0
                             _PySlot_GetName(id));
348
0
                return -1;
349
0
            }
350
0
            if (it->states[0].slot_struct_kind == _PySlot_KIND_SLOT) {
351
0
                MSG("deprecated NULL");
352
0
                if (PyErr_WarnFormat(
353
0
                    PyExc_DeprecationWarning,
354
0
                    1,
355
0
                    "NULL value in slot %s is deprecated",
356
0
                    _PySlot_GetName(id)) < 0)
357
0
                {
358
0
                    return -1;
359
0
                }
360
0
            }
361
0
            else {
362
0
                MSG("unwanted NULL in legacy struct");
363
0
            }
364
0
        }
365
25.2k
    }
366
367
31.5k
    _PySlot_PROBLEM_HANDLING duplicate_handling = _PySlot_get_duplicate_handling(id);
368
31.5k
    if (duplicate_handling != _PySlot_PROBLEM_ALLOW) {
369
27.8k
        if (_PySlotIterator_SawSlot(it, id)) {
370
0
            MSG("slot was seen before but shouldn't be duplicated");
371
0
            if (duplicate_handling == _PySlot_PROBLEM_REJECT) {
372
0
                MSG("error (duplicate rejected)");
373
0
                PyErr_Format(
374
0
                    PyExc_SystemError,
375
0
                    "%s%s%s has multiple %s (%d) slots",
376
0
                    kind_name(it->kind),
377
0
                    it->name ? " " : "",
378
0
                    it->name ? it->name : "",
379
0
                    _PySlot_GetName(id),
380
0
                    (int)it->current.sl_id);
381
0
                return -1;
382
0
            }
383
0
            if (it->states[0].slot_struct_kind == _PySlot_KIND_SLOT) {
384
0
                MSG("deprecated duplicate");
385
0
                if (PyErr_WarnFormat(
386
0
                        PyExc_DeprecationWarning,
387
0
                        0,
388
0
                        "%s%s%s has multiple %s (%d) slots. This is deprecated.",
389
0
                        kind_name(it->kind),
390
0
                        it->name ? " " : "",
391
0
                        it->name ? it->name : "",
392
0
                        _PySlot_GetName(id),
393
0
                        (int)it->current.sl_id) < 0) {
394
0
                    return -1;
395
0
                }
396
0
            }
397
0
            else {
398
0
                MSG("unwanted duplicate in legacy struct");
399
0
            }
400
0
        }
401
27.8k
    }
402
31.5k
    it->seen[seen_index(id)] |= seen_mask(id);
403
31.5k
    return 0;
404
31.5k
}