1 // Copyright 2021, Kay Hayen, mailto:kay.hayen@gmail.com
2 //
3 // Part of "Nuitka", an optimizing Python compiler that is compatible and
4 // integrates with CPython, but also works on its own.
5 //
6 // Licensed under the Apache License, Version 2.0 (the "License");
7 // you may not use this file except in compliance with the License.
8 // You may obtain a copy of the License at
9 //
10 // http://www.apache.org/licenses/LICENSE-2.0
11 //
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
18 #ifndef __NUITKA_COMPILED_COROUTINE_H__
19 #define __NUITKA_COMPILED_COROUTINE_H__
20
21 // Compiled coroutine type.
22
23 // Another cornerstone of the integration into CPython. Try to behave as well as
24 // normal coroutine objects do or even better.
25
26 #if PYTHON_VERSION >= 0x350
27
28 // The Nuitka_CoroutineObject is the storage associated with a compiled
29 // coroutine object instance of which there can be many for each code.
30 struct Nuitka_CoroutineObject {
31 /* Python object folklore: */
32 PyObject_VAR_HEAD;
33
34 PyObject *m_name;
35
36 // TODO: Only to make traceback for non-started throw
37 PyObject *m_module;
38
39 PyObject *m_qualname;
40 PyObject *m_yieldfrom;
41
42 // Weak references are supported for coroutine objects in CPython.
43 PyObject *m_weakrefs;
44
45 int m_running;
46
47 // When a coroutine is awaiting, this flag is set.
48 int m_awaiting;
49
50 void *m_code;
51
52 // The parent frame of the coroutine, if created.
53 struct Nuitka_FrameObject *m_frame;
54
55 PyCodeObject *m_code_object;
56
57 // While yielding, this was the frame currently active, restore when
58 // resuming.
59 struct Nuitka_FrameObject *m_resume_frame;
60
61 // Was it ever used, is it still running, or already finished.
62 Generator_Status m_status;
63
64 #if PYTHON_VERSION >= 0x370
65 _PyErr_StackItem m_exc_state;
66
67 // The cr_origin attribute.
68 PyObject *m_origin;
69 #endif
70
71 // The label index to resume after yield.
72 int m_yield_return_index;
73
74 // Returned value if yielded value is NULL, is
75 // NULL if not a return
76 PyObject *m_returned;
77
78 /* The heap of generator objects at run time. */
79 void *m_heap_storage;
80
81 /* Closure variables given, if any, we reference cells here. The last
82 * part is dynamically allocated, the array size differs per coroutine
83 * and includes the heap storage.
84 */
85 Py_ssize_t m_closure_given;
86 struct Nuitka_CellObject *m_closure[1];
87 };
88
89 extern PyTypeObject Nuitka_Coroutine_Type;
90
91 typedef PyObject *(*coroutine_code)(struct Nuitka_CoroutineObject *, PyObject *);
92
93 extern PyObject *Nuitka_Coroutine_New(coroutine_code code, PyObject *module, PyObject *name, PyObject *qualname,
94 PyCodeObject *code_object, struct Nuitka_CellObject **closure,
95 Py_ssize_t closure_given, Py_ssize_t heap_storage_size);
96
Nuitka_Coroutine_Check(PyObject * object)97 static inline bool Nuitka_Coroutine_Check(PyObject *object) { return Py_TYPE(object) == &Nuitka_Coroutine_Type; }
98
99 struct Nuitka_CoroutineWrapperObject {
100 /* Python object folklore: */
101 PyObject_HEAD;
102
103 struct Nuitka_CoroutineObject *m_coroutine;
104 };
105
106 extern PyTypeObject Nuitka_CoroutineWrapper_Type;
107
Nuitka_CoroutineWrapper_Check(PyObject * object)108 static inline bool Nuitka_CoroutineWrapper_Check(PyObject *object) {
109 return Py_TYPE(object) == &Nuitka_CoroutineWrapper_Type;
110 }
111
SAVE_COROUTINE_EXCEPTION(struct Nuitka_CoroutineObject * coroutine)112 static inline void SAVE_COROUTINE_EXCEPTION(struct Nuitka_CoroutineObject *coroutine) {
113 /* Before Python3.7: When yielding from an exception handler in Python3,
114 * the exception preserved to the frame is restored, while the current one
115 * is put as there.
116 *
117 * Python3.7: The exception is preserved in the coroutine object itself
118 * which has a new "m_exc_state" structure just for that.
119 */
120
121 PyThreadState *thread_state = PyThreadState_GET();
122
123 PyObject *saved_exception_type = EXC_TYPE(thread_state);
124 PyObject *saved_exception_value = EXC_VALUE(thread_state);
125 PyObject *saved_exception_traceback = EXC_TRACEBACK(thread_state);
126
127 CHECK_OBJECT_X(saved_exception_type);
128 CHECK_OBJECT_X(saved_exception_value);
129 CHECK_OBJECT_X(saved_exception_traceback);
130
131 #if PYTHON_VERSION < 0x370
132 EXC_TYPE(thread_state) = thread_state->frame->f_exc_type;
133 EXC_VALUE(thread_state) = thread_state->frame->f_exc_value;
134 EXC_TRACEBACK(thread_state) = thread_state->frame->f_exc_traceback;
135 #else
136 EXC_TYPE(thread_state) = coroutine->m_exc_state.exc_type;
137 EXC_VALUE(thread_state) = coroutine->m_exc_state.exc_value;
138 EXC_TRACEBACK(thread_state) = coroutine->m_exc_state.exc_traceback;
139 #endif
140
141 CHECK_OBJECT_X(EXC_TYPE(thread_state));
142 CHECK_OBJECT_X(EXC_VALUE(thread_state));
143 CHECK_OBJECT_X(EXC_TRACEBACK(thread_state));
144
145 #if PYTHON_VERSION < 0x370
146 thread_state->frame->f_exc_type = saved_exception_type;
147 thread_state->frame->f_exc_value = saved_exception_value;
148 thread_state->frame->f_exc_traceback = saved_exception_traceback;
149 #else
150 coroutine->m_exc_state.exc_type = saved_exception_type;
151 coroutine->m_exc_state.exc_value = saved_exception_value;
152 coroutine->m_exc_state.exc_traceback = saved_exception_traceback;
153 #endif
154 }
155
RESTORE_COROUTINE_EXCEPTION(struct Nuitka_CoroutineObject * coroutine)156 static inline void RESTORE_COROUTINE_EXCEPTION(struct Nuitka_CoroutineObject *coroutine) {
157 // When returning from yield, the exception of the frame is preserved, and
158 // the one that enters should be there.
159 PyThreadState *thread_state = PyThreadState_GET();
160
161 PyObject *saved_exception_type = EXC_TYPE(thread_state);
162 PyObject *saved_exception_value = EXC_VALUE(thread_state);
163 PyObject *saved_exception_traceback = EXC_TRACEBACK(thread_state);
164
165 CHECK_OBJECT_X(saved_exception_type);
166 CHECK_OBJECT_X(saved_exception_value);
167 CHECK_OBJECT_X(saved_exception_traceback);
168
169 #if PYTHON_VERSION < 0x370
170 EXC_TYPE(thread_state) = thread_state->frame->f_exc_type;
171 EXC_VALUE(thread_state) = thread_state->frame->f_exc_value;
172 EXC_TRACEBACK(thread_state) = thread_state->frame->f_exc_traceback;
173
174 thread_state->frame->f_exc_type = saved_exception_type;
175 thread_state->frame->f_exc_value = saved_exception_value;
176 thread_state->frame->f_exc_traceback = saved_exception_traceback;
177 #else
178 EXC_TYPE(thread_state) = coroutine->m_exc_state.exc_type;
179 EXC_VALUE(thread_state) = coroutine->m_exc_state.exc_value;
180 EXC_TRACEBACK(thread_state) = coroutine->m_exc_state.exc_traceback;
181
182 coroutine->m_exc_state.exc_type = saved_exception_type;
183 coroutine->m_exc_state.exc_value = saved_exception_value;
184 coroutine->m_exc_state.exc_traceback = saved_exception_traceback;
185 #endif
186
187 CHECK_OBJECT_X(EXC_TYPE(thread_state));
188 CHECK_OBJECT_X(EXC_VALUE(thread_state));
189 CHECK_OBJECT_X(EXC_TRACEBACK(thread_state));
190 }
191
192 #ifdef __cplusplus
193 enum Await_Kind {
194 await_normal, // user provided "await"
195 await_enter, // async with statement "__enter__"
196 await_exit // async with statement "__enter__"
197 };
198 #else
199 typedef int Generator_Status;
200 static const int await_normal = 0;
201 static const int await_enter = 1;
202 static const int await_exit = 2;
203 #endif
204
205 // Create the object to await for async for "iter".
206 extern PyObject *ASYNC_MAKE_ITERATOR(PyObject *value);
207
208 // Create the object to await for async for "next".
209 extern PyObject *ASYNC_ITERATOR_NEXT(PyObject *value);
210
211 // Create the object for plain "await".
212 extern PyObject *ASYNC_AWAIT(PyObject *awaitable, int await_kind);
213
214 #endif
215
216 // For reference count debugging.
217 #if _DEBUG_REFCOUNTS
218 extern int count_active_Nuitka_Coroutine_Type;
219 extern int count_allocated_Nuitka_Coroutine_Type;
220 extern int count_released_Nuitka_Coroutine_Type;
221
222 extern int count_active_Nuitka_CoroutineWrapper_Type;
223 extern int count_allocated_Nuitka_CoroutineWrapper_Type;
224 extern int count_released_Nuitka_CoroutineWrapper_Type;
225
226 extern int count_active_Nuitka_AIterWrapper_Type;
227 extern int count_allocated_Nuitka_AIterWrapper_Type;
228 extern int count_released_Nuitka_AIterWrapper_Type;
229 #endif
230
231 #endif
232