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