Nuitka
The Python compiler
Loading...
Searching...
No Matches
compiled_generator.h
1// Copyright 2025, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
3#ifndef __NUITKA_COMPILED_GENERATOR_H__
4#define __NUITKA_COMPILED_GENERATOR_H__
5
6/* This file is included from another C file, help IDEs to still parse it on its own. */
7#ifdef __IDE_ONLY__
8#include "nuitka/prelude.h"
9#endif
10
11// Compiled generator function type.
12
13// Another cornerstone of the integration into CPython. Try to behave as well as
14// normal generator objects do or even better.
15
16// Status of the generator object.
17#ifdef __cplusplus
18enum Generator_Status {
19 status_Unused, // Not used so far
20 status_Running, // Running, used but didn't stop yet
21 status_Finished // Stopped, no more values to come
22};
23#else
24typedef int Generator_Status;
25static const int status_Unused = 0;
26static const int status_Running = 1;
27static const int status_Finished = 2;
28#endif
29
30// We use this even before Python3.10
31#if PYTHON_VERSION < 0x3a0
32typedef enum {
33 PYGEN_RETURN = 0,
34 PYGEN_ERROR = -1,
35 PYGEN_NEXT = 1,
36} PySendResult;
37#endif
38
39// The Nuitka_GeneratorObject is the storage associated with a compiled
40// generator object instance of which there can be many for each code.
42 /* Python object folklore: */
43 PyObject_VAR_HEAD
44
45 PyObject *m_name;
46
47 // TODO: Only to make traceback for non-started throw
48 PyObject *m_module;
49
50#if PYTHON_VERSION >= 0x350
51 PyObject *m_qualname;
52#endif
53#if PYTHON_VERSION >= 0x300
54 // The value currently yielded from.
55 PyObject *m_yield_from;
56#endif
57
58 // Weak references are supported for generator objects in CPython.
59 PyObject *m_weakrefs;
60
61 int m_running;
62
63 void *m_code;
64
65 struct Nuitka_FrameObject *m_frame;
66 PyCodeObject *m_code_object;
67
68 // Was it ever used, is it still running, or already finished.
69 Generator_Status m_status;
70
71#if PYTHON_VERSION >= 0x370
72 struct Nuitka_ExceptionStackItem m_exc_state;
73#endif
74
75#if PYTHON_VERSION >= 0x300
76 struct Nuitka_ExceptionStackItem m_resume_exception;
77#endif
78
79 // The label index to resume after yield.
80 int m_yield_return_index;
81
82 // Returned value if yielded value is NULL, is
83 // NULL if not a return
84#if PYTHON_VERSION >= 0x300
85 PyObject *m_returned;
86#endif
87
88 // A kind of uuid for the generator object, used in comparisons.
89 long m_counter;
90
91 /* The heap of generator objects at run time. */
92 void *m_heap_storage;
93
94 /* Closure variables given, if any, we reference cells here. The last
95 * part is dynamically allocated, the array size differs per generator
96 * and includes the heap storage.
97 */
98 Py_ssize_t m_closure_given;
99 struct Nuitka_CellObject *m_closure[1];
100};
101
102extern PyTypeObject Nuitka_Generator_Type;
103
104typedef PyObject *(*generator_code)(PyThreadState *tstate, struct Nuitka_GeneratorObject *, PyObject *);
105
106extern PyObject *Nuitka_Generator_New(generator_code code, PyObject *module, PyObject *name,
107#if PYTHON_VERSION >= 0x350
108 PyObject *qualname,
109#endif
110 PyCodeObject *code_object, struct Nuitka_CellObject **closure,
111 Py_ssize_t closure_given, Py_ssize_t heap_storage_size);
112
113extern PyObject *Nuitka_Generator_NewEmpty(PyObject *module, PyObject *name,
114#if PYTHON_VERSION >= 0x350
115 PyObject *qualname,
116#endif
117 PyCodeObject *code_object, struct Nuitka_CellObject **closure,
118 Py_ssize_t closure_given);
119
120extern PyObject *Nuitka_Generator_qiter(PyThreadState *tstate, struct Nuitka_GeneratorObject *generator,
121 bool *finished);
122
123static inline bool Nuitka_Generator_Check(PyObject *object) { return Py_TYPE(object) == &Nuitka_Generator_Type; }
124
125static inline PyObject *Nuitka_Generator_GetName(PyObject *object) {
126 return ((struct Nuitka_GeneratorObject *)object)->m_name;
127}
128
129static inline void SAVE_GENERATOR_EXCEPTION(PyThreadState *tstate, struct Nuitka_GeneratorObject *generator) {
130 /* Before Python3.7: When yielding from an exception handler in Python3,
131 * the exception preserved to the frame is restored, while the current one
132 * is put as there.
133 *
134 * Python3.7: The exception is preserved in the generator object itself
135 * which has a new "m_exc_state" structure just for that.
136 */
137
138#if _DEBUG_EXCEPTIONS
139 PRINT_STRING("SAVE_GENERATOR_EXCEPTION: Saving: ");
140 PRINT_PUBLISHED_EXCEPTION();
141#endif
142
143#if PYTHON_VERSION < 0x3b0
144 PyObject *saved_exception_type = EXC_TYPE(tstate);
145#endif
146 PyObject *saved_exception_value = EXC_VALUE(tstate);
147#if PYTHON_VERSION < 0x3b0
148 PyTracebackObject *saved_exception_traceback = EXC_TRACEBACK(tstate);
149#endif
150
151#if PYTHON_VERSION < 0x370
152 EXC_TYPE(tstate) = tstate->frame->f_exc_type;
153 EXC_VALUE(tstate) = tstate->frame->f_exc_value;
154 SET_EXC_TRACEBACK(tstate, tstate->frame->f_exc_traceback);
155#else
156#if PYTHON_VERSION < 0x3b0
157 EXC_TYPE(tstate) = generator->m_exc_state.exception_type;
158#endif
159 EXC_VALUE(tstate) = generator->m_exc_state.exception_value;
160#if PYTHON_VERSION < 0x3b0
161 SET_EXC_TRACEBACK(tstate, generator->m_exc_state.exception_tb);
162#endif
163#endif
164
165#if _DEBUG_EXCEPTIONS
166 PRINT_STRING("SAVE_GENERATOR_EXCEPTION: Restored: ");
167 PRINT_PUBLISHED_EXCEPTION();
168#endif
169
170#if PYTHON_VERSION < 0x370
171 tstate->frame->f_exc_type = saved_exception_type;
172 tstate->frame->f_exc_value = saved_exception_value;
173 tstate->frame->f_exc_traceback = (PyObject *)saved_exception_traceback;
174#else
175#if PYTHON_VERSION < 0x3b0
176 generator->m_exc_state.exception_type = saved_exception_type;
177#endif
178 generator->m_exc_state.exception_value = saved_exception_value;
179#if PYTHON_VERSION < 0x3b0
180 generator->m_exc_state.exception_tb = (PyTracebackObject *)saved_exception_traceback;
181#endif
182#endif
183}
184
185static inline void RESTORE_GENERATOR_EXCEPTION(PyThreadState *tstate, struct Nuitka_GeneratorObject *generator) {
186 // When returning from yield, the exception of the frame is preserved, and
187 // the one that enters should be there.
188
189#if _DEBUG_EXCEPTIONS
190 PRINT_STRING("RESTORE_GENERATOR_EXCEPTION: Replacing :");
191 PRINT_PUBLISHED_EXCEPTION();
192#endif
193
194#if PYTHON_VERSION < 0x3b0
195 PyObject *saved_exception_type = EXC_TYPE(tstate);
196#endif
197 PyObject *saved_exception_value = EXC_VALUE(tstate);
198#if PYTHON_VERSION < 0x3b0
199 PyTracebackObject *saved_exception_traceback = EXC_TRACEBACK(tstate);
200#endif
201
202#if PYTHON_VERSION < 0x370
203 EXC_TYPE(tstate) = tstate->frame->f_exc_type;
204 EXC_VALUE(tstate) = tstate->frame->f_exc_value;
205 SET_EXC_TRACEBACK(tstate, tstate->frame->f_exc_traceback);
206
207 tstate->frame->f_exc_type = saved_exception_type;
208 tstate->frame->f_exc_value = saved_exception_value;
209 tstate->frame->f_exc_traceback = (PyObject *)saved_exception_traceback;
210#else
211#if PYTHON_VERSION < 0x3b0
212 EXC_TYPE(tstate) = generator->m_exc_state.exception_type;
213#endif
214 EXC_VALUE(tstate) = generator->m_exc_state.exception_value;
215#if PYTHON_VERSION < 0x3b0
216 SET_EXC_TRACEBACK(tstate, generator->m_exc_state.exception_tb);
217#endif
218
219#if PYTHON_VERSION < 0x3b0
220 generator->m_exc_state.exception_type = saved_exception_type;
221#endif
222 generator->m_exc_state.exception_value = saved_exception_value;
223#if PYTHON_VERSION < 0x3b0
224 generator->m_exc_state.exception_tb = (PyTracebackObject *)saved_exception_traceback;
225#endif
226#endif
227
228#if _DEBUG_EXCEPTIONS
229 PRINT_STRING("RESTORE_GENERATOR_EXCEPTION: Restored:");
230 PRINT_PUBLISHED_EXCEPTION();
231#endif
232}
233
234// Functions to preserver and restore from heap area temporary values during
235// yield/yield from/await exits of generator functions.
236extern void Nuitka_PreserveHeap(void *dest, ...);
237extern void Nuitka_RestoreHeap(void *source, ...);
238
239NUITKA_MAY_BE_UNUSED static void STORE_GENERATOR_EXCEPTION(PyThreadState *tstate,
240 struct Nuitka_GeneratorObject *generator) {
241#if PYTHON_VERSION < 0x3b0
242 EXC_TYPE_F(generator) = EXC_TYPE(tstate);
243 if (EXC_TYPE_F(generator) == Py_None) {
244 EXC_TYPE_F(generator) = NULL;
245 }
246 Py_XINCREF(EXC_TYPE_F(generator));
247#endif
248 EXC_VALUE_F(generator) = EXC_VALUE(tstate);
249 Py_XINCREF(EXC_VALUE_F(generator));
250#if PYTHON_VERSION < 0x3b0
251 ASSIGN_EXC_TRACEBACK_F(generator, EXC_TRACEBACK(tstate));
252 Py_XINCREF(EXC_TRACEBACK_F(generator));
253#endif
254}
255
256NUITKA_MAY_BE_UNUSED static void DROP_GENERATOR_EXCEPTION(struct Nuitka_GeneratorObject *generator) {
257#if PYTHON_VERSION < 0x3b0
258 Py_CLEAR(EXC_TYPE_F(generator));
259#endif
260 Py_CLEAR(EXC_VALUE_F(generator));
261#if PYTHON_VERSION < 0x3b0
262 Py_CLEAR(EXC_TRACEBACK_F(generator));
263#endif
264}
265
266#if _DEBUG_REFCOUNTS
267extern int count_active_Nuitka_Generator_Type;
268extern int count_allocated_Nuitka_Generator_Type;
269extern int count_released_Nuitka_Generator_Type;
270#endif
271
272#endif
273
274// Part of "Nuitka", an optimizing Python compiler that is compatible and
275// integrates with CPython, but also works on its own.
276//
277// Licensed under the Apache License, Version 2.0 (the "License");
278// you may not use this file except in compliance with the License.
279// You may obtain a copy of the License at
280//
281// http://www.apache.org/licenses/LICENSE-2.0
282//
283// Unless required by applicable law or agreed to in writing, software
284// distributed under the License is distributed on an "AS IS" BASIS,
285// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
286// See the License for the specific language governing permissions and
287// limitations under the License.
Definition compiled_cell.h:14
Definition exceptions.h:222
Definition compiled_frame.h:117
Definition compiled_generator.h:41