/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.4k | while (true) { |
125 | 71.4k | 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 | 53.9k | case _PySlot_KIND_TYPE: { |
144 | 53.9k | MSG("converting PyType_Slot structure"); |
145 | 53.9k | memset(&it->current, 0, sizeof(it->current)); |
146 | 53.9k | it->current.sl_id = (uint16_t)it->state->tp_slot->slot; |
147 | 53.9k | it->current.sl_flags = PySlot_INTPTR; |
148 | 53.9k | it->current.sl_ptr = (void*)it->state->tp_slot->pfunc; |
149 | 53.9k | } 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 | 53.9k | case _PySlot_KIND_TYPE: |
172 | 53.9k | result->sl_id = _PySlot_resolve_type_slot(result->sl_id); |
173 | 53.9k | 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.1k | case _PySlot_DTYPE_PTR: |
259 | 51.6k | case _PySlot_DTYPE_FUNC: |
260 | 51.6k | case _PySlot_DTYPE_VOID: |
261 | 51.6k | 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.1k | case _PySlot_DTYPE_PTR: |
269 | 16.1k | MSG("result: %d (%s): %p", |
270 | 16.1k | (int)result->sl_id, _PySlot_GetName(result->sl_id), |
271 | 16.1k | (void*)result->sl_ptr); |
272 | 16.1k | break; |
273 | 35.4k | case _PySlot_DTYPE_FUNC: |
274 | 35.4k | MSG("result: %d (%s): %p", |
275 | 35.4k | (int)result->sl_id, _PySlot_GetName(result->sl_id), |
276 | 35.4k | (void*)result->sl_func); |
277 | 35.4k | 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.77k | if (!(it->current.sl_flags & PySlot_STATIC) |
317 | 4.77k | && (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.77k | } |
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 | } |