Nuitka
The Python compiler
Loading...
Searching...
No Matches
exceptions.h
1// Copyright 2025, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
3#ifndef __NUITKA_EXCEPTIONS_H__
4#define __NUITKA_EXCEPTIONS_H__
5
6// Exception helpers for generated code and compiled code helpers.
7
8// Fundamental, because we use it for print style debugging in everything.
9#include "nuitka/checkers.h"
10#include "nuitka/constants.h"
11#include "nuitka/printing.h"
12
13// Did an error occur.
14NUITKA_MAY_BE_UNUSED static inline bool HAS_ERROR_OCCURRED(PyThreadState const *tstate) {
15#if PYTHON_VERSION < 0x3c0
16 return tstate->curexc_type != NULL;
17#else
18 return tstate->current_exception != NULL;
19#endif
20}
21
22// Get the error type occurred, no reference given.
23NUITKA_MAY_BE_UNUSED static inline PyObject *GET_ERROR_OCCURRED(PyThreadState const *const tstate) {
24#if PYTHON_VERSION < 0x3c0
25 return tstate->curexc_type;
26#else
27 return tstate->current_exception ? (PyObject *)PyExceptionInstance_Class(tstate->current_exception) : NULL;
28#endif
29}
30
31// Checks that an exception value is normalized or NULL.
32NUITKA_MAY_BE_UNUSED static inline void ASSERT_NORMALIZED_EXCEPTION_VALUE_X(PyObject const *const exception_value) {
33 CHECK_OBJECT_X(exception_value);
34 assert(exception_value == NULL || PyExceptionInstance_Check(exception_value));
35}
36
37// Checks that an exception value is normalized and not NULL.
38NUITKA_MAY_BE_UNUSED static inline void ASSERT_NORMALIZED_EXCEPTION_VALUE(PyObject *exception_value) {
39 CHECK_OBJECT(exception_value);
40 assert(PyExceptionInstance_Check(exception_value));
41}
42
43// Clear error, which likely set, similar to "_PyErr_Clear(tstate)" and "PyErr_Clear"
44NUITKA_MAY_BE_UNUSED static inline void CLEAR_ERROR_OCCURRED(PyThreadState *tstate) {
45#if PYTHON_VERSION < 0x3c0
46 PyObject *old_type = tstate->curexc_type;
47 PyObject *old_value = tstate->curexc_value;
48 PyObject *old_tb = tstate->curexc_traceback;
49
50 tstate->curexc_type = NULL;
51 tstate->curexc_value = NULL;
52 tstate->curexc_traceback = NULL;
53
54 Py_XDECREF(old_type);
55 Py_XDECREF(old_value);
56 Py_XDECREF(old_tb);
57#else
58 PyObject *old_exception_value = tstate->current_exception;
59 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(old_exception_value);
60
61 tstate->current_exception = NULL;
62
63 Py_XDECREF(old_exception_value);
64#endif
65}
66
67// Clear error, which is not likely set, use "CLEAR_ERROR_OCCURRED" not sure there is an error.
68NUITKA_MAY_BE_UNUSED static inline bool DROP_ERROR_OCCURRED(PyThreadState *tstate) {
69
70#if PYTHON_VERSION < 0x3c0
71 if (unlikely(tstate->curexc_type != NULL)) {
72 PyObject *old_type = tstate->curexc_type;
73 PyObject *old_value = tstate->curexc_value;
74 PyObject *old_tb = tstate->curexc_traceback;
75
76 tstate->curexc_type = NULL;
77 tstate->curexc_value = NULL;
78 tstate->curexc_traceback = NULL;
79
80 Py_DECREF(old_type);
81 Py_XDECREF(old_value);
82 Py_XDECREF(old_tb);
83
84 return true;
85 }
86#else
87 if (unlikely(tstate->current_exception != NULL)) {
88 PyObject *old_exception_value = tstate->current_exception;
89 ASSERT_NORMALIZED_EXCEPTION_VALUE(old_exception_value);
90
91 tstate->current_exception = NULL;
92
93 Py_DECREF(old_exception_value);
94
95 return true;
96 }
97#endif
98 return false;
99}
100
101#if PYTHON_VERSION < 0x3c0
102// Fetch the current error into object variables, transfers reference coming from tstate ownership
103NUITKA_MAY_BE_UNUSED static void FETCH_ERROR_OCCURRED(PyThreadState *tstate, PyObject **exception_type,
104 PyObject **exception_value,
105 PyTracebackObject **exception_traceback) {
106 *exception_type = tstate->curexc_type;
107 *exception_value = tstate->curexc_value;
108 *exception_traceback = (PyTracebackObject *)tstate->curexc_traceback;
109
110#if _DEBUG_EXCEPTIONS
111 PRINT_STRING("FETCH_ERROR_OCCURRED:\n");
112 PRINT_CURRENT_EXCEPTION();
113#endif
114
115 tstate->curexc_type = NULL;
116 tstate->curexc_value = NULL;
117 tstate->curexc_traceback = NULL;
118}
119
120// Fetch the current error into object variables.
121NUITKA_MAY_BE_UNUSED static void FETCH_ERROR_OCCURRED_UNTRACED(PyThreadState *tstate, PyObject **exception_type,
122 PyObject **exception_value,
123 PyTracebackObject **exception_traceback) {
124 *exception_type = tstate->curexc_type;
125 *exception_value = tstate->curexc_value;
126 *exception_traceback = (PyTracebackObject *)tstate->curexc_traceback;
127
128 tstate->curexc_type = NULL;
129 tstate->curexc_value = NULL;
130 tstate->curexc_traceback = NULL;
131}
132
133NUITKA_MAY_BE_UNUSED static void RESTORE_ERROR_OCCURRED(PyThreadState *tstate, PyObject *exception_type,
134 PyObject *exception_value,
135 PyTracebackObject *exception_traceback) {
136 PyObject *old_exception_type = tstate->curexc_type;
137 PyObject *old_exception_value = tstate->curexc_value;
138 PyObject *old_exception_traceback = tstate->curexc_traceback;
139
140 tstate->curexc_type = exception_type;
141 tstate->curexc_value = exception_value;
142 tstate->curexc_traceback = (PyObject *)exception_traceback;
143
144#if _DEBUG_EXCEPTIONS
145 PRINT_STRING("RESTORE_ERROR_OCCURRED:\n");
146 PRINT_CURRENT_EXCEPTION();
147#endif
148
149 Py_XDECREF(old_exception_type);
150 Py_XDECREF(old_exception_value);
151 Py_XDECREF(old_exception_traceback);
152}
153
154NUITKA_MAY_BE_UNUSED static void RESTORE_ERROR_OCCURRED_UNTRACED(PyThreadState *tstate, PyObject *exception_type,
155 PyObject *exception_value,
156 PyTracebackObject *exception_traceback) {
157 PyObject *old_exception_type = tstate->curexc_type;
158 PyObject *old_exception_value = tstate->curexc_value;
159 PyObject *old_exception_traceback = tstate->curexc_traceback;
160
161 tstate->curexc_type = exception_type;
162 tstate->curexc_value = exception_value;
163 tstate->curexc_traceback = (PyObject *)exception_traceback;
164
165 Py_XDECREF(old_exception_type);
166 Py_XDECREF(old_exception_value);
167 Py_XDECREF(old_exception_traceback);
168}
169#endif
170
171struct Nuitka_FrameObject;
172
173extern PyTracebackObject *MAKE_TRACEBACK(struct Nuitka_FrameObject *frame, int lineno);
174
175// Add a frame to an existing exception trace-back.
176NUITKA_MAY_BE_UNUSED static PyTracebackObject *ADD_TRACEBACK(PyTracebackObject *exception_tb,
177 struct Nuitka_FrameObject *frame, int lineno) {
178 CHECK_OBJECT(exception_tb);
179 CHECK_OBJECT(frame);
180
181 PyTracebackObject *traceback_new = MAKE_TRACEBACK(frame, lineno);
182 traceback_new->tb_next = exception_tb;
183 Py_INCREF(exception_tb);
184 return traceback_new;
185}
186
187// Need some wrapper functions for accessing exception type, value, and traceback
188// due to changes in Python 3.7
189
190#if PYTHON_VERSION < 0x370
191#define EXC_TYPE(x) (x->exc_type)
192#define EXC_VALUE(x) (x->exc_value)
193#define EXC_TRACEBACK(x) ((PyTracebackObject *)(x->exc_traceback))
194#define EXC_TRACEBACK_PTR(x) ((PyTracebackObject **)(&x->exc_traceback))
195#define SET_EXC_TRACEBACK(x, tb) x->exc_traceback = (PyObject *)tb
196#elif PYTHON_VERSION < 0x3b0
197#define EXC_TYPE(x) (x->exc_state.exc_type)
198#define EXC_VALUE(x) (x->exc_state.exc_value)
199#define EXC_TRACEBACK(x) ((PyTracebackObject *)(x->exc_state.exc_traceback))
200#define EXC_TRACEBACK_PTR(x) ((PyTracebackObject **)(&x->exc_state.exc_traceback))
201#define SET_EXC_TRACEBACK(x, tb) x->exc_state.exc_traceback = (PyObject *)tb
202#else
203#define EXC_TYPE(x) ((PyObject *)Py_TYPE(x->exc_state.exc_value))
204#define EXC_VALUE(x) (x->exc_state.exc_value)
205#endif
206
207#if PYTHON_VERSION < 0x370
208#define EXC_TYPE_F(x) (x->m_frame->m_frame.f_exc_type)
209#define EXC_VALUE_F(x) (x->m_frame->m_frame.f_exc_value)
210#define EXC_TRACEBACK_F(x) (x->m_frame->m_frame.f_exc_traceback)
211#define ASSIGN_EXC_TRACEBACK_F(x, tb) x->m_frame->m_frame.f_exc_traceback = (PyObject *)(tb)
212#elif PYTHON_VERSION < 0x3b0
213#define EXC_TYPE_F(x) (x->m_exc_state.exception_type)
214#define EXC_VALUE_F(x) (x->m_exc_state.exception_value)
215#define EXC_TRACEBACK_F(x) (x->m_exc_state.exception_tb)
216#define ASSIGN_EXC_TRACEBACK_F(x, tb) x->m_exc_state.exception_tb = (PyTracebackObject *)(tb)
217#else
218#define EXC_VALUE_F(x) (x->m_exc_state.exception_value)
219#endif
220
221#if PYTHON_VERSION < 0x3b0
223 PyObject *exception_type;
224 PyObject *exception_value;
225 PyTracebackObject *exception_tb;
226};
227
228#if defined(__cplusplus)
229static const struct Nuitka_ExceptionStackItem Nuitka_ExceptionStackItem_Empty = {NULL, NULL, NULL};
230#else
231#define Nuitka_ExceptionStackItem_Empty \
232 (struct Nuitka_ExceptionStackItem) { .exception_type = NULL, .exception_value = NULL, .exception_tb = NULL }
233#endif
234#else
236 PyObject *exception_value;
237};
238
239#if defined(__cplusplus)
240static const struct Nuitka_ExceptionStackItem Nuitka_ExceptionStackItem_Empty = {NULL};
241#else
242#define Nuitka_ExceptionStackItem_Empty \
243 (struct Nuitka_ExceptionStackItem) { .exception_value = NULL }
244#endif
245
246#endif
247
248// Helper that gets the current thread exception, for use in exception handlers
249NUITKA_MAY_BE_UNUSED inline static struct Nuitka_ExceptionStackItem GET_CURRENT_EXCEPTION(PyThreadState *tstate) {
250 struct Nuitka_ExceptionStackItem result;
251#if PYTHON_VERSION < 0x3b0
252 result.exception_type = EXC_TYPE(tstate);
253 Py_XINCREF(result.exception_type);
254#endif
255 result.exception_value = EXC_VALUE(tstate);
256 Py_XINCREF(result.exception_value);
257#if PYTHON_VERSION < 0x3b0
258 result.exception_tb = (PyTracebackObject *)EXC_TRACEBACK(tstate);
259 Py_XINCREF(result.exception_tb);
260#endif
261
262 return result;
263};
264
265#if PYTHON_VERSION < 0x300 && !defined(_NUITKA_EXPERIMENTAL_DISABLE_SYS_EXC_VARS)
266#define _NUITKA_MAINTAIN_SYS_EXC_VARS 1
267#endif
268
269// Helper that sets the current thread exception, releasing the current one, for
270// use in this file only.
271NUITKA_MAY_BE_UNUSED inline static void SET_CURRENT_EXCEPTION(PyThreadState *tstate,
272 struct Nuitka_ExceptionStackItem *exc_state) {
273#if PYTHON_VERSION < 0x3b0
274 CHECK_OBJECT_X(exc_state->exception_type);
275#endif
276 CHECK_OBJECT_X(exc_state->exception_value);
277#if PYTHON_VERSION < 0x3b0
278 CHECK_OBJECT_X(exc_state->exception_tb);
279#endif
280
281#if PYTHON_VERSION < 0x3b0
282 PyObject *old_type = EXC_TYPE(tstate);
283#endif
284 PyObject *old_value = EXC_VALUE(tstate);
285#if PYTHON_VERSION < 0x3b0
286 PyTracebackObject *old_tb = EXC_TRACEBACK(tstate);
287#endif
288
289#if PYTHON_VERSION < 0x3b0
290 CHECK_OBJECT_X(old_type);
291#endif
292 CHECK_OBJECT_X(old_value);
293#if PYTHON_VERSION < 0x3b0
294 CHECK_OBJECT_X(old_tb);
295#endif
296
297#if PYTHON_VERSION < 0x3b0
298 EXC_TYPE(tstate) = exc_state->exception_type;
299#endif
300 EXC_VALUE(tstate) = exc_state->exception_value;
301#if PYTHON_VERSION < 0x3b0
302 SET_EXC_TRACEBACK(tstate, exc_state->exception_tb);
303#endif
304
305#if _DEBUG_EXCEPTIONS
306 PRINT_STRING("SET_CURRENT_EXCEPTION:\n");
307 PRINT_PUBLISHED_EXCEPTION();
308#endif
309
310#if PYTHON_VERSION < 0x3b0
311 Py_XDECREF(old_type);
312#endif
313 Py_XDECREF(old_value);
314#if PYTHON_VERSION < 0x3b0
315 Py_XDECREF(old_tb);
316#endif
317
318#if _NUITKA_MAINTAIN_SYS_EXC_VARS
319 // Set sys attributes in the fastest possible way, spell-checker: ignore sysdict
320 PyObject *sys_dict = tstate->interp->sysdict;
321 CHECK_OBJECT(sys_dict);
322
323 PyDict_SetItem(sys_dict, const_str_plain_exc_type, exc_state->exception_type ? exc_state->exception_type : Py_None);
324 PyDict_SetItem(sys_dict, const_str_plain_exc_value,
325 exc_state->exception_value ? exc_state->exception_value : Py_None);
326 PyDict_SetItem(sys_dict, const_str_plain_exc_traceback,
327 exc_state->exception_tb ? (PyObject *)exc_state->exception_tb : Py_None);
328
329 if (exc_state->exception_type) {
330 assert(Py_REFCNT(exc_state->exception_type) >= 2);
331 }
332 if (exc_state->exception_value) {
333 assert(Py_REFCNT(exc_state->exception_value) >= 2);
334 }
335 if (exc_state->exception_tb) {
336 assert(Py_REFCNT(exc_state->exception_tb) >= 2);
337 }
338#endif
339}
340
341// Normalize an exception, may release old values and replace them, expects
342// references passed and returns them.
343NUITKA_MAY_BE_UNUSED static inline void NORMALIZE_EXCEPTION(PyThreadState *tstate, PyObject **exception_type,
344 PyObject **exception_value,
345 PyTracebackObject **exception_tb);
346
347extern PyObject *NORMALIZE_EXCEPTION_VALUE_FOR_RAISE(PyThreadState *tstate, PyObject *exception_type);
348
349// Helper that sets the current thread exception, and has no reference passed.
350// Similar to "PyErr_SetNone".
351NUITKA_MAY_BE_UNUSED inline static void SET_CURRENT_EXCEPTION_TYPE0(PyThreadState *tstate, PyObject *exception_type) {
352 CHECK_OBJECT(exception_type);
353
354#if PYTHON_VERSION < 0x3c0
355 PyObject *old_exception_type = tstate->curexc_type;
356 PyObject *old_exception_value = tstate->curexc_value;
357 PyObject *old_exception_traceback = tstate->curexc_traceback;
358
359 tstate->curexc_type = exception_type;
360 Py_INCREF(exception_type);
361 tstate->curexc_value = NULL;
362 tstate->curexc_traceback = NULL;
363
364#if _DEBUG_EXCEPTIONS
365 PRINT_STRING("SET_CURRENT_EXCEPTION_TYPE0:\n");
366 PRINT_CURRENT_EXCEPTION();
367#endif
368
369 Py_XDECREF(old_exception_type);
370 Py_XDECREF(old_exception_value);
371 Py_XDECREF(old_exception_traceback);
372#else
373 PyObject *old_exception = tstate->current_exception;
374 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(old_exception);
375
376 // TODO: Make the call, exception creation on the outside somehow.
377 PyObject *exception_value = NORMALIZE_EXCEPTION_VALUE_FOR_RAISE(tstate, exception_type);
378 ASSERT_NORMALIZED_EXCEPTION_VALUE(exception_value);
379 tstate->current_exception = exception_value;
380
381#if _DEBUG_EXCEPTIONS
382 PRINT_STRING("SET_CURRENT_EXCEPTION_TYPE0:\n");
383 PRINT_CURRENT_EXCEPTION();
384#endif
385
386 Py_XDECREF(old_exception);
387#endif
388}
389
390// Same as "PyErr_SetObject" CPython API, use this instead.
391NUITKA_MAY_BE_UNUSED inline static void
392SET_CURRENT_EXCEPTION_TYPE0_VALUE0(PyThreadState *tstate, PyObject *exception_type, PyObject *exception_value) {
393 CHECK_OBJECT(exception_type);
394 CHECK_OBJECT(exception_value);
395
396#if PYTHON_VERSION < 0x3c0
397 PyObject *old_exception_type = tstate->curexc_type;
398 PyObject *old_exception_value = tstate->curexc_value;
399 PyObject *old_exception_traceback = tstate->curexc_traceback;
400
401 tstate->curexc_type = exception_type;
402 Py_INCREF(exception_type);
403 tstate->curexc_value = exception_value;
404 Py_INCREF(exception_value);
405 tstate->curexc_traceback = NULL;
406
407#if _DEBUG_EXCEPTIONS
408 PRINT_STRING("SET_CURRENT_EXCEPTION_TYPE0_VALUE0:\n");
409 PRINT_CURRENT_EXCEPTION();
410#endif
411
412 Py_XDECREF(old_exception_type);
413 Py_XDECREF(old_exception_value);
414 Py_XDECREF(old_exception_traceback);
415#else
416 PyObject *old_exception_value = tstate->current_exception;
417 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(old_exception_value);
418
419 // TODO: Make the call on the outside.
420 NORMALIZE_EXCEPTION(tstate, &exception_type, &exception_value, NULL);
421 ASSERT_NORMALIZED_EXCEPTION_VALUE(exception_value);
422 tstate->current_exception = exception_value;
423 Py_INCREF(exception_value);
424
425#if _DEBUG_EXCEPTIONS
426 PRINT_STRING("SET_CURRENT_EXCEPTION_TYPE_0_VALUE0:\n");
427 PRINT_CURRENT_EXCEPTION();
428#endif
429
430 Py_XDECREF(old_exception_value);
431#endif
432}
433
434// TODO: For Python3.12 it would be nice to know it's normalized already, so we
435// can avoid the call to "NORMALIZE_EXCEPTION".
436NUITKA_MAY_BE_UNUSED inline static void
437SET_CURRENT_EXCEPTION_TYPE0_VALUE1(PyThreadState *tstate, PyObject *exception_type, PyObject *exception_value) {
438 CHECK_OBJECT(exception_type);
439 CHECK_OBJECT(exception_value);
440
441#if PYTHON_VERSION < 0x3c0
442 PyObject *old_exception_type = tstate->curexc_type;
443 PyObject *old_exception_value = tstate->curexc_value;
444 PyObject *old_exception_traceback = tstate->curexc_traceback;
445
446 tstate->curexc_type = exception_type;
447 Py_INCREF(exception_type);
448 tstate->curexc_value = exception_value;
449 tstate->curexc_traceback = NULL;
450
451#if _DEBUG_EXCEPTIONS
452 PRINT_STRING("SET_CURRENT_EXCEPTION_TYPE0_VALUE1:\n");
453 PRINT_CURRENT_EXCEPTION();
454#endif
455
456 Py_XDECREF(old_exception_type);
457 Py_XDECREF(old_exception_value);
458 Py_XDECREF(old_exception_traceback);
459#else
460 PyObject *old_exception_value = tstate->current_exception;
461 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(old_exception_value);
462
463 // TODO: Make the call, exception creation on the outside somehow.
464 NORMALIZE_EXCEPTION(tstate, &exception_type, &exception_value, NULL);
465 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(exception_value);
466 tstate->current_exception = exception_value;
467
468#if _DEBUG_EXCEPTIONS
469 PRINT_STRING("SET_CURRENT_EXCEPTION_TYPE0_VALUE1:\n");
470 PRINT_CURRENT_EXCEPTION();
471#endif
472
473 Py_XDECREF(old_exception_value);
474#endif
475}
476
477// Helper that sets the current thread exception, and has no reference passed.
478// Same as CPython API PyErr_SetString
479
480NUITKA_MAY_BE_UNUSED inline static void SET_CURRENT_EXCEPTION_TYPE0_STR(PyThreadState *tstate, PyObject *exception_type,
481 char const *value) {
482 PyObject *exception_value = Nuitka_String_FromString(value);
483
484 SET_CURRENT_EXCEPTION_TYPE0_VALUE1(tstate, exception_type, exception_value);
485}
486
487// Helper that sets the current thread exception with format of one or two arg, and has no reference passed.
488extern void SET_CURRENT_EXCEPTION_TYPE0_FORMAT1(PyObject *exception_type, char const *format, char const *value);
489extern void SET_CURRENT_EXCEPTION_TYPE0_FORMAT2(PyObject *exception_type, char const *format, char const *value1,
490 char const *value2);
491extern void SET_CURRENT_EXCEPTION_TYPE0_FORMAT3(PyObject *exception_type, char const *format, char const *value1,
492 char const *value2, char const *value3);
493
494extern void SET_CURRENT_EXCEPTION_TYPE_COMPLAINT(char const *format, PyObject *mistyped);
495extern void SET_CURRENT_EXCEPTION_TYPE_COMPLAINT_NICE(char const *format, PyObject *mistyped);
496
497#if PYTHON_VERSION < 0x300
498
499// Preserve the current exception as the frame to restore.
500NUITKA_MAY_BE_UNUSED static inline void PRESERVE_FRAME_EXCEPTION(PyThreadState *tstate,
501 struct Nuitka_FrameObject *frame_object) {
502 PyFrameObject *frame = (PyFrameObject *)frame_object;
503
504 // Setting exception for frame if not already done.
505 if (frame->f_exc_type == NULL) {
506 if (tstate->exc_type != NULL && tstate->exc_type != Py_None) {
507#if _DEBUG_EXCEPTIONS
508 PRINT_STRING("PRESERVE_FRAME_EXCEPTION: preserve thread exception\n");
509#endif
510 frame->f_exc_type = tstate->exc_type;
511 Py_INCREF(frame->f_exc_type);
512 frame->f_exc_value = tstate->exc_value;
513 Py_XINCREF(frame->f_exc_value);
514 frame->f_exc_traceback = tstate->exc_traceback;
515 Py_XINCREF(frame->f_exc_traceback);
516 } else {
517#if _DEBUG_EXCEPTIONS
518 PRINT_STRING("PRESERVE_FRAME_EXCEPTION: no exception to preserve\n");
519#endif
520 frame->f_exc_type = Py_None;
521 Py_INCREF_IMMORTAL(frame->f_exc_type);
522 frame->f_exc_value = NULL;
523 frame->f_exc_traceback = NULL;
524 }
525 }
526#if _DEBUG_EXCEPTIONS
527 else {
528 PRINT_STRING("PRESERVE_FRAME_EXCEPTION: already preserving\n");
529 }
530
531 PRINT_ITEM((PyObject *)frame_object);
532 PRINT_NEW_LINE();
533 PRINT_EXCEPTION(frame->f_exc_type, frame->f_exc_value, (PyTracebackObject *)frame->f_exc_traceback);
534#endif
535}
536
537// Restore a previously preserved exception to the frame.
538NUITKA_MAY_BE_UNUSED static inline void RESTORE_FRAME_EXCEPTION(PyThreadState *tstate,
539 struct Nuitka_FrameObject *frame_object) {
540 PyFrameObject *frame = (PyFrameObject *)frame_object;
541
542 if (frame->f_exc_type) {
543#if _DEBUG_EXCEPTIONS
544 PRINT_STRING("RESTORE_FRAME_EXCEPTION: restoring preserved\n");
545 PRINT_ITEM((PyObject *)frame_object);
546 PRINT_NEW_LINE();
547#endif
548
549 struct Nuitka_ExceptionStackItem exc_state;
550
551 exc_state.exception_type = frame->f_exc_type;
552 exc_state.exception_value = frame->f_exc_value;
553 exc_state.exception_tb = (PyTracebackObject *)frame->f_exc_traceback;
554
555 SET_CURRENT_EXCEPTION(tstate, &exc_state);
556
557 frame->f_exc_type = NULL;
558 frame->f_exc_value = NULL;
559 frame->f_exc_traceback = NULL;
560 }
561#if _DEBUG_EXCEPTIONS
562 else {
563 PRINT_STRING("RESTORE_FRAME_EXCEPTION: nothing to restore\n");
564 PRINT_ITEM((PyObject *)frame_object);
565 PRINT_NEW_LINE();
566 }
567#endif
568}
569
570#endif
571
572// Similar to "PyException_SetTraceback", only done for Python3.
573#if PYTHON_VERSION < 0x300
574#define ATTACH_TRACEBACK_TO_EXCEPTION_VALUE(exception_value, exception_tb) ;
575#else
576NUITKA_MAY_BE_UNUSED static inline void ATTACH_TRACEBACK_TO_EXCEPTION_VALUE(PyObject *exception_value,
577 PyTracebackObject *exception_tb) {
578 CHECK_OBJECT(exception_value);
579 CHECK_OBJECT_X(exception_tb);
580
581 if (exception_tb == (PyTracebackObject *)Py_None) {
582 exception_tb = NULL;
583 }
584
585 assert(PyExceptionInstance_Check(exception_value));
586 assert(exception_tb == NULL || PyTraceBack_Check(exception_tb));
587
588 PyBaseExceptionObject *e = (PyBaseExceptionObject *)exception_value;
589
590 PyObject *old = e->traceback;
591 Py_XINCREF(exception_tb);
592 e->traceback = (PyObject *)exception_tb;
593 Py_XDECREF(old);
594}
595
596// Much like "PyException_GetTraceback", but does not give a reference.
597NUITKA_MAY_BE_UNUSED static inline PyTracebackObject *GET_EXCEPTION_TRACEBACK(PyObject *exception_value) {
598 CHECK_OBJECT(exception_value);
599 assert(PyExceptionInstance_Check(exception_value));
600
601 PyBaseExceptionObject *exc_object = (PyBaseExceptionObject *)(exception_value);
602 return (PyTracebackObject *)exc_object->traceback;
603}
604
605#endif
606
607NUITKA_MAY_BE_UNUSED static bool EXCEPTION_MATCH_BOOL_SINGLE(PyThreadState *tstate, PyObject *exception_value,
608 PyObject *exception_checked);
609
610NUITKA_MAY_BE_UNUSED static bool _CHECK_AND_CLEAR_EXCEPTION_OCCURRED(PyThreadState *tstate, PyObject *exception_type) {
611#if PYTHON_VERSION < 0x3c0
612 PyObject *exception_current = tstate->curexc_type;
613#else
614 PyObject *exception_current = tstate->current_exception;
615 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(exception_current);
616#endif
617 if (exception_current == NULL) {
618 return true;
619 } else if (EXCEPTION_MATCH_BOOL_SINGLE(tstate, exception_current, exception_type)) {
620 CHECK_OBJECT(exception_current);
621
622#if PYTHON_VERSION < 0x3c0
623 // Clear the exception first, we believe we know it doesn't have side effects.
624 Py_DECREF(exception_current);
625 tstate->curexc_type = NULL;
626
627 PyObject *old_value = tstate->curexc_value;
628 PyObject *old_tb = tstate->curexc_traceback;
629
630 tstate->curexc_value = NULL;
631 tstate->curexc_traceback = NULL;
632
633 Py_XDECREF(old_value);
634 Py_XDECREF(old_tb);
635#else
636 tstate->current_exception = NULL;
637 Py_DECREF(exception_current);
638#endif
639
640 return true;
641 } else {
642 return false;
643 }
644}
645
646/* Special helper that checks for StopIteration and if so clears it, only
647 indicating if it was set in the return value.
648
649 Equivalent to if(PyErr_ExceptionMatches(PyExc_StopIteration) PyErr_Clear();
650
651 If error is raised by built-in function next() and an iterator's __next__()
652 method to signal that there are no further items produced by the iterator then
653 it resets the TSTATE to NULL and returns True else return False
654
655*/
656NUITKA_MAY_BE_UNUSED static bool CHECK_AND_CLEAR_STOP_ITERATION_OCCURRED(PyThreadState *tstate) {
657 return _CHECK_AND_CLEAR_EXCEPTION_OCCURRED(tstate, PyExc_StopIteration);
658}
659
660/* Special helper that checks for KeyError and if so clears it, only
661 indicating if it was set in the return value.
662
663 Equivalent to if(PyErr_ExceptionMatches(PyExc_KeyError) PyErr_Clear();
664
665*/
666NUITKA_MAY_BE_UNUSED static bool CHECK_AND_CLEAR_KEY_ERROR_OCCURRED(PyThreadState *tstate) {
667 return _CHECK_AND_CLEAR_EXCEPTION_OCCURRED(tstate, PyExc_KeyError);
668}
669
670NUITKA_MAY_BE_UNUSED static bool CHECK_AND_CLEAR_ATTRIBUTE_ERROR_OCCURRED(PyThreadState *tstate) {
671 return _CHECK_AND_CLEAR_EXCEPTION_OCCURRED(tstate, PyExc_AttributeError);
672}
673
674#if PYTHON_VERSION >= 0x3c0
675NUITKA_MAY_BE_UNUSED static PyObject *MAKE_TUPLE1(PyThreadState *tstate, PyObject *element1);
676
677NUITKA_MAY_BE_UNUSED static PyObject *MAKE_EXCEPTION_FROM_TYPE_ARG0(PyThreadState *tstate, PyObject *type,
678 PyObject *arg) {
679 PyBaseExceptionObject *self;
680
681 PyTypeObject *type_object = (PyTypeObject *)type;
682
683 self = (PyBaseExceptionObject *)(type_object->tp_alloc(type_object, 0));
684
685 self->dict = NULL;
686 self->notes = NULL;
687 self->traceback = self->cause = self->context = NULL;
688 self->suppress_context = 0;
689
690 assert(arg != NULL);
691
692 if (!PyTuple_Check(arg)) {
693 self->args = MAKE_TUPLE1(tstate, arg);
694 } else {
695 self->args = Py_NewRef(arg);
696 }
697
698 return (PyObject *)self;
699}
700#else
701
702extern PyObject *CALL_FUNCTION_WITH_SINGLE_ARG(PyThreadState *tstate, PyObject *called, PyObject *arg);
703
704NUITKA_MAY_BE_UNUSED static PyObject *MAKE_EXCEPTION_FROM_TYPE_ARG0(PyThreadState *tstate, PyObject *type,
705 PyObject *arg) {
706 return CALL_FUNCTION_WITH_SINGLE_ARG(tstate, type, arg);
707}
708
709#endif
710
711#if PYTHON_VERSION < 0x3c0
713 PyObject *exception_type;
714 PyObject *exception_value;
715 PyTracebackObject *exception_tb;
716};
717
718static const struct Nuitka_ExceptionPreservationItem Empty_Nuitka_ExceptionPreservationItem = {0};
719
720// Fetch the current exception into state, transfers reference coming from tstate ownership. Old values are overwritten.
721NUITKA_MAY_BE_UNUSED static void FETCH_ERROR_OCCURRED_STATE(PyThreadState *tstate,
722 struct Nuitka_ExceptionPreservationItem *exception_state) {
723 FETCH_ERROR_OCCURRED(tstate, &exception_state->exception_type, &exception_state->exception_value,
724 &exception_state->exception_tb);
725}
726
727// Restore the current exception from state, transfers reference from state to tstate ownership.
728NUITKA_MAY_BE_UNUSED static void
729RESTORE_ERROR_OCCURRED_STATE(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state) {
730 RESTORE_ERROR_OCCURRED(tstate, exception_state->exception_type, exception_state->exception_value,
731 exception_state->exception_tb);
732}
733
734NUITKA_MAY_BE_UNUSED static void
735FETCH_ERROR_OCCURRED_STATE_UNTRACED(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state) {
736 FETCH_ERROR_OCCURRED_UNTRACED(tstate, &exception_state->exception_type, &exception_state->exception_value,
737 &exception_state->exception_tb);
738}
739
740NUITKA_MAY_BE_UNUSED static void
741RESTORE_ERROR_OCCURRED_STATE_UNTRACED(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state) {
742 RESTORE_ERROR_OCCURRED_UNTRACED(tstate, exception_state->exception_type, exception_state->exception_value,
743 exception_state->exception_tb);
744}
745
746NUITKA_MAY_BE_UNUSED static void
747ASSERT_SAME_EXCEPTION_STATE(struct Nuitka_ExceptionPreservationItem const *exception_state1,
748 struct Nuitka_ExceptionPreservationItem const *exception_state2) {
749 assert(exception_state1->exception_type == exception_state2->exception_type);
750 assert(exception_state1->exception_value == exception_state2->exception_value);
751 assert(exception_state1->exception_tb == exception_state2->exception_tb);
752}
753
754NUITKA_MAY_BE_UNUSED static void
755ASSERT_EMPTY_EXCEPTION_STATE(struct Nuitka_ExceptionPreservationItem const *exception_state) {
756 assert(exception_state->exception_type == NULL);
757 assert(exception_state->exception_value == NULL);
758 assert(exception_state->exception_tb == NULL);
759}
760
761NUITKA_MAY_BE_UNUSED static void INIT_ERROR_OCCURRED_STATE(struct Nuitka_ExceptionPreservationItem *exception_state) {
762 exception_state->exception_type = NULL;
763 exception_state->exception_value = NULL;
764 exception_state->exception_tb = NULL;
765}
766
767NUITKA_MAY_BE_UNUSED static void
768RELEASE_ERROR_OCCURRED_STATE(struct Nuitka_ExceptionPreservationItem *exception_state) {
769 CHECK_OBJECT(exception_state->exception_type);
770 CHECK_OBJECT_X(exception_state->exception_value);
771 CHECK_OBJECT_X(exception_state->exception_tb);
772 Py_DECREF(exception_state->exception_type);
773 Py_XDECREF(exception_state->exception_value);
774 Py_XDECREF(exception_state->exception_tb);
775}
776
777NUITKA_MAY_BE_UNUSED static void
778RELEASE_ERROR_OCCURRED_STATE_X(struct Nuitka_ExceptionPreservationItem *exception_state) {
779 CHECK_OBJECT_X(exception_state->exception_type);
780 CHECK_OBJECT_X(exception_state->exception_value);
781 CHECK_OBJECT_X(exception_state->exception_tb);
782
783 Py_XDECREF(exception_state->exception_type);
784 Py_XDECREF(exception_state->exception_value);
785 Py_XDECREF(exception_state->exception_tb);
786}
787
788NUITKA_MAY_BE_UNUSED static void SET_EXCEPTION_PRESERVATION_STATE_FROM_ARGS(
789 PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state, PyObject *exception_type,
790 PyObject *exception_value, PyTracebackObject *exception_tb) {
791 Py_INCREF(exception_type);
792 Py_XINCREF(exception_value);
793 Py_XINCREF(exception_tb);
794
795 exception_state->exception_type = exception_type;
796 exception_state->exception_value = exception_value;
797 exception_state->exception_tb = exception_tb;
798}
799
800NUITKA_MAY_BE_UNUSED static void
801ASSIGN_ARGS_FROM_EXCEPTION_PRESERVATION_STATE(struct Nuitka_ExceptionPreservationItem *exception_state,
802 PyObject **exception_type, PyObject **exception_value,
803 PyTracebackObject **exception_tb) {
804
805 *exception_type = exception_state->exception_type;
806 Py_INCREF(*exception_type);
807 *exception_value = exception_state->exception_value;
808 Py_XINCREF(*exception_value);
809 *exception_tb = exception_state->exception_tb;
810 Py_XINCREF(*exception_tb);
811}
812
813NUITKA_MAY_BE_UNUSED static PyTracebackObject *
814GET_EXCEPTION_STATE_TRACEBACK(struct Nuitka_ExceptionPreservationItem *exception_state) {
815 return exception_state->exception_tb;
816}
817
818// Transfer ownership of the traceback to the exception state.
819NUITKA_MAY_BE_UNUSED static void SET_EXCEPTION_STATE_TRACEBACK(struct Nuitka_ExceptionPreservationItem *exception_state,
820 PyTracebackObject *exception_tb) {
821 CHECK_OBJECT_X(exception_state->exception_tb);
822 CHECK_OBJECT_X(exception_tb);
823
824 Py_XDECREF(exception_state->exception_tb);
825 exception_state->exception_tb = exception_tb;
826}
827
828NUITKA_MAY_BE_UNUSED static bool HAS_EXCEPTION_STATE(struct Nuitka_ExceptionPreservationItem const *exception_state) {
829 return exception_state->exception_type != NULL;
830}
831
832NUITKA_MAY_BE_UNUSED static bool
833EXCEPTION_STATE_MATCH_BOOL_SINGLE(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state,
834 PyObject *exception_checked) {
835 return EXCEPTION_MATCH_BOOL_SINGLE(tstate, exception_state->exception_type, exception_checked);
836}
837
838NUITKA_MAY_BE_UNUSED inline static void
839CHECK_EXCEPTION_STATE(struct Nuitka_ExceptionPreservationItem const *exception_state) {
840 CHECK_OBJECT(exception_state->exception_type);
841 CHECK_OBJECT_X(exception_state->exception_value);
842 CHECK_OBJECT_X(exception_state->exception_tb);
843}
844
845NUITKA_MAY_BE_UNUSED inline static void
846CHECK_EXCEPTION_STATE_X(struct Nuitka_ExceptionPreservationItem const *exception_state) {
847 CHECK_OBJECT_X(exception_state->exception_type);
848 CHECK_OBJECT_X(exception_state->exception_value);
849 CHECK_OBJECT_X(exception_state->exception_tb);
850}
851
852#else
854 PyObject *exception_value;
855};
856
857static const struct Nuitka_ExceptionPreservationItem Empty_Nuitka_ExceptionPreservationItem = {0};
858
859// Fetch the current exception into state, transfers reference coming from tstate ownership. Old value is overwritten.
860NUITKA_MAY_BE_UNUSED static void FETCH_ERROR_OCCURRED_STATE(PyThreadState *tstate,
861 struct Nuitka_ExceptionPreservationItem *exception_state) {
862 exception_state->exception_value = tstate->current_exception;
863 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(exception_state->exception_value);
864
865#if _DEBUG_EXCEPTIONS
866 PRINT_STRING("FETCH_ERROR_OCCURRED_STATE:\n");
867 PRINT_CURRENT_EXCEPTION();
868#endif
869
870 tstate->current_exception = NULL;
871}
872
873NUITKA_MAY_BE_UNUSED static void
874FETCH_ERROR_OCCURRED_STATE_UNTRACED(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state) {
875 exception_state->exception_value = tstate->current_exception;
876 tstate->current_exception = NULL;
877
878 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(exception_state->exception_value);
879}
880
881NUITKA_MAY_BE_UNUSED static void
882RESTORE_ERROR_OCCURRED_STATE(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state) {
883 PyObject *old_exception_value = tstate->current_exception;
884 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(old_exception_value);
885
886 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(exception_state->exception_value);
887 tstate->current_exception = exception_state->exception_value;
888
889#if _DEBUG_EXCEPTIONS
890 PRINT_STRING("RESTORE_ERROR_OCCURRED_STATE:\n");
891 PRINT_CURRENT_EXCEPTION();
892#endif
893
894 Py_XDECREF(old_exception_value);
895}
896
897NUITKA_MAY_BE_UNUSED static void
898RESTORE_ERROR_OCCURRED_STATE_UNTRACED(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state) {
899 PyObject *old_exception_value = tstate->current_exception;
900 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(old_exception_value);
901
902 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(exception_state->exception_value);
903 tstate->current_exception = exception_state->exception_value;
904
905 Py_XDECREF(old_exception_value);
906}
907
908NUITKA_MAY_BE_UNUSED static void
909ASSERT_SAME_EXCEPTION_STATE(struct Nuitka_ExceptionPreservationItem *exception_state1,
910 struct Nuitka_ExceptionPreservationItem *exception_state2) {
911 assert(exception_state1->exception_value == exception_state2->exception_value);
912}
913
914NUITKA_MAY_BE_UNUSED static void
915ASSERT_EMPTY_EXCEPTION_STATE(struct Nuitka_ExceptionPreservationItem const *exception_state) {
916 assert(exception_state->exception_value == NULL);
917}
918
919NUITKA_MAY_BE_UNUSED static void INIT_ERROR_OCCURRED_STATE(struct Nuitka_ExceptionPreservationItem *exception_state) {
920 exception_state->exception_value = NULL;
921}
922
923NUITKA_MAY_BE_UNUSED static void
924RELEASE_ERROR_OCCURRED_STATE(struct Nuitka_ExceptionPreservationItem *exception_state) {
925 CHECK_OBJECT(exception_state->exception_value);
926 Py_DECREF(exception_state->exception_value);
927}
928
929NUITKA_MAY_BE_UNUSED static void
930RELEASE_ERROR_OCCURRED_STATE_X(struct Nuitka_ExceptionPreservationItem *exception_state) {
931 CHECK_OBJECT_X(exception_state->exception_value);
932 Py_XDECREF(exception_state->exception_value);
933}
934
935NUITKA_MAY_BE_UNUSED static void SET_EXCEPTION_PRESERVATION_STATE_FROM_ARGS(
936 PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state, PyObject *exception_type,
937 PyObject *exception_value, PyTracebackObject *exception_tb) {
938
939 Py_XINCREF(exception_type);
940 Py_XINCREF(exception_value);
941 Py_XINCREF(exception_tb);
942
943 NORMALIZE_EXCEPTION(tstate, &exception_type, &exception_value, &exception_tb);
944 ASSERT_NORMALIZED_EXCEPTION_VALUE(exception_value);
945
946 exception_state->exception_value = exception_value;
947 Py_INCREF(exception_value);
948
949 ATTACH_TRACEBACK_TO_EXCEPTION_VALUE(exception_value, exception_tb);
950
951 Py_XDECREF(exception_type);
952 Py_XDECREF(exception_value);
953 Py_XDECREF(exception_tb);
954}
955
956NUITKA_MAY_BE_UNUSED static void
957ASSIGN_ARGS_FROM_EXCEPTION_PRESERVATION_STATE(struct Nuitka_ExceptionPreservationItem *exception_state,
958 PyObject **exception_type, PyObject **exception_value,
959 PyTracebackObject **exception_tb) {
960
961 *exception_value = exception_state->exception_value;
962
963 if (*exception_value) {
964 Py_INCREF(*exception_value);
965
966 *exception_type = (PyObject *)PyExceptionInstance_Class(*exception_value);
967 Py_INCREF(*exception_type);
968 *exception_tb = GET_EXCEPTION_TRACEBACK(*exception_value);
969 Py_XINCREF(*exception_tb);
970 } else {
971 *exception_type = NULL;
972 *exception_tb = NULL;
973 }
974}
975
976NUITKA_MAY_BE_UNUSED static PyTracebackObject *
977GET_EXCEPTION_STATE_TRACEBACK(struct Nuitka_ExceptionPreservationItem *exception_state) {
978 return GET_EXCEPTION_TRACEBACK(exception_state->exception_value);
979}
980
981// Transfer ownership of the traceback to the exception state.
982NUITKA_MAY_BE_UNUSED static void SET_EXCEPTION_STATE_TRACEBACK(struct Nuitka_ExceptionPreservationItem *exception_state,
983 PyTracebackObject *exception_tb) {
984 ATTACH_TRACEBACK_TO_EXCEPTION_VALUE(exception_state->exception_value, exception_tb);
985 Py_XDECREF(exception_tb);
986 CHECK_OBJECT_X(exception_tb);
987}
988
989NUITKA_MAY_BE_UNUSED static bool HAS_EXCEPTION_STATE(struct Nuitka_ExceptionPreservationItem const *exception_state) {
990 return exception_state->exception_value != NULL;
991}
992
993NUITKA_MAY_BE_UNUSED static bool
994EXCEPTION_STATE_MATCH_BOOL_SINGLE(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem const *exception_state,
995 PyObject *exception_checked) {
996 return EXCEPTION_MATCH_BOOL_SINGLE(tstate, exception_state->exception_value, exception_checked);
997}
998
999NUITKA_MAY_BE_UNUSED inline static void
1000CHECK_EXCEPTION_STATE(struct Nuitka_ExceptionPreservationItem const *exception_state) {
1001 ASSERT_NORMALIZED_EXCEPTION_VALUE(exception_state->exception_value);
1002}
1003
1004NUITKA_MAY_BE_UNUSED inline static void
1005CHECK_EXCEPTION_STATE_X(struct Nuitka_ExceptionPreservationItem const *exception_state) {
1006 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(exception_state->exception_value);
1007}
1008
1009#endif
1010
1011NUITKA_MAY_BE_UNUSED inline static void SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0(
1012 PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state, PyObject *exception_type) {
1013
1014 SET_EXCEPTION_PRESERVATION_STATE_FROM_ARGS(tstate, exception_state, exception_type, NULL, NULL);
1015}
1016
1017extern PyObject *CALL_FUNCTION_WITH_SINGLE_ARG(PyThreadState *tstate, PyObject *called, PyObject *arg);
1018
1019NUITKA_MAY_BE_UNUSED inline static void
1020SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_VALUE1(PyThreadState *tstate,
1021 struct Nuitka_ExceptionPreservationItem *exception_state,
1022 PyObject *exception_type, PyObject *exception_value) {
1023#if PYTHON_VERSION < 0x3c0
1024 Py_INCREF(exception_type);
1025
1026 exception_state->exception_type = exception_type;
1027 exception_state->exception_value = exception_value;
1028 exception_state->exception_tb = NULL;
1029#else
1030 PyObject *exc = CALL_FUNCTION_WITH_SINGLE_ARG(tstate, exception_type, exception_value);
1031 exception_state->exception_value = exc;
1032 Py_DECREF(exception_value);
1033#endif
1034}
1035
1036NUITKA_MAY_BE_UNUSED inline static void
1037SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_VALUE1_NORMALIZED(PyThreadState *tstate,
1038 struct Nuitka_ExceptionPreservationItem *exception_state,
1039 PyObject *exception_type, PyObject *exception_value) {
1040#if PYTHON_VERSION < 0x3c0
1041 SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_VALUE1(tstate, exception_state, exception_type, exception_value);
1042#else
1043 exception_state->exception_value = exception_value;
1044#endif
1045}
1046
1047NUITKA_MAY_BE_UNUSED inline static void
1048SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_VALUE0(PyThreadState *tstate,
1049 struct Nuitka_ExceptionPreservationItem *exception_state,
1050 PyObject *exception_type, PyObject *exception_value) {
1051 // TODO: Add variants for normalized values only.
1052 SET_EXCEPTION_PRESERVATION_STATE_FROM_ARGS(tstate, exception_state, exception_type, exception_value, NULL);
1053}
1054
1055NUITKA_MAY_BE_UNUSED inline static void
1056SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_STR(PyThreadState *tstate,
1057 struct Nuitka_ExceptionPreservationItem *exception_state,
1058 PyObject *exception_type, char const *value) {
1059 PyObject *exception_value = Nuitka_String_FromString(value);
1060
1061 SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_VALUE1(tstate, exception_state, exception_type, exception_value);
1062}
1063
1064#define SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_FORMAT1(tstate, exception_state, exception_type, message, arg1) \
1065 { \
1066 PyObject *exception_value = Nuitka_String_FromFormat(message, arg1); \
1067 CHECK_OBJECT(exception_value); \
1068 SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_VALUE1(tstate, exception_state, exception_type, exception_value); \
1069 }
1070
1071#define SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_FORMAT2(tstate, exception_state, exception_type, message, arg1, \
1072 arg2) \
1073 { \
1074 PyObject *exception_value = Nuitka_String_FromFormat(message, arg1, arg2); \
1075 CHECK_OBJECT(exception_value); \
1076 SET_EXCEPTION_PRESERVATION_STATE_FROM_TYPE0_VALUE1(tstate, exception_state, exception_type, exception_value); \
1077 }
1078
1079NUITKA_MAY_BE_UNUSED static bool EXCEPTION_MATCH_GENERATOR(PyThreadState *tstate, PyObject *exception_value) {
1080 CHECK_OBJECT(exception_value);
1081
1082 // TODO: For Python3.12 this must be done differently to be a lot better.
1083
1084 // We need to check the class.
1085 if (PyExceptionInstance_Check(exception_value)) {
1086 exception_value = PyExceptionInstance_Class(exception_value);
1087 }
1088
1089 // Lets be optimistic. If it matches, we would be wasting our time.
1090 if (exception_value == PyExc_GeneratorExit || exception_value == PyExc_StopIteration) {
1091 return true;
1092 }
1093
1094 if (PyExceptionClass_Check(exception_value)) {
1095 // Save the current exception, if any, we must preserve it.
1096 struct Nuitka_ExceptionPreservationItem saved_exception_state;
1097 FETCH_ERROR_OCCURRED_STATE(tstate, &saved_exception_state);
1098
1099 int res = PyObject_IsSubclass(exception_value, PyExc_GeneratorExit);
1100
1101 // This function must not fail, so print the error here */
1102 if (unlikely(res == -1)) {
1103 PyErr_WriteUnraisable(exception_value);
1104 }
1105
1106 if (res == 1) {
1107 return true;
1108 }
1109
1110 res = PyObject_IsSubclass(exception_value, PyExc_StopIteration);
1111
1112 // This function must not fail, so print the error here */
1113 if (unlikely(res == -1)) {
1114 PyErr_WriteUnraisable(exception_value);
1115 }
1116
1117 RESTORE_ERROR_OCCURRED_STATE(tstate, &saved_exception_state);
1118
1119 return res == 1;
1120 }
1121
1122 return false;
1123}
1124
1125NUITKA_MAY_BE_UNUSED static bool
1126EXCEPTION_STATE_MATCH_GENERATOR(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state) {
1127#if PYTHON_VERSION < 0x3c0
1128 return EXCEPTION_MATCH_GENERATOR(tstate, exception_state->exception_type);
1129#else
1130 return EXCEPTION_MATCH_GENERATOR(tstate, exception_state->exception_value);
1131#endif
1132}
1133
1134NUITKA_MAY_BE_UNUSED static bool EXCEPTION_MATCH_BOOL_SINGLE(PyThreadState *tstate, PyObject *exception_value,
1135 PyObject *exception_checked) {
1136 CHECK_OBJECT(exception_value);
1137 CHECK_OBJECT(exception_checked);
1138
1139 // We need to check the class.
1140 if (PyExceptionInstance_Check(exception_value)) {
1141 exception_value = PyExceptionInstance_Class(exception_value);
1142 }
1143
1144 // Lets be optimistic. If it matches, we would be wasting our time.
1145 if (exception_value == exception_checked) {
1146 return true;
1147 }
1148
1149 if (PyExceptionClass_Check(exception_value)) {
1150#if PYTHON_VERSION < 0x300
1151 // Save the current exception, if any, we must preserve it.
1152 struct Nuitka_ExceptionPreservationItem saved_exception_state;
1153 FETCH_ERROR_OCCURRED_STATE(tstate, &saved_exception_state);
1154
1155 // Python3.10 at least uses PyType_IsSubtype and needs no
1156 // fetch restore.
1157 int res = PyObject_IsSubclass(exception_value, exception_checked);
1158
1159 // This function must not fail, so print the error here */
1160 if (unlikely(res == -1)) {
1161 PyErr_WriteUnraisable(exception_value);
1162 }
1163
1164 RESTORE_ERROR_OCCURRED_STATE(tstate, &saved_exception_state);
1165
1166 return res == 1;
1167#else
1168 int res = Nuitka_Type_IsSubtype((PyTypeObject *)exception_value, (PyTypeObject *)exception_checked);
1169 return res == 1;
1170#endif
1171 }
1172
1173 return false;
1174}
1175
1176NUITKA_MAY_BE_UNUSED static inline int _EXCEPTION_MATCH_BOOL(PyThreadState *tstate, PyObject *exception_value,
1177 PyObject *exception_checked) {
1178 CHECK_OBJECT(exception_value);
1179 CHECK_OBJECT(exception_checked);
1180
1181 // Reduce to exception class actually. TODO: Can this not be an instance from called code?!
1182 PyObject *exception_class;
1183 if (PyExceptionInstance_Check(exception_value)) {
1184 exception_class = PyExceptionInstance_Class(exception_value);
1185 } else {
1186 exception_class = exception_value;
1187 }
1188
1189#if PYTHON_VERSION < 0x300
1190 if (PyExceptionClass_Check(exception_class) && PyExceptionClass_Check(exception_checked)) {
1191 // Save the current exception, if any, we must preserve it.
1192 struct Nuitka_ExceptionPreservationItem saved_exception_state;
1193 FETCH_ERROR_OCCURRED_STATE(tstate, &saved_exception_state);
1194
1195 // Avoid recursion limit being exceeded just then
1196 int recursion_limit = Py_GetRecursionLimit();
1197 if (recursion_limit < (1 << 30)) {
1198 Py_SetRecursionLimit(recursion_limit + 5);
1199 }
1200
1201 int res = PyObject_IsSubclass(exception_class, exception_checked);
1202
1203 Py_SetRecursionLimit(recursion_limit);
1204
1205 // This function must not fail, so print the error here */
1206 if (unlikely(res == -1)) {
1207 PyErr_WriteUnraisable(exception_value);
1208 res = 0;
1209 }
1210
1211 RESTORE_ERROR_OCCURRED_STATE(tstate, &saved_exception_state);
1212
1213 return res;
1214 } else {
1215 return exception_class == exception_checked;
1216 }
1217#else
1218 if (PyExceptionClass_Check(exception_class) && PyExceptionClass_Check(exception_checked)) {
1219 return Nuitka_Type_IsSubtype((PyTypeObject *)exception_class, (PyTypeObject *)exception_checked);
1220 } else {
1221 return exception_class == exception_checked;
1222 }
1223#endif
1224}
1225
1226// This is for the actual comparison operation that is being done in the
1227// node tree, no other code should use it. TODO: Then it's probably not
1228// properly located here.
1229NUITKA_MAY_BE_UNUSED static inline int EXCEPTION_MATCH_BOOL(PyThreadState *tstate, PyObject *exception_value,
1230 PyObject *exception_checked) {
1231 CHECK_OBJECT(exception_value);
1232 CHECK_OBJECT(exception_checked);
1233
1234#if PYTHON_VERSION >= 0x300
1235 /* Note: Exact matching tuples seems to needed, despite using GET_ITEM later
1236 on, this probably cannot be overloaded that deep. */
1237 if (PyTuple_Check(exception_checked)) {
1238 Py_ssize_t length = PyTuple_GET_SIZE(exception_checked);
1239
1240 for (Py_ssize_t i = 0; i < length; i += 1) {
1241 PyObject *element = PyTuple_GET_ITEM(exception_checked, i);
1242
1243 if (unlikely(!PyExceptionClass_Check(element))) {
1244 SET_CURRENT_EXCEPTION_TYPE0_STR(
1245 tstate, PyExc_TypeError, "catching classes that do not inherit from BaseException is not allowed");
1246 return -1;
1247 }
1248 }
1249 } else if (unlikely(!PyExceptionClass_Check(exception_checked))) {
1250 SET_CURRENT_EXCEPTION_TYPE0_STR(tstate, PyExc_TypeError,
1251 "catching classes that do not inherit from BaseException is not allowed");
1252 return -1;
1253 }
1254#endif
1255
1256 if (PyTuple_Check(exception_checked)) {
1257 Py_ssize_t length = PyTuple_GET_SIZE(exception_checked);
1258
1259 for (Py_ssize_t i = 0; i < length; i += 1) {
1260 PyObject *element = PyTuple_GET_ITEM(exception_checked, i);
1261
1262 int res = EXCEPTION_MATCH_BOOL(tstate, exception_value, element);
1263
1264 if (res != 0) {
1265 return res;
1266 }
1267 }
1268
1269 return 0;
1270 } else {
1271 return _EXCEPTION_MATCH_BOOL(tstate, exception_value, exception_checked);
1272 }
1273}
1274
1275// Normalize an exception type to a value.
1276
1277extern void Nuitka_Err_NormalizeException(PyThreadState *tstate, PyObject **exc, PyObject **val,
1278 PyTracebackObject **tb);
1279
1280// Normalize an exception, may release old values and replace them, expects
1281// references passed and returns them.
1282NUITKA_MAY_BE_UNUSED static inline void NORMALIZE_EXCEPTION(PyThreadState *tstate, PyObject **exception_type,
1283 PyObject **exception_value,
1284 PyTracebackObject **exception_tb) {
1285#if _DEBUG_EXCEPTIONS
1286 PRINT_STRING("NORMALIZE_EXCEPTION: Enter\n");
1287 PRINT_EXCEPTION(*exception_type, *exception_value, *exception_tb);
1288#endif
1289 CHECK_OBJECT_X(*exception_type);
1290 CHECK_OBJECT_X(*exception_value);
1291 if (exception_tb) {
1292 CHECK_OBJECT_X(*exception_tb);
1293 }
1294
1295 // TODO: Often we already know this to be true.
1296 if (*exception_type != Py_None && *exception_type != NULL) {
1297 Nuitka_Err_NormalizeException(tstate, exception_type, exception_value, exception_tb);
1298 }
1299
1300#if _DEBUG_EXCEPTIONS
1301 PRINT_STRING("NORMALIZE_EXCEPTION: Leave\n");
1302 PRINT_EXCEPTION(*exception_type, *exception_value, exception_tb ? *exception_tb : NULL);
1303#endif
1304}
1305
1306#if PYTHON_VERSION < 0x3c0
1307// Normalize an exception, may release old values and replace them, expects
1308// references passed and returns them.
1309static inline void NORMALIZE_EXCEPTION_STATE(PyThreadState *tstate,
1310 struct Nuitka_ExceptionPreservationItem *exception_state) {
1311 CHECK_EXCEPTION_STATE_X(exception_state);
1312
1313 NORMALIZE_EXCEPTION(tstate, &exception_state->exception_type, &exception_state->exception_value,
1314 &exception_state->exception_tb);
1315}
1316#endif
1317
1318extern PyObject *CALL_FUNCTION_NO_ARGS(PyThreadState *tstate, PyObject *called);
1319
1320// Publish an exception, erasing the values of the variables.
1321NUITKA_MAY_BE_UNUSED static inline void
1322PUBLISH_CURRENT_EXCEPTION(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state) {
1323#if _DEBUG_EXCEPTIONS
1324 PRINT_STRING("PUBLISH_CURRENT_EXCEPTION:\n");
1325 PRINT_EXCEPTION_STATE(exception_state);
1326#endif
1327
1328#if PYTHON_VERSION < 0x3c0
1329 NORMALIZE_EXCEPTION_STATE(tstate, exception_state);
1330 ATTACH_TRACEBACK_TO_EXCEPTION_VALUE(exception_state->exception_value, exception_state->exception_tb);
1331#endif
1332
1333 struct Nuitka_ExceptionStackItem exc_state;
1334
1335#if PYTHON_VERSION < 0x3b0
1336 exc_state.exception_type = exception_state->exception_type;
1337#endif
1338 exc_state.exception_value = exception_state->exception_value;
1339#if PYTHON_VERSION < 0x3b0
1340 exc_state.exception_tb = exception_state->exception_tb;
1341#endif
1342
1343 SET_CURRENT_EXCEPTION(tstate, &exc_state);
1344
1345#if PYTHON_VERSION >= 0x3b0 && PYTHON_VERSION < 0x3c0
1346 // TODO: We shouldn't get these in the first place, we don't transfer the
1347 // type anymore and the exception tb could come in already attached.
1348 Py_DECREF(exception_state->exception_type);
1349 Py_XDECREF(exception_state->exception_tb);
1350#endif
1351
1352 INIT_ERROR_OCCURRED_STATE(exception_state);
1353}
1354
1355NUITKA_MAY_BE_UNUSED static bool
1356_CHECK_AND_CLEAR_EXCEPTION_STATE(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state,
1357 PyObject *exception_type) {
1358#if PYTHON_VERSION < 0x3c0
1359 PyObject *exception_current = exception_state->exception_type;
1360#else
1361 PyObject *exception_current = exception_state->exception_value;
1362 ASSERT_NORMALIZED_EXCEPTION_VALUE_X(exception_current);
1363#endif
1364 if (exception_current == NULL) {
1365 return true;
1366 } else if (EXCEPTION_MATCH_BOOL_SINGLE(tstate, exception_current, exception_type)) {
1367 CHECK_OBJECT(exception_current);
1368
1369 RELEASE_ERROR_OCCURRED_STATE(exception_state);
1370 INIT_ERROR_OCCURRED_STATE(exception_state);
1371
1372 return true;
1373 } else {
1374 return false;
1375 }
1376}
1377
1378// TODO: Get rid of "CHECK_AND_CLEAR_STOP_ITERATION_OCCURRED" and rename this to
1379// its name.
1380NUITKA_MAY_BE_UNUSED static bool
1381CHECK_AND_CLEAR_STOP_ITERATION_STATE(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state) {
1382 return _CHECK_AND_CLEAR_EXCEPTION_STATE(tstate, exception_state, PyExc_StopIteration);
1383}
1384
1385// Format a UnboundLocalError exception for a variable name. TODO: This is more
1386// for "raising.h" it seems.
1387extern void FORMAT_UNBOUND_LOCAL_ERROR(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state,
1388 PyObject *variable_name);
1389
1390extern void FORMAT_UNBOUND_CLOSURE_ERROR(PyThreadState *tstate,
1391 struct Nuitka_ExceptionPreservationItem *exception_state,
1392 PyObject *variable_name);
1393
1394#if PYTHON_VERSION >= 0x300
1395static inline PyBaseExceptionObject *_PyBaseExceptionObject_cast(PyObject *exc) {
1396 assert(PyExceptionInstance_Check(exc));
1397 return (PyBaseExceptionObject *)exc;
1398}
1399
1400// Exception context, replacement for "PyException_GetContext", it however gives no
1401// reference.
1402NUITKA_MAY_BE_UNUSED static inline PyObject *Nuitka_Exception_GetContext(PyObject *self) {
1403 return _PyBaseExceptionObject_cast(self)->context;
1404}
1405
1406// Exception context, replacement for "PyException_SetContext" it however doesn't
1407// consume a reference.
1408NUITKA_MAY_BE_UNUSED static inline void Nuitka_Exception_SetContext(PyObject *self, PyObject *context) {
1409 CHECK_OBJECT(context);
1410
1411 Py_INCREF(context);
1412 Py_XSETREF(_PyBaseExceptionObject_cast(self)->context, context);
1413}
1414
1415NUITKA_MAY_BE_UNUSED static inline void Nuitka_Exception_DeleteContext(PyObject *self) {
1416 Py_XSETREF(_PyBaseExceptionObject_cast(self)->context, NULL);
1417}
1418
1419#if PYTHON_VERSION >= 0x300
1420// Attach the exception context if necessary.
1421NUITKA_MAY_BE_UNUSED static inline void
1422ADD_EXCEPTION_CONTEXT(PyThreadState *tstate, struct Nuitka_ExceptionPreservationItem *exception_state) {
1423 PyObject *context = EXC_VALUE(tstate);
1424
1425 if (context != NULL) {
1426#if PYTHON_VERSION < 0x3c0
1427 NORMALIZE_EXCEPTION_STATE(tstate, exception_state);
1428#endif
1429 Nuitka_Exception_SetContext(exception_state->exception_value, context);
1430 }
1431}
1432#endif
1433
1434// Our replacement for "PyException_SetCause", consumes a reference.
1435NUITKA_MAY_BE_UNUSED static inline void Nuitka_Exception_SetCause(PyObject *self, PyObject *cause) {
1436 PyBaseExceptionObject *base_self = _PyBaseExceptionObject_cast(self);
1437 base_self->suppress_context = 1;
1438 Py_XSETREF(base_self->cause, cause);
1439}
1440
1441#endif
1442
1443#endif
1444
1445// Part of "Nuitka", an optimizing Python compiler that is compatible and
1446// integrates with CPython, but also works on its own.
1447//
1448// Licensed under the Apache License, Version 2.0 (the "License");
1449// you may not use this file except in compliance with the License.
1450// You may obtain a copy of the License at
1451//
1452// http://www.apache.org/licenses/LICENSE-2.0
1453//
1454// Unless required by applicable law or agreed to in writing, software
1455// distributed under the License is distributed on an "AS IS" BASIS,
1456// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1457// See the License for the specific language governing permissions and
1458// limitations under the License.
Definition exceptions.h:712
Definition exceptions.h:222
Definition compiled_frame.h:117