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 /** Compiled Asyncgen.
19  *
20  * Unlike in CPython, we have one type for just asyncgen, this doesn't do generators
21  * nor coroutines.
22  *
23  * It strives to be full replacement for normal asyncgen.
24  *
25  */
26 
27 // This file is included from another C file, help IDEs to still parse it on
28 // its own.
29 #ifdef __IDE_ONLY__
30 #include "nuitka/freelists.h"
31 #include "nuitka/prelude.h"
32 #include <structmember.h>
33 #endif
34 
35 // For reporting about reference counts per type.
36 #if _DEBUG_REFCOUNTS
37 int count_active_Nuitka_Asyncgen_Type = 0;
38 int count_allocated_Nuitka_Asyncgen_Type = 0;
39 int count_released_Nuitka_Asyncgen_Type = 0;
40 int count_active_Nuitka_AsyncgenValueWrapper_Type = 0;
41 int count_allocated_Nuitka_AsyncgenValueWrapper_Type = 0;
42 int count_released_Nuitka_AsyncgenValueWrapper_Type = 0;
43 int count_active_Nuitka_AsyncgenAsend_Type = 0;
44 int count_allocated_Nuitka_AsyncgenAsend_Type = 0;
45 int count_released_Nuitka_AsyncgenAsend_Type = 0;
46 int count_active_Nuitka_AsyncgenAthrow_Type = 0;
47 int count_allocated_Nuitka_AsyncgenAthrow_Type = 0;
48 int count_released_Nuitka_AsyncgenAthrow_Type = 0;
49 #endif
50 
Nuitka_Asyncgen_get_name(struct Nuitka_AsyncgenObject * asyncgen)51 static PyObject *Nuitka_Asyncgen_get_name(struct Nuitka_AsyncgenObject *asyncgen) {
52     CHECK_OBJECT(asyncgen);
53 
54     Py_INCREF(asyncgen->m_name);
55     return asyncgen->m_name;
56 }
57 
Nuitka_Asyncgen_set_name(struct Nuitka_AsyncgenObject * asyncgen,PyObject * value)58 static int Nuitka_Asyncgen_set_name(struct Nuitka_AsyncgenObject *asyncgen, PyObject *value) {
59     CHECK_OBJECT(asyncgen);
60     CHECK_OBJECT_X(value);
61 
62     // Cannot be deleted, not be non-unicode value.
63     if (unlikely((value == NULL) || !PyUnicode_Check(value))) {
64         SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_TypeError, "__name__ must be set to a string object");
65 
66         return -1;
67     }
68 
69     PyObject *tmp = asyncgen->m_name;
70     Py_INCREF(value);
71     asyncgen->m_name = value;
72     Py_DECREF(tmp);
73 
74     return 0;
75 }
76 
Nuitka_Asyncgen_get_qualname(struct Nuitka_AsyncgenObject * asyncgen)77 static PyObject *Nuitka_Asyncgen_get_qualname(struct Nuitka_AsyncgenObject *asyncgen) {
78     CHECK_OBJECT(asyncgen);
79 
80     Py_INCREF(asyncgen->m_qualname);
81     return asyncgen->m_qualname;
82 }
83 
Nuitka_Asyncgen_set_qualname(struct Nuitka_AsyncgenObject * asyncgen,PyObject * value)84 static int Nuitka_Asyncgen_set_qualname(struct Nuitka_AsyncgenObject *asyncgen, PyObject *value) {
85     CHECK_OBJECT(asyncgen);
86     CHECK_OBJECT_X(value);
87 
88     // Cannot be deleted, not be non-unicode value.
89     if (unlikely((value == NULL) || !PyUnicode_Check(value))) {
90         SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_TypeError, "__qualname__ must be set to a string object");
91 
92         return -1;
93     }
94 
95     PyObject *tmp = asyncgen->m_qualname;
96     Py_INCREF(value);
97     asyncgen->m_qualname = value;
98     Py_DECREF(tmp);
99 
100     return 0;
101 }
102 
Nuitka_Asyncgen_get_ag_await(struct Nuitka_AsyncgenObject * asyncgen)103 static PyObject *Nuitka_Asyncgen_get_ag_await(struct Nuitka_AsyncgenObject *asyncgen) {
104     CHECK_OBJECT(asyncgen);
105 
106     if (asyncgen->m_yieldfrom) {
107         Py_INCREF(asyncgen->m_yieldfrom);
108         return asyncgen->m_yieldfrom;
109     } else {
110         Py_INCREF(Py_None);
111         return Py_None;
112     }
113 }
114 
Nuitka_Asyncgen_get_code(struct Nuitka_AsyncgenObject * asyncgen)115 static PyObject *Nuitka_Asyncgen_get_code(struct Nuitka_AsyncgenObject *asyncgen) {
116     CHECK_OBJECT(asyncgen);
117     CHECK_OBJECT(asyncgen->m_code_object);
118 
119     Py_INCREF(asyncgen->m_code_object);
120     return (PyObject *)asyncgen->m_code_object;
121 }
122 
Nuitka_Asyncgen_set_code(struct Nuitka_AsyncgenObject * asyncgen,PyObject * value)123 static int Nuitka_Asyncgen_set_code(struct Nuitka_AsyncgenObject *asyncgen, PyObject *value) {
124     CHECK_OBJECT(asyncgen);
125 
126     SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "ag_code is not writable in Nuitka");
127     return -1;
128 }
129 
Nuitka_Asyncgen_get_frame(struct Nuitka_AsyncgenObject * asyncgen)130 static PyObject *Nuitka_Asyncgen_get_frame(struct Nuitka_AsyncgenObject *asyncgen) {
131     CHECK_OBJECT(asyncgen);
132     CHECK_OBJECT_X(asyncgen->m_frame);
133 
134     if (asyncgen->m_frame) {
135         Py_INCREF(asyncgen->m_frame);
136         return (PyObject *)asyncgen->m_frame;
137     } else {
138         Py_INCREF(Py_None);
139         return Py_None;
140     }
141 }
142 
Nuitka_Asyncgen_set_frame(struct Nuitka_AsyncgenObject * asyncgen,PyObject * value)143 static int Nuitka_Asyncgen_set_frame(struct Nuitka_AsyncgenObject *asyncgen, PyObject *value) {
144     CHECK_OBJECT(asyncgen);
145     CHECK_OBJECT_X(value);
146 
147     SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "ag_frame is not writable in Nuitka");
148     return -1;
149 }
150 
Nuitka_Asyncgen_release_closure(struct Nuitka_AsyncgenObject * asyncgen)151 static void Nuitka_Asyncgen_release_closure(struct Nuitka_AsyncgenObject *asyncgen) {
152     CHECK_OBJECT(asyncgen);
153 
154     for (Py_ssize_t i = 0; i < asyncgen->m_closure_given; i++) {
155         CHECK_OBJECT(asyncgen->m_closure[i]);
156         Py_DECREF(asyncgen->m_closure[i]);
157     }
158 
159     asyncgen->m_closure_given = 0;
160 }
161 
Nuitka_YieldFromAsyncgenCore(struct Nuitka_AsyncgenObject * asyncgen,PyObject * send_value,bool mode)162 static PyObject *Nuitka_YieldFromAsyncgenCore(struct Nuitka_AsyncgenObject *asyncgen, PyObject *send_value, bool mode) {
163     CHECK_OBJECT(asyncgen);
164     CHECK_OBJECT_X(send_value);
165 
166     PyObject *yieldfrom = asyncgen->m_yieldfrom;
167     CHECK_OBJECT(yieldfrom);
168 
169     // Need to make it unaccessible while using it.
170     asyncgen->m_yieldfrom = NULL;
171 
172     PyObject *returned_value;
173     PyObject *yielded = _Nuitka_YieldFromCore(yieldfrom, send_value, &returned_value, mode);
174 
175     if (yielded == NULL) {
176         assert(asyncgen->m_yieldfrom == NULL);
177         Py_DECREF(yieldfrom);
178 
179         yielded = ((asyncgen_code)asyncgen->m_code)(asyncgen, returned_value);
180     } else {
181         assert(asyncgen->m_yieldfrom == NULL);
182         asyncgen->m_yieldfrom = yieldfrom;
183     }
184 
185     return yielded;
186 }
187 
188 #if _DEBUG_ASYNCGEN
_PRINT_ASYNCGEN_STATUS(char const * descriptor,char const * context,struct Nuitka_AsyncgenObject * asyncgen)189 NUITKA_MAY_BE_UNUSED static void _PRINT_ASYNCGEN_STATUS(char const *descriptor, char const *context,
190                                                         struct Nuitka_AsyncgenObject *asyncgen) {
191     char const *status;
192 
193     switch (asyncgen->m_status) {
194     case status_Finished:
195         status = "(finished)";
196         break;
197     case status_Running:
198         status = "(running)";
199         break;
200     case status_Unused:
201         status = "(unused)";
202         break;
203     default:
204         status = "(ILLEGAL)";
205         break;
206     }
207 
208     PRINT_STRING(descriptor);
209     PRINT_STRING(" : ");
210     PRINT_STRING(context);
211     PRINT_STRING(" ");
212     PRINT_ITEM((PyObject *)asyncgen);
213     PRINT_STRING(" ");
214     PRINT_STRING(status);
215     PRINT_NEW_LINE();
216 }
217 
218 #define PRINT_ASYNCGEN_STATUS(context, asyncgen) _PRINT_ASYNCGEN_STATUS(__FUNCTION__, context, asyncgen)
219 
220 #endif
221 
Nuitka_YieldFromAsyncgenNext(struct Nuitka_AsyncgenObject * asyncgen)222 static PyObject *Nuitka_YieldFromAsyncgenNext(struct Nuitka_AsyncgenObject *asyncgen) {
223     CHECK_OBJECT(asyncgen);
224 
225 #if _DEBUG_ASYNCGEN
226     PRINT_ASYNCGEN_STATUS("Enter", asyncgen);
227     PRINT_NEW_LINE();
228 #endif
229     PyObject *result = Nuitka_YieldFromAsyncgenCore(asyncgen, Py_None, true);
230 #if _DEBUG_ASYNCGEN
231     PRINT_ASYNCGEN_STATUS("Leave", asyncgen);
232     PRINT_CURRENT_EXCEPTION();
233     PRINT_NEW_LINE();
234 #endif
235 
236     return result;
237 }
238 
Nuitka_YieldFromAsyncgenInitial(struct Nuitka_AsyncgenObject * asyncgen,PyObject * send_value)239 static PyObject *Nuitka_YieldFromAsyncgenInitial(struct Nuitka_AsyncgenObject *asyncgen, PyObject *send_value) {
240     CHECK_OBJECT(asyncgen);
241     CHECK_OBJECT_X(send_value);
242 
243 #if _DEBUG_ASYNCGEN
244     PRINT_ASYNCGEN_STATUS("Enter", asyncgen);
245     PRINT_NEW_LINE();
246 #endif
247     PyObject *result = Nuitka_YieldFromAsyncgenCore(asyncgen, send_value, false);
248 #if _DEBUG_ASYNCGEN
249     PRINT_ASYNCGEN_STATUS("Leave", asyncgen);
250     PRINT_CURRENT_EXCEPTION();
251     PRINT_NEW_LINE();
252 #endif
253 
254     return result;
255 }
256 
257 static PyObject *Nuitka_AsyncgenValueWrapper_New(PyObject *value);
258 
_Nuitka_Asyncgen_send(struct Nuitka_AsyncgenObject * asyncgen,PyObject * value,bool closing,PyObject * exception_type,PyObject * exception_value,PyTracebackObject * exception_tb)259 static PyObject *_Nuitka_Asyncgen_send(struct Nuitka_AsyncgenObject *asyncgen, PyObject *value, bool closing,
260                                        PyObject *exception_type, PyObject *exception_value,
261                                        PyTracebackObject *exception_tb) {
262     CHECK_OBJECT(asyncgen);
263     assert(Nuitka_Asyncgen_Check((PyObject *)asyncgen));
264     CHECK_OBJECT_X(value);
265     CHECK_OBJECT_X(exception_type);
266     CHECK_OBJECT_X(exception_value);
267     CHECK_OBJECT_X(exception_tb);
268 
269 #if _DEBUG_ASYNCGEN
270     PRINT_ASYNCGEN_STATUS("Enter", asyncgen);
271     PRINT_COROUTINE_VALUE("value", value);
272     PRINT_EXCEPTION(exception_type, exception_value, exception_tb);
273     PRINT_CURRENT_EXCEPTION();
274     PRINT_NEW_LINE();
275 #endif
276 
277     if (value != NULL) {
278         assert(exception_type == NULL);
279         assert(exception_value == NULL);
280         assert(exception_tb == NULL);
281     }
282 
283     if (asyncgen->m_status == status_Unused && value != NULL && value != Py_None) {
284         // No exception if value is given.
285 
286         SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_TypeError, "can't send non-None value to a just-started async generator");
287         return NULL;
288     }
289 
290     if (asyncgen->m_status != status_Finished) {
291         if (asyncgen->m_running) {
292             SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_ValueError, "async generator already executing");
293             return NULL;
294         }
295 
296         PyThreadState *thread_state = PyThreadState_GET();
297 
298         // Put the asyncgen back on the frame stack.
299 
300         // First take of running frame from the stack, owning a reference.
301         PyFrameObject *return_frame = thread_state->frame;
302 
303 #ifndef __NUITKA_NO_ASSERT__
304         if (return_frame) {
305             assertFrameObject((struct Nuitka_FrameObject *)return_frame);
306         }
307 #endif
308 
309         if (asyncgen->m_resume_frame) {
310             // It would be nice if our frame were still alive. Nobody had the
311             // right to release it.
312             assertFrameObject(asyncgen->m_resume_frame);
313 
314             // It's not supposed to be on the top right now.
315             assert(return_frame != &asyncgen->m_resume_frame->m_frame);
316 
317             thread_state->frame = &asyncgen->m_frame->m_frame;
318             asyncgen->m_resume_frame = NULL;
319         }
320 
321         // Consider it as running.
322         if (asyncgen->m_status == status_Unused) {
323             asyncgen->m_status = status_Running;
324         }
325 
326         // Continue the yielder function while preventing recursion.
327         asyncgen->m_running = true;
328 
329         // Check for thrown exception, and publish it.
330         if (unlikely(exception_type != NULL)) {
331             assert(value == NULL);
332 
333             // Transfer exception ownership to published.
334             RESTORE_ERROR_OCCURRED(exception_type, exception_value, exception_tb);
335         }
336 
337         if (asyncgen->m_frame) {
338             Nuitka_Frame_MarkAsExecuting(asyncgen->m_frame);
339         }
340 
341 #if _DEBUG_ASYNCGEN
342         PRINT_ASYNCGEN_STATUS("Switching to asyncgen", asyncgen);
343         PRINT_COROUTINE_VALUE("value", value);
344         PRINT_CURRENT_EXCEPTION();
345         PRINT_NEW_LINE();
346         // dumpFrameStack();
347 #endif
348 
349         PyObject *yielded;
350 
351         if (asyncgen->m_yieldfrom == NULL) {
352             yielded = ((asyncgen_code)asyncgen->m_code)(asyncgen, value);
353         } else {
354             yielded = Nuitka_YieldFromAsyncgenInitial(asyncgen, value);
355         }
356 
357         // If the asyncgen returns with m_yieldfrom set, it wants us to yield
358         // from that value from now on.
359         while (yielded == NULL && asyncgen->m_yieldfrom != NULL) {
360             yielded = Nuitka_YieldFromAsyncgenNext(asyncgen);
361         }
362 
363         if (asyncgen->m_frame) {
364             Nuitka_Frame_MarkAsNotExecuting(asyncgen->m_frame);
365         }
366 
367         asyncgen->m_running = false;
368 
369         thread_state = PyThreadState_GET();
370 
371         // Remove the back frame from asyncgen if it's there.
372         if (asyncgen->m_frame) {
373             // assert(thread_state->frame == &asyncgen->m_frame->m_frame);
374             assertFrameObject(asyncgen->m_frame);
375 
376             Py_CLEAR(asyncgen->m_frame->m_frame.f_back);
377 
378             // Remember where to resume from.
379             asyncgen->m_resume_frame = (struct Nuitka_FrameObject *)thread_state->frame;
380         }
381 
382         thread_state->frame = return_frame;
383 
384 #if _DEBUG_ASYNCGEN
385         PRINT_ASYNCGEN_STATUS("Returned from coroutine", asyncgen);
386         // dumpFrameStack();
387 #endif
388 
389 #ifndef __NUITKA_NO_ASSERT__
390         if (return_frame) {
391             assertFrameObject((struct Nuitka_FrameObject *)return_frame);
392         }
393 #endif
394 
395         if (yielded == NULL) {
396 #if _DEBUG_ASYNCGEN
397             PRINT_ASYNCGEN_STATUS("finishing from yield", asyncgen);
398             PRINT_STRING("-> finishing sets status_Finished\n");
399             PRINT_NEW_LINE();
400 #endif
401             asyncgen->m_status = status_Finished;
402 
403             if (asyncgen->m_frame != NULL) {
404                 asyncgen->m_frame->m_frame.f_gen = NULL;
405                 Py_DECREF(asyncgen->m_frame);
406                 asyncgen->m_frame = NULL;
407             }
408 
409             Nuitka_Asyncgen_release_closure(asyncgen);
410 
411             PyObject *error_occurred = GET_ERROR_OCCURRED();
412 
413             if (error_occurred == PyExc_StopIteration || error_occurred == PyExc_StopAsyncIteration) {
414                 PyObject *saved_exception_type, *saved_exception_value;
415                 PyTracebackObject *saved_exception_tb;
416 
417                 FETCH_ERROR_OCCURRED(&saved_exception_type, &saved_exception_value, &saved_exception_tb);
418                 NORMALIZE_EXCEPTION(&saved_exception_type, &saved_exception_value, &saved_exception_tb);
419 
420                 if (error_occurred == PyExc_StopIteration) {
421                     SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "async generator raised StopIteration");
422                 } else {
423                     SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "async generator raised StopAsyncIteration");
424                 }
425 
426                 FETCH_ERROR_OCCURRED(&exception_type, &exception_value, &exception_tb);
427 
428                 RAISE_EXCEPTION_WITH_CAUSE(&exception_type, &exception_value, &exception_tb, saved_exception_value);
429 
430                 CHECK_OBJECT(exception_value);
431                 CHECK_OBJECT(saved_exception_value);
432 
433                 Py_INCREF(saved_exception_value);
434                 PyException_SetContext(exception_value, saved_exception_value);
435 
436                 Py_DECREF(saved_exception_type);
437                 Py_XDECREF(saved_exception_tb);
438 
439                 RESTORE_ERROR_OCCURRED(exception_type, exception_value, exception_tb);
440             }
441 
442             return NULL;
443         } else {
444             // For normal yield, wrap the result value before returning.
445             if (asyncgen->m_yieldfrom == NULL) {
446                 // TODO: Why not transfer ownership to constructor.
447                 PyObject *wrapped = Nuitka_AsyncgenValueWrapper_New(yielded);
448                 yielded = wrapped;
449 
450                 assert(yielded != NULL);
451             }
452 
453             return yielded;
454         }
455     } else {
456         // Release exception if any, we are finished with it and will raise another.
457         Py_XDECREF(exception_type);
458         Py_XDECREF(exception_value);
459         Py_XDECREF(exception_tb);
460 
461         SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopAsyncIteration);
462 
463         return NULL;
464     }
465 }
466 
467 // Note: Used by compiled frames.
_Nuitka_Asyncgen_close(struct Nuitka_AsyncgenObject * asyncgen)468 static bool _Nuitka_Asyncgen_close(struct Nuitka_AsyncgenObject *asyncgen) {
469 #if _DEBUG_ASYNCGEN
470     PRINT_ASYNCGEN_STATUS("Enter", asyncgen);
471 #endif
472     CHECK_OBJECT(asyncgen);
473 
474     if (asyncgen->m_status == status_Running) {
475         Py_INCREF(PyExc_GeneratorExit);
476 
477         PyObject *result = _Nuitka_Asyncgen_send(asyncgen, NULL, true, PyExc_GeneratorExit, NULL, NULL);
478 
479         if (unlikely(result)) {
480             Py_DECREF(result);
481 
482             SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "async generator ignored GeneratorExit");
483             return false;
484         } else {
485             PyObject *error = GET_ERROR_OCCURRED();
486             assert(error != NULL);
487 
488             if (EXCEPTION_MATCH_GENERATOR(error)) {
489                 CLEAR_ERROR_OCCURRED();
490 
491                 return true;
492             }
493 
494             return false;
495         }
496     }
497 
498     return true;
499 }
500 
501 static bool _Nuitka_Generator_check_throw2(PyObject **exception_type, PyObject **exception_value,
502                                            PyTracebackObject **exception_tb);
503 
504 // This function is called when yielding to a asyncgen through "_Nuitka_YieldFromPassExceptionTo"
505 // and potentially wrapper objects used by generators, or by the throw method itself.
506 // Note:
507 //   Exception arguments are passed for ownership and must be released before returning. The
508 //   value of exception_type will not be NULL, but the actual exception will not necessarily
509 //   be normalized.
_Nuitka_Asyncgen_throw2(struct Nuitka_AsyncgenObject * asyncgen,bool close_on_genexit,PyObject * exception_type,PyObject * exception_value,PyTracebackObject * exception_tb)510 static PyObject *_Nuitka_Asyncgen_throw2(struct Nuitka_AsyncgenObject *asyncgen, bool close_on_genexit,
511                                          PyObject *exception_type, PyObject *exception_value,
512                                          PyTracebackObject *exception_tb) {
513     CHECK_OBJECT(asyncgen);
514     assert(Nuitka_Asyncgen_Check((PyObject *)asyncgen));
515     CHECK_OBJECT(exception_type);
516     CHECK_OBJECT_X(exception_value);
517     CHECK_OBJECT_X(exception_tb);
518 
519 #if _DEBUG_ASYNCGEN
520     PRINT_ASYNCGEN_STATUS("Enter", asyncgen);
521     PRINT_COROUTINE_VALUE("yieldfrom", asyncgen->m_yieldfrom);
522     PRINT_EXCEPTION(exception_type, exception_value, exception_tb);
523     PRINT_NEW_LINE();
524 #endif
525 
526     if (asyncgen->m_yieldfrom != NULL) {
527         // TODO: This check is not done for coroutines, correct?
528         if (close_on_genexit) {
529             if (EXCEPTION_MATCH_BOOL_SINGLE(exception_type, PyExc_GeneratorExit)) {
530                 // Asynchronous generators need to close the yield_from.
531                 asyncgen->m_running = 1;
532                 bool res = Nuitka_gen_close_iter(asyncgen->m_yieldfrom);
533                 asyncgen->m_running = 0;
534 
535                 if (res == false) {
536                     // Release exception, we are done with it now and pick up the new one.
537                     Py_DECREF(exception_type);
538                     Py_XDECREF(exception_value);
539                     Py_XDECREF(exception_tb);
540 
541                     FETCH_ERROR_OCCURRED(&exception_type, &exception_value, &exception_tb);
542                 }
543 
544                 return _Nuitka_Asyncgen_send(asyncgen, NULL, false, exception_type, exception_value, exception_tb);
545             }
546         }
547 
548         PyObject *ret;
549 
550 #if _DEBUG_ASYNCGEN
551         PRINT_ASYNCGEN_STATUS("Passing to yielded from", asyncgen);
552         PRINT_COROUTINE_VALUE("m_yieldfrom", asyncgen->m_yieldfrom);
553         PRINT_NEW_LINE();
554 #endif
555 
556         if (PyGen_CheckExact(asyncgen->m_yieldfrom) || PyCoro_CheckExact(asyncgen->m_yieldfrom)) {
557             PyGenObject *gen = (PyGenObject *)asyncgen->m_yieldfrom;
558 
559             // Transferred exception ownership to "Nuitka_UncompiledGenerator_throw".
560             asyncgen->m_running = 1;
561             ret = Nuitka_UncompiledGenerator_throw(gen, 1, exception_type, exception_value, exception_tb);
562             asyncgen->m_running = 0;
563         } else if (Nuitka_Generator_Check(asyncgen->m_yieldfrom)) {
564             struct Nuitka_GeneratorObject *gen = ((struct Nuitka_GeneratorObject *)asyncgen->m_yieldfrom);
565             // Transferred exception ownership to "_Nuitka_Generator_throw2".
566             asyncgen->m_running = 1;
567             ret = _Nuitka_Generator_throw2(gen, exception_type, exception_value, exception_tb);
568             asyncgen->m_running = 0;
569         } else if (Nuitka_Coroutine_Check(asyncgen->m_yieldfrom)) {
570             struct Nuitka_CoroutineObject *coro = ((struct Nuitka_CoroutineObject *)asyncgen->m_yieldfrom);
571             // Transferred exception ownership to "_Nuitka_Coroutine_throw2".
572             asyncgen->m_running = 1;
573             ret = _Nuitka_Coroutine_throw2(coro, true, exception_type, exception_value, exception_tb);
574             asyncgen->m_running = 0;
575         } else if (Nuitka_CoroutineWrapper_Check(asyncgen->m_yieldfrom)) {
576             struct Nuitka_CoroutineObject *coro =
577                 ((struct Nuitka_CoroutineWrapperObject *)asyncgen->m_yieldfrom)->m_coroutine;
578 
579             // Transferred exception ownership to "_Nuitka_Coroutine_throw2".
580             asyncgen->m_running = 1;
581             ret = _Nuitka_Coroutine_throw2(coro, true, exception_type, exception_value, exception_tb);
582             asyncgen->m_running = 0;
583         } else if (Nuitka_AsyncgenAsend_Check(asyncgen->m_yieldfrom)) {
584             struct Nuitka_AsyncgenAsendObject *asyncgen_asend =
585                 ((struct Nuitka_AsyncgenAsendObject *)asyncgen->m_yieldfrom);
586 
587             // Transferred exception ownership to "_Nuitka_AsyncgenAsend_throw2".
588             asyncgen->m_running = 1;
589             ret = _Nuitka_AsyncgenAsend_throw2(asyncgen_asend, exception_type, exception_value, exception_tb);
590             asyncgen->m_running = 0;
591         } else {
592             PyObject *meth = PyObject_GetAttr(asyncgen->m_yieldfrom, const_str_plain_throw);
593             if (unlikely(meth == NULL)) {
594                 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
595                     // Release exception, we are done with it now.
596                     Py_DECREF(exception_type);
597                     Py_XDECREF(exception_value);
598                     Py_XDECREF(exception_tb);
599 
600                     return NULL;
601                 }
602 
603                 CLEAR_ERROR_OCCURRED();
604 
605                 // Passing exception ownership to that code.
606                 goto throw_here;
607             }
608 
609             CHECK_OBJECT(exception_type);
610 
611 #if 0
612             // TODO: Add slow mode traces.
613             PRINT_ITEM(coroutine->m_yieldfrom);
614             PRINT_NEW_LINE();
615 #endif
616 
617             asyncgen->m_running = 1;
618             ret = PyObject_CallFunctionObjArgs(meth, exception_type, exception_value, exception_tb, NULL);
619             asyncgen->m_running = 0;
620 
621             Py_DECREF(meth);
622 
623             // Release exception, we are done with it now.
624             Py_DECREF(exception_type);
625             Py_XDECREF(exception_value);
626             Py_XDECREF(exception_tb);
627         }
628 
629         if (unlikely(ret == NULL)) {
630             PyObject *val;
631 
632             if (_PyGen_FetchStopIterationValue(&val) == 0) {
633                 CHECK_OBJECT(val);
634 
635                 asyncgen->m_yieldfrom = NULL;
636 
637                 // Return value, not to continue with yielding from.
638                 if (asyncgen->m_yieldfrom != NULL) {
639                     CHECK_OBJECT(asyncgen->m_yieldfrom);
640 #if _DEBUG_ASYNCGEN
641                     PRINT_ASYNCGEN_STATUS("Yield from removal:", asyncgen);
642                     PRINT_COROUTINE_VALUE("yieldfrom", asyncgen->m_yieldfrom);
643 #endif
644                     Py_DECREF(asyncgen->m_yieldfrom);
645                     asyncgen->m_yieldfrom = NULL;
646                 }
647 
648 #if _DEBUG_ASYNCGEN
649                 PRINT_ASYNCGEN_STATUS("Sending return value into ourselves", asyncgen);
650                 PRINT_COROUTINE_VALUE("value", val);
651                 PRINT_NEW_LINE();
652 #endif
653 
654                 ret = _Nuitka_Asyncgen_send(asyncgen, val, false, NULL, NULL, NULL);
655             } else {
656 #if _DEBUG_ASYNCGEN
657                 PRINT_ASYNCGEN_STATUS("Sending exception value into ourselves", asyncgen);
658                 PRINT_COROUTINE_VALUE("yieldfrom", asyncgen->m_yieldfrom);
659                 PRINT_CURRENT_EXCEPTION();
660                 PRINT_NEW_LINE();
661 #endif
662                 ret = _Nuitka_Asyncgen_send(asyncgen, NULL, false, NULL, NULL, NULL);
663             }
664 
665 #if _DEBUG_ASYNCGEN
666             PRINT_ASYNCGEN_STATUS("Leave with value/exception from sending into ourselves:", asyncgen);
667             PRINT_COROUTINE_VALUE("return_value", ret);
668             PRINT_CURRENT_EXCEPTION();
669             PRINT_NEW_LINE();
670 #endif
671         } else {
672 #if _DEBUG_ASYNCGEN
673             PRINT_ASYNCGEN_STATUS("Leave with return value:", asyncgen);
674             PRINT_COROUTINE_VALUE("return_value", ret);
675             PRINT_CURRENT_EXCEPTION();
676             PRINT_NEW_LINE();
677 #endif
678         }
679 
680         return ret;
681     }
682 
683 throw_here:
684     // We continue to have exception ownership here.
685 
686     if (unlikely(_Nuitka_Generator_check_throw2(&exception_type, &exception_value, &exception_tb) == false)) {
687         // Exception was released by _Nuitka_Generator_check_throw2 already.
688         return NULL;
689     }
690 
691     if (asyncgen->m_status == status_Running) {
692         PyObject *result = _Nuitka_Asyncgen_send(asyncgen, NULL, false, exception_type, exception_value, exception_tb);
693         return result;
694     } else if (asyncgen->m_status == status_Finished) {
695         RESTORE_ERROR_OCCURRED(exception_type, exception_value, exception_tb);
696         return NULL;
697     } else {
698         if (exception_tb == NULL) {
699             // TODO: Our compiled objects really need a way to store common
700             // stuff in a "shared" part across all instances, and outside of
701             // run time, so we could reuse this.
702             struct Nuitka_FrameObject *frame = MAKE_FUNCTION_FRAME(asyncgen->m_code_object, asyncgen->m_module, 0);
703             exception_tb = MAKE_TRACEBACK(frame, asyncgen->m_code_object->co_firstlineno);
704             Py_DECREF(frame);
705         }
706 
707         RESTORE_ERROR_OCCURRED(exception_type, exception_value, exception_tb);
708 
709 #if _DEBUG_ASYNCGEN
710         PRINT_ASYNCGEN_STATUS("Finishing from exception", asyncgen);
711         PRINT_NEW_LINE();
712 #endif
713 
714         asyncgen->m_status = status_Finished;
715 
716         return NULL;
717     }
718 }
719 
Nuitka_Asyncgen_throw(struct Nuitka_AsyncgenObject * asyncgen,PyObject * args)720 static PyObject *Nuitka_Asyncgen_throw(struct Nuitka_AsyncgenObject *asyncgen, PyObject *args) {
721     CHECK_OBJECT(asyncgen);
722     CHECK_OBJECT_DEEP(args);
723 
724     PyObject *exception_type;
725     PyObject *exception_value = NULL;
726     PyTracebackObject *exception_tb = NULL;
727 
728     // This takes no references, that is for us to do.
729     int res = PyArg_UnpackTuple(args, "throw", 1, 3, &exception_type, &exception_value, &exception_tb);
730 
731     if (unlikely(res == 0)) {
732         return NULL;
733     }
734 
735 #if _DEBUG_ASYNCGEN
736     PRINT_ASYNCGEN_STATUS("Enter", asyncgen);
737     PRINT_EXCEPTION(exception_type, exception_value, exception_tb);
738     PRINT_NEW_LINE();
739 #endif
740 
741     // Handing ownership of exception over, we need not release it ourselves
742     Py_INCREF(exception_type);
743     Py_XINCREF(exception_value);
744     Py_XINCREF(exception_tb);
745 
746     PyObject *result = _Nuitka_Asyncgen_throw2(asyncgen, false, exception_type, exception_value, exception_tb);
747 
748     if (result == NULL) {
749         if (GET_ERROR_OCCURRED() == NULL) {
750             SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
751         }
752     }
753 
754 #if _DEBUG_ASYNCGEN
755     PRINT_ASYNCGEN_STATUS("Leave", asyncgen);
756     PRINT_COROUTINE_VALUE("return value", result);
757     PRINT_CURRENT_EXCEPTION();
758 #endif
759 
760     CHECK_OBJECT(exception_type);
761     CHECK_OBJECT_X(exception_value);
762     CHECK_OBJECT_X(exception_tb);
763 
764     return result;
765 }
766 
Nuitka_Asyncgen_init_hooks(struct Nuitka_AsyncgenObject * asyncgen)767 static int Nuitka_Asyncgen_init_hooks(struct Nuitka_AsyncgenObject *asyncgen) {
768     /* Just do this once per async generator object. */
769     if (asyncgen->m_hooks_init_done) {
770         return 0;
771     }
772     asyncgen->m_hooks_init_done = 1;
773 
774     PyThreadState *tstate = PyThreadState_GET();
775 
776     /* Attach the finalizer if any. */
777     PyObject *finalizer = tstate->async_gen_finalizer;
778     if (finalizer != NULL) {
779         Py_INCREF(finalizer);
780         asyncgen->m_finalizer = finalizer;
781     }
782 
783     /* Call the "firstiter" hook for async generator. */
784     PyObject *firstiter = tstate->async_gen_firstiter;
785     if (firstiter != NULL) {
786         Py_INCREF(firstiter);
787 
788         PyObject *res = CALL_FUNCTION_WITH_SINGLE_ARG(firstiter, (PyObject *)asyncgen);
789 
790         Py_DECREF(firstiter);
791 
792         if (unlikely(res == NULL)) {
793             return 1;
794         }
795 
796         Py_DECREF(res);
797     }
798 
799     return 0;
800 }
801 
802 static PyObject *Nuitka_AsyncgenAsend_New(struct Nuitka_AsyncgenObject *asyncgen, PyObject *sendval);
803 static PyObject *Nuitka_AsyncgenAthrow_New(struct Nuitka_AsyncgenObject *asyncgen, PyObject *args);
804 
Nuitka_Asyncgen_anext(struct Nuitka_AsyncgenObject * asyncgen)805 static PyObject *Nuitka_Asyncgen_anext(struct Nuitka_AsyncgenObject *asyncgen) {
806     CHECK_OBJECT(asyncgen);
807 
808     if (Nuitka_Asyncgen_init_hooks(asyncgen)) {
809         return NULL;
810     }
811 
812 #if _DEBUG_ASYNCGEN
813     PRINT_ASYNCGEN_STATUS("Enter", asyncgen);
814     PRINT_NEW_LINE();
815 #endif
816 
817     PyObject *result = Nuitka_AsyncgenAsend_New(asyncgen, Py_None);
818 
819 #if _DEBUG_ASYNCGEN
820     PRINT_ASYNCGEN_STATUS("Leave", asyncgen);
821     PRINT_COROUTINE_VALUE("result", result);
822     PRINT_NEW_LINE();
823 #endif
824 
825     return result;
826 }
827 
Nuitka_Asyncgen_asend(struct Nuitka_AsyncgenObject * asyncgen,PyObject * value)828 static PyObject *Nuitka_Asyncgen_asend(struct Nuitka_AsyncgenObject *asyncgen, PyObject *value) {
829     CHECK_OBJECT(asyncgen);
830 
831     if (Nuitka_Asyncgen_init_hooks(asyncgen)) {
832         return NULL;
833     }
834 
835     return Nuitka_AsyncgenAsend_New(asyncgen, value);
836 }
837 
Nuitka_Asyncgen_aclose(struct Nuitka_AsyncgenObject * asyncgen)838 static PyObject *Nuitka_Asyncgen_aclose(struct Nuitka_AsyncgenObject *asyncgen) {
839     CHECK_OBJECT(asyncgen);
840 
841     if (Nuitka_Asyncgen_init_hooks(asyncgen)) {
842         return NULL;
843     }
844 
845     return Nuitka_AsyncgenAthrow_New(asyncgen, NULL);
846 }
847 
Nuitka_Asyncgen_athrow(struct Nuitka_AsyncgenObject * asyncgen,PyObject * args)848 static PyObject *Nuitka_Asyncgen_athrow(struct Nuitka_AsyncgenObject *asyncgen, PyObject *args) {
849     CHECK_OBJECT(asyncgen);
850 
851     if (Nuitka_Asyncgen_init_hooks(asyncgen)) {
852         return NULL;
853     }
854 
855     return Nuitka_AsyncgenAthrow_New(asyncgen, args);
856 }
857 
Nuitka_Asyncgen_tp_finalize(struct Nuitka_AsyncgenObject * asyncgen)858 static void Nuitka_Asyncgen_tp_finalize(struct Nuitka_AsyncgenObject *asyncgen) {
859     if (asyncgen->m_status != status_Running) {
860         return;
861     }
862 
863     PyObject *save_exception_type, *save_exception_value;
864     PyTracebackObject *save_exception_tb;
865     FETCH_ERROR_OCCURRED(&save_exception_type, &save_exception_value, &save_exception_tb);
866 
867     bool close_result = _Nuitka_Asyncgen_close(asyncgen);
868 
869     if (unlikely(close_result == false)) {
870         PyErr_WriteUnraisable((PyObject *)asyncgen);
871     }
872 
873     /* Restore the saved exception if any. */
874     RESTORE_ERROR_OCCURRED(save_exception_type, save_exception_value, save_exception_tb);
875 }
876 
877 #define MAX_ASYNCGEN_FREE_LIST_COUNT 100
878 static struct Nuitka_AsyncgenObject *free_list_asyncgens = NULL;
879 static int free_list_asyncgens_count = 0;
880 
881 // TODO: This might have to be finalize actually.
Nuitka_Asyncgen_tp_dealloc(struct Nuitka_AsyncgenObject * asyncgen)882 static void Nuitka_Asyncgen_tp_dealloc(struct Nuitka_AsyncgenObject *asyncgen) {
883 #if _DEBUG_REFCOUNTS
884     count_active_Nuitka_Asyncgen_Type -= 1;
885     count_released_Nuitka_Asyncgen_Type += 1;
886 #endif
887 
888     // Revive temporarily.
889     assert(Py_REFCNT(asyncgen) == 0);
890     Py_REFCNT(asyncgen) = 1;
891 
892     // Save the current exception, if any, we must preserve it.
893     PyObject *save_exception_type, *save_exception_value;
894     PyTracebackObject *save_exception_tb;
895 
896     PyObject *finalizer = asyncgen->m_finalizer;
897     if (finalizer != NULL && asyncgen->m_closed == false) {
898         /* Save the current exception, if any. */
899         FETCH_ERROR_OCCURRED(&save_exception_type, &save_exception_value, &save_exception_tb);
900 
901         PyObject *res = CALL_FUNCTION_WITH_SINGLE_ARG(finalizer, (PyObject *)asyncgen);
902 
903         if (unlikely(res == NULL)) {
904             PyErr_WriteUnraisable((PyObject *)asyncgen);
905         } else {
906             Py_DECREF(res);
907         }
908 
909         RESTORE_ERROR_OCCURRED(save_exception_type, save_exception_value, save_exception_tb);
910         return;
911     }
912 
913     FETCH_ERROR_OCCURRED(&save_exception_type, &save_exception_value, &save_exception_tb);
914 
915     bool close_result = _Nuitka_Asyncgen_close(asyncgen);
916 
917     if (unlikely(close_result == false)) {
918         PyErr_WriteUnraisable((PyObject *)asyncgen);
919     }
920 
921     Nuitka_Asyncgen_release_closure(asyncgen);
922 
923     // Allow for above code to resurrect the coroutine.
924     Py_REFCNT(asyncgen) -= 1;
925     if (Py_REFCNT(asyncgen) >= 1) {
926         return;
927     }
928 
929     if (asyncgen->m_frame) {
930         asyncgen->m_frame->m_frame.f_gen = NULL;
931         Py_DECREF(asyncgen->m_frame);
932         asyncgen->m_frame = NULL;
933     }
934 
935     // Now it is safe to release references and memory for it.
936     Nuitka_GC_UnTrack(asyncgen);
937 
938     Py_XDECREF(asyncgen->m_finalizer);
939 
940     if (asyncgen->m_weakrefs != NULL) {
941         PyObject_ClearWeakRefs((PyObject *)asyncgen);
942         assert(!ERROR_OCCURRED());
943     }
944 
945     Py_DECREF(asyncgen->m_name);
946     Py_DECREF(asyncgen->m_qualname);
947 
948     /* Put the object into freelist or release to GC */
949     releaseToFreeList(free_list_asyncgens, asyncgen, MAX_ASYNCGEN_FREE_LIST_COUNT);
950 
951     RESTORE_ERROR_OCCURRED(save_exception_type, save_exception_value, save_exception_tb);
952 }
953 
Nuitka_Asyncgen_tp_repr(struct Nuitka_AsyncgenObject * asyncgen)954 static PyObject *Nuitka_Asyncgen_tp_repr(struct Nuitka_AsyncgenObject *asyncgen) {
955     CHECK_OBJECT(asyncgen);
956 
957     return PyUnicode_FromFormat("<compiled_async_generator object %s at %p>",
958                                 Nuitka_String_AsString(asyncgen->m_qualname), asyncgen);
959 }
960 
Nuitka_Asyncgen_tp_traverse(struct Nuitka_AsyncgenObject * asyncgen,visitproc visit,void * arg)961 static int Nuitka_Asyncgen_tp_traverse(struct Nuitka_AsyncgenObject *asyncgen, visitproc visit, void *arg) {
962     CHECK_OBJECT(asyncgen);
963 
964     Py_VISIT(asyncgen->m_yieldfrom);
965 
966     for (Py_ssize_t i = 0; i < asyncgen->m_closure_given; i++) {
967         Py_VISIT(asyncgen->m_closure[i]);
968     }
969 
970     Py_VISIT(asyncgen->m_finalizer);
971 
972     return 0;
973 }
974 
975 // TODO: Set "__doc__" automatically for method clones of compiled types from
976 // the documentation of built-in original type.
977 static PyMethodDef Nuitka_Asyncgen_methods[] = {{"asend", (PyCFunction)Nuitka_Asyncgen_asend, METH_O, NULL},
978                                                 {"athrow", (PyCFunction)Nuitka_Asyncgen_athrow, METH_VARARGS, NULL},
979                                                 {"aclose", (PyCFunction)Nuitka_Asyncgen_aclose, METH_NOARGS, NULL},
980                                                 {NULL}};
981 
982 static PyAsyncMethods Nuitka_Asyncgen_as_async = {
983     0,                               /* am_await */
984     PyObject_SelfIter,               /* am_aiter */
985     (unaryfunc)Nuitka_Asyncgen_anext /* am_anext */
986 };
987 
988 // TODO: Set "__doc__" automatically for method clones of compiled types from
989 // the documentation of built-in original type.
990 static PyGetSetDef Nuitka_Asyncgen_getsetlist[] = {
991     {(char *)"__name__", (getter)Nuitka_Asyncgen_get_name, (setter)Nuitka_Asyncgen_set_name, NULL},
992     {(char *)"__qualname__", (getter)Nuitka_Asyncgen_get_qualname, (setter)Nuitka_Asyncgen_set_qualname, NULL},
993     {(char *)"ag_await", (getter)Nuitka_Asyncgen_get_ag_await, (setter)NULL, NULL},
994     {(char *)"ag_code", (getter)Nuitka_Asyncgen_get_code, (setter)Nuitka_Asyncgen_set_code, NULL},
995     {(char *)"ag_frame", (getter)Nuitka_Asyncgen_get_frame, (setter)Nuitka_Asyncgen_set_frame, NULL},
996 
997     {NULL}};
998 
999 static PyMemberDef Nuitka_Asyncgen_members[] = {
1000     {(char *)"ag_running", T_BOOL, offsetof(struct Nuitka_AsyncgenObject, m_running), READONLY},
1001 #if PYTHON_VERSION >= 0x380
1002     {(char *)"ag_running", T_BOOL, offsetof(struct Nuitka_AsyncgenObject, m_running_async), READONLY},
1003 #endif
1004     {NULL}};
1005 
1006 PyTypeObject Nuitka_Asyncgen_Type = {
1007     PyVarObject_HEAD_INIT(NULL, 0) "compiled_async_generator",          /* tp_name */
1008     sizeof(struct Nuitka_AsyncgenObject),                               /* tp_basicsize */
1009     sizeof(struct Nuitka_CellObject *),                                 /* tp_itemsize */
1010     (destructor)Nuitka_Asyncgen_tp_dealloc,                             /* tp_dealloc */
1011     0,                                                                  /* tp_print */
1012     0,                                                                  /* tp_getattr */
1013     0,                                                                  /* tp_setattr */
1014     &Nuitka_Asyncgen_as_async,                                          /* tp_as_async */
1015     (reprfunc)Nuitka_Asyncgen_tp_repr,                                  /* tp_repr */
1016     0,                                                                  /* tp_as_number */
1017     0,                                                                  /* tp_as_sequence */
1018     0,                                                                  /* tp_as_mapping */
1019     0,                                                                  /* tp_hash */
1020     0,                                                                  /* tp_call */
1021     0,                                                                  /* tp_str */
1022     PyObject_GenericGetAttr,                                            /* tp_getattro */
1023     0,                                                                  /* tp_setattro */
1024     0,                                                                  /* tp_as_buffer */
1025     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
1026     0,                                                                  /* tp_doc */
1027     (traverseproc)Nuitka_Asyncgen_tp_traverse,                          /* tp_traverse */
1028     0,                                                                  /* tp_clear */
1029     0,                                                                  /* tp_richcompare */
1030     offsetof(struct Nuitka_AsyncgenObject, m_weakrefs),                 /* tp_weaklistoffset */
1031     0,                                                                  /* tp_iter */
1032     0,                                                                  /* tp_iternext */
1033     Nuitka_Asyncgen_methods,                                            /* tp_methods */
1034     Nuitka_Asyncgen_members,                                            /* tp_members */
1035     Nuitka_Asyncgen_getsetlist,                                         /* tp_getset */
1036     0,                                                                  /* tp_base */
1037     0,                                                                  /* tp_dict */
1038     0,                                                                  /* tp_descr_get */
1039     0,                                                                  /* tp_descr_set */
1040     0,                                                                  /* tp_dictoffset */
1041     0,                                                                  /* tp_init */
1042     0,                                                                  /* tp_alloc */
1043     0,                                                                  /* tp_new */
1044     0,                                                                  /* tp_free */
1045 };
1046 
Nuitka_Asyncgen_New(asyncgen_code code,PyObject * module,PyObject * name,PyObject * qualname,PyCodeObject * code_object,struct Nuitka_CellObject ** closure,Py_ssize_t closure_given,Py_ssize_t heap_storage_size)1047 PyObject *Nuitka_Asyncgen_New(asyncgen_code code, PyObject *module, PyObject *name, PyObject *qualname,
1048                               PyCodeObject *code_object, struct Nuitka_CellObject **closure, Py_ssize_t closure_given,
1049                               Py_ssize_t heap_storage_size) {
1050 #if _DEBUG_REFCOUNTS
1051     count_active_Nuitka_Asyncgen_Type += 1;
1052     count_allocated_Nuitka_Asyncgen_Type += 1;
1053 #endif
1054 
1055     struct Nuitka_AsyncgenObject *result;
1056 
1057     // TODO: Change the var part of the type to 1 maybe
1058     Py_ssize_t full_size = closure_given + (heap_storage_size + sizeof(void *) - 1) / sizeof(void *);
1059 
1060     // Macro to assign result memory from GC or free list.
1061     allocateFromFreeList(free_list_asyncgens, struct Nuitka_AsyncgenObject, Nuitka_Asyncgen_Type, full_size);
1062 
1063     // For quicker access of generator heap.
1064     result->m_heap_storage = &result->m_closure[closure_given];
1065 
1066     result->m_code = (void *)code;
1067 
1068     CHECK_OBJECT(module);
1069     result->m_module = module;
1070 
1071     CHECK_OBJECT(name);
1072     result->m_name = name;
1073     Py_INCREF(name);
1074 
1075     // The "qualname" defaults to NULL for most compact C code.
1076     if (qualname == NULL) {
1077         qualname = name;
1078     }
1079     CHECK_OBJECT(qualname);
1080 
1081     result->m_qualname = qualname;
1082     Py_INCREF(qualname);
1083 
1084     result->m_yieldfrom = NULL;
1085 
1086     memcpy(&result->m_closure[0], closure, closure_given * sizeof(struct Nuitka_CellObject *));
1087     result->m_closure_given = closure_given;
1088 
1089     result->m_weakrefs = NULL;
1090 
1091     result->m_status = status_Unused;
1092     result->m_running = false;
1093     result->m_awaiting = false;
1094 #if PYTHON_VERSION >= 0x380
1095     result->m_running_async = false;
1096 #endif
1097 
1098     result->m_yield_return_index = 0;
1099 
1100     result->m_frame = NULL;
1101     result->m_code_object = code_object;
1102 
1103     result->m_resume_frame = NULL;
1104 
1105     result->m_finalizer = NULL;
1106     result->m_hooks_init_done = false;
1107     result->m_closed = false;
1108 
1109 #if PYTHON_VERSION >= 0x370
1110     result->m_exc_state.exc_type = NULL;
1111     result->m_exc_state.exc_value = NULL;
1112     result->m_exc_state.exc_traceback = NULL;
1113 #endif
1114 
1115     Nuitka_GC_Track(result);
1116     return (PyObject *)result;
1117 }
1118 
1119 struct Nuitka_AsyncgenWrappedValueObject {
1120     /* Python object folklore: */
1121     PyObject_HEAD;
1122 
1123     PyObject *m_value;
1124 };
1125 
1126 static struct Nuitka_AsyncgenWrappedValueObject *free_list_asyncgen_value_wrappers = NULL;
1127 static int free_list_asyncgen_value_wrappers_count = 0;
1128 
Nuitka_AsyncgenValueWrapper_tp_dealloc(struct Nuitka_AsyncgenWrappedValueObject * asyncgen_value_wrapper)1129 static void Nuitka_AsyncgenValueWrapper_tp_dealloc(struct Nuitka_AsyncgenWrappedValueObject *asyncgen_value_wrapper) {
1130 #if _DEBUG_REFCOUNTS
1131     count_active_Nuitka_AsyncgenValueWrapper_Type -= 1;
1132     count_released_Nuitka_AsyncgenValueWrapper_Type += 1;
1133 #endif
1134 
1135     Nuitka_GC_UnTrack((PyObject *)asyncgen_value_wrapper);
1136 
1137     CHECK_OBJECT(asyncgen_value_wrapper->m_value);
1138     Py_DECREF(asyncgen_value_wrapper->m_value);
1139 
1140     /* Put the object into freelist or release to GC */
1141     releaseToFreeList(free_list_asyncgen_value_wrappers, asyncgen_value_wrapper, MAX_ASYNCGEN_FREE_LIST_COUNT);
1142 }
1143 
Nuitka_AsyncgenValueWrapper_tp_traverse(struct Nuitka_AsyncgenWrappedValueObject * asyncgen_value_wrapper,visitproc visit,void * arg)1144 static int Nuitka_AsyncgenValueWrapper_tp_traverse(struct Nuitka_AsyncgenWrappedValueObject *asyncgen_value_wrapper,
1145                                                    visitproc visit, void *arg) {
1146     CHECK_OBJECT(asyncgen_value_wrapper);
1147 
1148     Py_VISIT(asyncgen_value_wrapper->m_value);
1149 
1150     return 0;
1151 }
1152 
1153 static PyTypeObject Nuitka_AsyncgenValueWrapper_Type = {
1154     PyVarObject_HEAD_INIT(NULL, 0) "compiled_async_generator_wrapped_value", /* tp_name */
1155     sizeof(struct Nuitka_AsyncgenWrappedValueObject),                        /* tp_basicsize */
1156     0,                                                                       /* tp_itemsize */
1157     (destructor)Nuitka_AsyncgenValueWrapper_tp_dealloc,                      /* tp_dealloc */
1158     0,                                                                       /* tp_print */
1159     0,                                                                       /* tp_getattr */
1160     0,                                                                       /* tp_setattr */
1161     0,                                                                       /* tp_as_async */
1162     0,                                                                       /* tp_repr */
1163     0,                                                                       /* tp_as_number */
1164     0,                                                                       /* tp_as_sequence */
1165     0,                                                                       /* tp_as_mapping */
1166     0,                                                                       /* tp_hash */
1167     0,                                                                       /* tp_call */
1168     0,                                                                       /* tp_str */
1169     PyObject_GenericGetAttr,                                                 /* tp_getattro */
1170     0,                                                                       /* tp_setattro */
1171     0,                                                                       /* tp_as_buffer */
1172     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,                                 /* tp_flags */
1173     0,                                                                       /* tp_doc */
1174     (traverseproc)Nuitka_AsyncgenValueWrapper_tp_traverse,                   /* tp_traverse */
1175     0,                                                                       /* tp_clear */
1176     0,                                                                       /* tp_richcompare */
1177     0,                                                                       /* tp_weaklistoffset */
1178     0,                                                                       /* tp_iter */
1179     0,                                                                       /* tp_iternext */
1180     0,                                                                       /* tp_methods */
1181     0,                                                                       /* tp_members */
1182     0,                                                                       /* tp_getset */
1183     0,                                                                       /* tp_base */
1184     0,                                                                       /* tp_dict */
1185     0,                                                                       /* tp_descr_get */
1186     0,                                                                       /* tp_descr_set */
1187     0,                                                                       /* tp_dictoffset */
1188     0,                                                                       /* tp_init */
1189     0,                                                                       /* tp_alloc */
1190     0,                                                                       /* tp_new */
1191 };
1192 
1193 // Note: This expects a reference given in value, because that is the
1194 // only way we use it.
Nuitka_AsyncgenValueWrapper_New(PyObject * value)1195 static PyObject *Nuitka_AsyncgenValueWrapper_New(PyObject *value) {
1196     CHECK_OBJECT(value);
1197 
1198 #if _DEBUG_REFCOUNTS
1199     count_active_Nuitka_AsyncgenValueWrapper_Type -= 1;
1200     count_released_Nuitka_AsyncgenValueWrapper_Type += 1;
1201 #endif
1202 
1203     struct Nuitka_AsyncgenWrappedValueObject *result;
1204 
1205     allocateFromFreeListFixed(free_list_asyncgen_value_wrappers, struct Nuitka_AsyncgenWrappedValueObject,
1206                               Nuitka_AsyncgenValueWrapper_Type);
1207 
1208     result->m_value = value;
1209 
1210     Nuitka_GC_Track(result);
1211 
1212     return (PyObject *)result;
1213 }
1214 
1215 #define Nuitka_AsyncgenWrappedValue_CheckExact(o) (Py_TYPE(o) == &Nuitka_AsyncgenValueWrapper_Type)
1216 
1217 typedef enum {
1218     AWAITABLE_STATE_INIT = 0,   /* Has not yet been iterated. */
1219     AWAITABLE_STATE_ITER = 1,   /* Being iterated currently. */
1220     AWAITABLE_STATE_CLOSED = 2, /* Closed, no more. */
1221 } AwaitableState;
1222 
1223 struct Nuitka_AsyncgenAsendObject {
1224     /* Python object folklore: */
1225     PyObject_HEAD;
1226 
1227     struct Nuitka_AsyncgenObject *m_gen;
1228     PyObject *m_sendval;
1229 
1230     AwaitableState m_state;
1231 };
1232 
1233 #if _DEBUG_ASYNCGEN
1234 
_PRINT_ASYNCGENASEND_STATUS(char const * descriptor,char const * context,struct Nuitka_AsyncgenAsendObject * asyncgen_asend)1235 NUITKA_MAY_BE_UNUSED static void _PRINT_ASYNCGENASEND_STATUS(char const *descriptor, char const *context,
1236                                                              struct Nuitka_AsyncgenAsendObject *asyncgen_asend) {
1237     char const *status;
1238 
1239     switch (asyncgen_asend->m_state) {
1240     case AWAITABLE_STATE_INIT:
1241         status = "(init)";
1242         break;
1243     case AWAITABLE_STATE_ITER:
1244         status = "(iter)";
1245         break;
1246     case AWAITABLE_STATE_CLOSED:
1247         status = "(closed)";
1248         break;
1249     default:
1250         status = "(ILLEGAL)";
1251         break;
1252     }
1253 
1254     PRINT_STRING(descriptor);
1255     PRINT_STRING(" : ");
1256     PRINT_STRING(context);
1257     PRINT_STRING(" ");
1258     PRINT_ITEM((PyObject *)asyncgen_asend);
1259     PRINT_STRING(" ");
1260     PRINT_STRING(status);
1261     PRINT_NEW_LINE();
1262 }
1263 
1264 #define PRINT_ASYNCGENASEND_STATUS(context, asyncgen_asend)                                                            \
1265     _PRINT_ASYNCGENASEND_STATUS(__FUNCTION__, context, asyncgen_asend)
1266 
1267 #endif
1268 
1269 /**
1270  * These can be created by byte code loop, and we don't now its internals,
1271  * yet we have to unwrap ourselves too. These could break in future updates,
1272  * and ideally we would have checks to cover those.
1273  */
1274 
1275 struct _PyAsyncGenWrappedValue {
1276     /* Python object folklore: */
1277     PyObject_HEAD;
1278 
1279     PyObject *agw_val;
1280 };
1281 
1282 #define _PyAsyncGenWrappedValue_CheckExact(o) (Py_TYPE(o) == &_PyAsyncGenWrappedValue_Type)
1283 
Nuitka_Asyncgen_unwrap_value(struct Nuitka_AsyncgenObject * asyncgen,PyObject * result)1284 static PyObject *Nuitka_Asyncgen_unwrap_value(struct Nuitka_AsyncgenObject *asyncgen, PyObject *result) {
1285     CHECK_OBJECT(asyncgen);
1286     CHECK_OBJECT_X(result);
1287 
1288     if (result == NULL) {
1289         if (!ERROR_OCCURRED()) {
1290             SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopAsyncIteration);
1291             asyncgen->m_closed = true;
1292         } else if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) || PyErr_ExceptionMatches(PyExc_GeneratorExit)) {
1293             asyncgen->m_closed = true;
1294         }
1295 
1296 #if PYTHON_VERSION >= 0x380
1297         asyncgen->m_running_async = false;
1298 #endif
1299         return NULL;
1300     }
1301 
1302     if (_PyAsyncGenWrappedValue_CheckExact(result)) {
1303         /* async yield */
1304         _PyGen_SetStopIterationValue(((struct _PyAsyncGenWrappedValue *)result)->agw_val);
1305 
1306         Py_DECREF(result);
1307 
1308 #if PYTHON_VERSION >= 0x380
1309         asyncgen->m_running_async = false;
1310 #endif
1311         return NULL;
1312     } else if (Nuitka_AsyncgenWrappedValue_CheckExact(result)) {
1313         /* async yield */
1314         _PyGen_SetStopIterationValue(((struct Nuitka_AsyncgenWrappedValueObject *)result)->m_value);
1315 
1316         Py_DECREF(result);
1317 
1318 #if PYTHON_VERSION >= 0x380
1319         asyncgen->m_running_async = false;
1320 #endif
1321         return NULL;
1322     }
1323 
1324     return result;
1325 }
1326 
1327 static struct Nuitka_AsyncgenAsendObject *free_list_asyncgen_asends = NULL;
1328 static int free_list_asyncgen_asends_count = 0;
1329 
Nuitka_AsyncgenAsend_tp_dealloc(struct Nuitka_AsyncgenAsendObject * asyncgen_asend)1330 static void Nuitka_AsyncgenAsend_tp_dealloc(struct Nuitka_AsyncgenAsendObject *asyncgen_asend) {
1331 #if _DEBUG_REFCOUNTS
1332     count_active_Nuitka_AsyncgenAsend_Type -= 1;
1333     count_released_Nuitka_AsyncgenAsend_Type += 1;
1334 #endif
1335 
1336     Nuitka_GC_UnTrack(asyncgen_asend);
1337 
1338     CHECK_OBJECT(asyncgen_asend->m_gen);
1339     Py_DECREF(asyncgen_asend->m_gen);
1340 
1341     CHECK_OBJECT(asyncgen_asend->m_sendval);
1342     Py_DECREF(asyncgen_asend->m_sendval);
1343 
1344     releaseToFreeList(free_list_asyncgen_asends, asyncgen_asend, MAX_ASYNCGEN_FREE_LIST_COUNT);
1345 }
1346 
Nuitka_AsyncgenAsend_tp_traverse(struct Nuitka_AsyncgenAsendObject * asyncgen_asend,visitproc visit,void * arg)1347 static int Nuitka_AsyncgenAsend_tp_traverse(struct Nuitka_AsyncgenAsendObject *asyncgen_asend, visitproc visit,
1348                                             void *arg) {
1349     CHECK_OBJECT(asyncgen_asend);
1350 
1351     CHECK_OBJECT(asyncgen_asend->m_gen);
1352     CHECK_OBJECT(asyncgen_asend->m_sendval);
1353 
1354     Py_VISIT(asyncgen_asend->m_gen);
1355     Py_VISIT(asyncgen_asend->m_sendval);
1356 
1357     return 0;
1358 }
1359 
Nuitka_AsyncgenAsend_send(struct Nuitka_AsyncgenAsendObject * asyncgen_asend,PyObject * arg)1360 static PyObject *Nuitka_AsyncgenAsend_send(struct Nuitka_AsyncgenAsendObject *asyncgen_asend, PyObject *arg) {
1361 #if _DEBUG_ASYNCGEN
1362     PRINT_ASYNCGENASEND_STATUS("Enter", asyncgen_asend);
1363     PRINT_COROUTINE_VALUE("arg", arg);
1364     PRINT_NEW_LINE();
1365 #endif
1366 
1367     if (asyncgen_asend->m_state == AWAITABLE_STATE_CLOSED) {
1368 #if PYTHON_VERSION < 0x390
1369         SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
1370 #else
1371         SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "cannot reuse already awaited __anext__()/asend()");
1372 #endif
1373 
1374 #if _DEBUG_ASYNCGEN
1375         PRINT_ASYNCGENASEND_STATUS("Leave", asyncgen_asend);
1376         PRINT_STRING("Closed -> StopIteration\n");
1377         PRINT_CURRENT_EXCEPTION();
1378         PRINT_NEW_LINE();
1379 #endif
1380 
1381         return NULL;
1382     } else if (asyncgen_asend->m_state == AWAITABLE_STATE_INIT) {
1383 #if PYTHON_VERSION >= 0x380
1384         if (asyncgen_asend->m_gen->m_running_async) {
1385             SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "anext(): asynchronous generator is already running");
1386             return NULL;
1387         }
1388 #endif
1389         if (arg == NULL || arg == Py_None) {
1390             arg = asyncgen_asend->m_sendval;
1391         }
1392 
1393         asyncgen_asend->m_state = AWAITABLE_STATE_ITER;
1394 
1395 #if _DEBUG_ASYNCGEN
1396         PRINT_STRING("Init -> begin iteration\n");
1397         PRINT_COROUTINE_VALUE("computed arg from sendval", arg);
1398         PRINT_NEW_LINE();
1399 #endif
1400     }
1401 
1402 #if PYTHON_VERSION >= 0x380
1403     asyncgen_asend->m_gen->m_running_async = true;
1404 #endif
1405     // TODO: Who releases arg.
1406     // Py_INCREF(arg);
1407 
1408     PyObject *result = _Nuitka_Asyncgen_send(asyncgen_asend->m_gen, arg, false, NULL, NULL, NULL);
1409     result = Nuitka_Asyncgen_unwrap_value(asyncgen_asend->m_gen, result);
1410 
1411     if (result == NULL) {
1412         asyncgen_asend->m_state = AWAITABLE_STATE_CLOSED;
1413     }
1414 
1415 #if _DEBUG_ASYNCGEN
1416     PRINT_ASYNCGENASEND_STATUS("Leave", asyncgen_asend);
1417     PRINT_COROUTINE_VALUE("result", result);
1418     PRINT_NEW_LINE();
1419 #endif
1420 
1421     return result;
1422 }
1423 
Nuitka_AsyncgenAsend_tp_iternext(struct Nuitka_AsyncgenAsendObject * asyncgen_asend)1424 static PyObject *Nuitka_AsyncgenAsend_tp_iternext(struct Nuitka_AsyncgenAsendObject *asyncgen_asend) {
1425 #if _DEBUG_ASYNCGEN
1426     PRINT_ASYNCGENASEND_STATUS("Enter", asyncgen_asend);
1427     PRINT_STRING("Deferring to Nuitka_AsyncgenAsend_send(Py_None)\n");
1428     PRINT_NEW_LINE();
1429 #endif
1430 
1431     PyObject *result = Nuitka_AsyncgenAsend_send(asyncgen_asend, Py_None);
1432 
1433 #if _DEBUG_ASYNCGEN
1434     PRINT_ASYNCGENASEND_STATUS("Leave", asyncgen_asend);
1435     PRINT_COROUTINE_VALUE("result", result);
1436     PRINT_NEW_LINE();
1437 #endif
1438 
1439     return result;
1440 }
1441 
Nuitka_AsyncgenAsend_throw(struct Nuitka_AsyncgenAsendObject * asyncgen_asend,PyObject * args)1442 static PyObject *Nuitka_AsyncgenAsend_throw(struct Nuitka_AsyncgenAsendObject *asyncgen_asend, PyObject *args) {
1443 #if _DEBUG_ASYNCGEN
1444     PRINT_ASYNCGENASEND_STATUS("Enter", asyncgen_asend);
1445     PRINT_STRING("Nuitka_AsyncgenAsend_throw: args:");
1446     PRINT_ITEM(args);
1447     PRINT_NEW_LINE();
1448     PRINT_STRING("Nuitka_AsyncgenAsend_throw: On entry: ");
1449     PRINT_CURRENT_EXCEPTION();
1450 #endif
1451 
1452     if (asyncgen_asend->m_state == AWAITABLE_STATE_CLOSED) {
1453         SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
1454         return NULL;
1455     }
1456 
1457     PyObject *result = Nuitka_Asyncgen_throw(asyncgen_asend->m_gen, args);
1458 
1459 #if _DEBUG_ASYNCGEN
1460     PRINT_STRING("Nuitka_AsyncgenAsend_throw: Async throw result:");
1461     PRINT_ITEM(result);
1462     PRINT_STRING(" exception: ");
1463     PRINT_CURRENT_EXCEPTION();
1464 #endif
1465 
1466     result = Nuitka_Asyncgen_unwrap_value(asyncgen_asend->m_gen, result);
1467 
1468     if (result == NULL) {
1469         asyncgen_asend->m_state = AWAITABLE_STATE_CLOSED;
1470     }
1471 
1472 #if _DEBUG_ASYNCGEN
1473     PRINT_STRING("Nuitka_AsyncgenAsend_throw: Leave with result: ");
1474     PRINT_ITEM(result);
1475     PRINT_NEW_LINE();
1476     PRINT_STRING("Nuitka_AsyncgenAsend_throw: Leave with exception: ");
1477     PRINT_CURRENT_EXCEPTION();
1478     PRINT_STRING("Nuitka_AsyncgenAsend_throw: Leave with exception: ");
1479     PRINT_PUBLISHED_EXCEPTION();
1480     PRINT_NEW_LINE();
1481 #endif
1482     CHECK_OBJECT_DEEP(args);
1483 
1484     return result;
1485 }
1486 
_Nuitka_AsyncgenAsend_throw2(struct Nuitka_AsyncgenAsendObject * asyncgen_asend,PyObject * exception_type,PyObject * exception_value,PyTracebackObject * exception_tb)1487 static PyObject *_Nuitka_AsyncgenAsend_throw2(struct Nuitka_AsyncgenAsendObject *asyncgen_asend,
1488                                               PyObject *exception_type, PyObject *exception_value,
1489                                               PyTracebackObject *exception_tb) {
1490 #if _DEBUG_ASYNCGEN
1491     PRINT_ASYNCGENASEND_STATUS("Enter", asyncgen_asend);
1492     PRINT_EXCEPTION(exception_type, exception_value, exception_tb);
1493     PRINT_CURRENT_EXCEPTION();
1494     PRINT_NEW_LINE();
1495 #endif
1496 
1497     if (asyncgen_asend->m_state == AWAITABLE_STATE_CLOSED) {
1498         SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
1499         return NULL;
1500     }
1501 
1502     PyObject *result =
1503         _Nuitka_Asyncgen_throw2(asyncgen_asend->m_gen, false, exception_type, exception_value, exception_tb);
1504 
1505     // TODO: This might not be all that necessary as this is not directly outside facing.
1506     if (result == NULL) {
1507         if (GET_ERROR_OCCURRED() == NULL) {
1508             SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
1509         }
1510     }
1511 
1512 #if _DEBUG_ASYNCGEN
1513     PRINT_ASYNCGENASEND_STATUS("Got result", asyncgen_asend);
1514     PRINT_COROUTINE_VALUE("result", result);
1515     PRINT_CURRENT_EXCEPTION();
1516 #endif
1517 
1518     result = Nuitka_Asyncgen_unwrap_value(asyncgen_asend->m_gen, result);
1519 
1520 #if _DEBUG_ASYNCGEN
1521     PRINT_COROUTINE_VALUE("unwrapped", result);
1522     PRINT_NEW_LINE();
1523 #endif
1524 
1525     if (result == NULL) {
1526         asyncgen_asend->m_state = AWAITABLE_STATE_CLOSED;
1527     }
1528 
1529 #if _DEBUG_ASYNCGEN
1530     PRINT_ASYNCGENASEND_STATUS("Leave", asyncgen_asend);
1531     PRINT_COROUTINE_VALUE("result", result);
1532     PRINT_CURRENT_EXCEPTION();
1533     PRINT_NEW_LINE();
1534 #endif
1535     return result;
1536 }
1537 
Nuitka_AsyncgenAsend_close(struct Nuitka_AsyncgenAsendObject * asyncgen_asend,PyObject * args)1538 static PyObject *Nuitka_AsyncgenAsend_close(struct Nuitka_AsyncgenAsendObject *asyncgen_asend, PyObject *args) {
1539     asyncgen_asend->m_state = AWAITABLE_STATE_CLOSED;
1540 
1541     Py_INCREF(Py_None);
1542     return Py_None;
1543 }
1544 
Nuitka_AsyncgenAsend_tp_repr(struct Nuitka_AsyncgenAsendObject * asyncgen_asend)1545 static PyObject *Nuitka_AsyncgenAsend_tp_repr(struct Nuitka_AsyncgenAsendObject *asyncgen_asend) {
1546     return PyUnicode_FromFormat("<compiled_async_generator_asend of %s at %p>",
1547                                 Nuitka_String_AsString(asyncgen_asend->m_gen->m_qualname), asyncgen_asend);
1548 }
1549 
1550 static PyMethodDef Nuitka_AsyncgenAsend_methods[] = {
1551     {"send", (PyCFunction)Nuitka_AsyncgenAsend_send, METH_O, NULL},
1552     {"throw", (PyCFunction)Nuitka_AsyncgenAsend_throw, METH_VARARGS, NULL},
1553     {"close", (PyCFunction)Nuitka_AsyncgenAsend_close, METH_NOARGS, NULL},
1554     {NULL}};
1555 
1556 static PyAsyncMethods Nuitka_AsyncgenAsend_as_async = {
1557     PyObject_SelfIter, /* am_await */
1558     0,                 /* am_aiter */
1559     0                  /* am_anext */
1560 };
1561 
1562 static PyTypeObject Nuitka_AsyncgenAsend_Type = {
1563     PyVarObject_HEAD_INIT(NULL, 0) "compiled_async_generator_asend", /* tp_name */
1564     sizeof(struct Nuitka_AsyncgenAsendObject),                       /* tp_basicsize */
1565     0,                                                               /* tp_itemsize */
1566     (destructor)Nuitka_AsyncgenAsend_tp_dealloc,                     /* tp_dealloc */
1567     0,                                                               /* tp_print */
1568     0,                                                               /* tp_getattr */
1569     0,                                                               /* tp_setattr */
1570     &Nuitka_AsyncgenAsend_as_async,                                  /* tp_as_async */
1571     (reprfunc)Nuitka_AsyncgenAsend_tp_repr,                          /* tp_repr */
1572     0,                                                               /* tp_as_number */
1573     0,                                                               /* tp_as_sequence */
1574     0,                                                               /* tp_as_mapping */
1575     0,                                                               /* tp_hash */
1576     0,                                                               /* tp_call */
1577     0,                                                               /* tp_str */
1578     PyObject_GenericGetAttr,                                         /* tp_getattro */
1579     0,                                                               /* tp_setattro */
1580     0,                                                               /* tp_as_buffer */
1581     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,                         /* tp_flags */
1582     0,                                                               /* tp_doc */
1583     (traverseproc)Nuitka_AsyncgenAsend_tp_traverse,                  /* tp_traverse */
1584     0,                                                               /* tp_clear */
1585     0,                                                               /* tp_richcompare */
1586     0,                                                               /* tp_weaklistoffset */
1587     PyObject_SelfIter,                                               /* tp_iter */
1588     (iternextfunc)Nuitka_AsyncgenAsend_tp_iternext,                  /* tp_iternext */
1589     Nuitka_AsyncgenAsend_methods,                                    /* tp_methods */
1590     0,                                                               /* tp_members */
1591     0,                                                               /* tp_getset */
1592     0,                                                               /* tp_base */
1593     0,                                                               /* tp_dict */
1594     0,                                                               /* tp_descr_get */
1595     0,                                                               /* tp_descr_set */
1596     0,                                                               /* tp_dictoffset */
1597     0,                                                               /* tp_init */
1598     0,                                                               /* tp_alloc */
1599     0,                                                               /* tp_new */
1600 };
1601 
Nuitka_AsyncgenAsend_Check(PyObject * object)1602 static bool Nuitka_AsyncgenAsend_Check(PyObject *object) { return Py_TYPE(object) == &Nuitka_AsyncgenAsend_Type; }
1603 
Nuitka_AsyncgenAsend_New(struct Nuitka_AsyncgenObject * asyncgen,PyObject * send_value)1604 static PyObject *Nuitka_AsyncgenAsend_New(struct Nuitka_AsyncgenObject *asyncgen, PyObject *send_value) {
1605     CHECK_OBJECT(asyncgen);
1606     CHECK_OBJECT(send_value);
1607 
1608 #if _DEBUG_REFCOUNTS
1609     count_active_Nuitka_AsyncgenAsend_Type += 1;
1610     count_allocated_Nuitka_AsyncgenAsend_Type += 1;
1611 #endif
1612 
1613     struct Nuitka_AsyncgenAsendObject *result;
1614 
1615     allocateFromFreeListFixed(free_list_asyncgen_asends, struct Nuitka_AsyncgenAsendObject, Nuitka_AsyncgenAsend_Type);
1616 
1617     Py_INCREF(asyncgen);
1618     result->m_gen = asyncgen;
1619 
1620     Py_INCREF(send_value);
1621     result->m_sendval = send_value;
1622 
1623     result->m_state = AWAITABLE_STATE_INIT;
1624 
1625     Nuitka_GC_Track(result);
1626     return (PyObject *)result;
1627 }
1628 
1629 struct Nuitka_AsyncgenAthrowObject {
1630     /* Python object folklore: */
1631     PyObject_HEAD;
1632 
1633     // The asyncgen we are working for.
1634     struct Nuitka_AsyncgenObject *m_gen;
1635     // Arguments, NULL in case of close, otherwise throw arguments.
1636     PyObject *m_args;
1637 
1638     AwaitableState m_state;
1639 };
1640 
1641 #if _DEBUG_ASYNCGEN
1642 
_PRINT_ASYNCGENATHROW_STATUS(char const * descriptor,char const * context,struct Nuitka_AsyncgenAthrowObject * asyncgen_athrow)1643 NUITKA_MAY_BE_UNUSED static void _PRINT_ASYNCGENATHROW_STATUS(char const *descriptor, char const *context,
1644                                                               struct Nuitka_AsyncgenAthrowObject *asyncgen_athrow) {
1645     char const *status;
1646 
1647     switch (asyncgen_athrow->m_state) {
1648     case AWAITABLE_STATE_INIT:
1649         status = "(init)";
1650         break;
1651     case AWAITABLE_STATE_ITER:
1652         status = "(iter)";
1653         break;
1654     case AWAITABLE_STATE_CLOSED:
1655         status = "(closed)";
1656         break;
1657     default:
1658         status = "(ILLEGAL)";
1659         break;
1660     }
1661 
1662     PRINT_STRING(descriptor);
1663     PRINT_STRING(" : ");
1664     PRINT_STRING(context);
1665     PRINT_STRING(" ");
1666     PRINT_ITEM((PyObject *)asyncgen_athrow);
1667     PRINT_STRING(" ");
1668     PRINT_STRING(status);
1669     PRINT_NEW_LINE();
1670 }
1671 
1672 #define PRINT_ASYNCGENATHROW_STATUS(context, coroutine)                                                                \
1673     _PRINT_ASYNCGENATHROW_STATUS(__FUNCTION__, context, asyncgen_athrow)
1674 
1675 #endif
1676 
1677 static struct Nuitka_AsyncgenAthrowObject *free_list_asyncgen_athrows = NULL;
1678 static int free_list_asyncgen_athrows_count = 0;
1679 
Nuitka_AsyncgenAthrow_dealloc(struct Nuitka_AsyncgenAthrowObject * asyncgen_athrow)1680 static void Nuitka_AsyncgenAthrow_dealloc(struct Nuitka_AsyncgenAthrowObject *asyncgen_athrow) {
1681 #if _DEBUG_REFCOUNTS
1682     count_active_Nuitka_AIterWrapper_Type -= 1;
1683     count_released_Nuitka_AIterWrapper_Type += 1;
1684 #endif
1685 
1686     Nuitka_GC_UnTrack(asyncgen_athrow);
1687 
1688     CHECK_OBJECT(asyncgen_athrow->m_gen);
1689     Py_DECREF(asyncgen_athrow->m_gen);
1690 
1691     CHECK_OBJECT_X(asyncgen_athrow->m_args);
1692     Py_XDECREF(asyncgen_athrow->m_args);
1693 
1694     /* Put the object into freelist or release to GC */
1695     releaseToFreeList(free_list_asyncgen_athrows, asyncgen_athrow, MAX_ASYNCGEN_FREE_LIST_COUNT);
1696 }
1697 
Nuitka_AsyncgenAthrow_traverse(struct Nuitka_AsyncgenAthrowObject * asyncgen_athrow,visitproc visit,void * arg)1698 static int Nuitka_AsyncgenAthrow_traverse(struct Nuitka_AsyncgenAthrowObject *asyncgen_athrow, visitproc visit,
1699                                           void *arg) {
1700     Py_VISIT(asyncgen_athrow->m_gen);
1701     Py_VISIT(asyncgen_athrow->m_args);
1702 
1703     return 0;
1704 }
1705 
Nuitka_AsyncgenAthrow_send(struct Nuitka_AsyncgenAthrowObject * asyncgen_athrow,PyObject * arg)1706 static PyObject *Nuitka_AsyncgenAthrow_send(struct Nuitka_AsyncgenAthrowObject *asyncgen_athrow, PyObject *arg) {
1707 #if _DEBUG_ASYNCGEN
1708     PRINT_ASYNCGENATHROW_STATUS("Enter", asyncgen_athrow);
1709     PRINT_COROUTINE_VALUE("arg", arg);
1710     PRINT_NEW_LINE();
1711 #endif
1712 
1713     struct Nuitka_AsyncgenObject *asyncgen = asyncgen_athrow->m_gen;
1714 
1715     // Closing twice is not allowed with 3.9 or higher.
1716     if (asyncgen_athrow->m_state == AWAITABLE_STATE_CLOSED) {
1717 #if PYTHON_VERSION < 0x390
1718         SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
1719 #else
1720         SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "cannot reuse already awaited aclose()/athrow()");
1721 #endif
1722 
1723         return NULL;
1724     }
1725 
1726     // If finished, just report StopIteration.
1727     if (asyncgen->m_status == status_Finished) {
1728         SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
1729         return NULL;
1730     }
1731 
1732     PyObject *retval;
1733 
1734     if (asyncgen_athrow->m_state == AWAITABLE_STATE_INIT) {
1735 #if PYTHON_VERSION >= 0x380
1736         if (asyncgen_athrow->m_gen->m_running_async) {
1737             if (asyncgen_athrow->m_args == NULL) {
1738                 SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError,
1739                                                 "aclose(): asynchronous generator is already running");
1740             } else {
1741                 SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError,
1742                                                 "athrow(): asynchronous generator is already running");
1743             }
1744             return NULL;
1745         }
1746 #endif
1747 
1748         // Can also close only once.
1749         if (asyncgen->m_closed) {
1750 #if PYTHON_VERSION >= 0x380
1751             asyncgen_athrow->m_state = AWAITABLE_STATE_CLOSED;
1752             SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopAsyncIteration);
1753 #else
1754             SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
1755 #endif
1756             return NULL;
1757         }
1758 
1759         // Starting accepts only "None" as input value.
1760         if (arg != Py_None) {
1761             SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError,
1762                                             "can't send non-None value to a just-started coroutine");
1763 
1764             return NULL;
1765         }
1766 
1767 #if PYTHON_VERSION >= 0x380
1768         asyncgen_athrow->m_gen->m_running_async = true;
1769 #endif
1770         asyncgen_athrow->m_state = AWAITABLE_STATE_ITER;
1771 
1772         if (asyncgen_athrow->m_args == NULL) {
1773             asyncgen->m_closed = true;
1774 
1775             Py_INCREF(PyExc_GeneratorExit);
1776             retval =
1777                 _Nuitka_Asyncgen_throw2(asyncgen, 1, /* Do not close generator when PyExc_GeneratorExit is passed */
1778                                         PyExc_GeneratorExit, NULL, NULL);
1779 
1780             if (retval) {
1781                 if (_PyAsyncGenWrappedValue_CheckExact(retval) || Nuitka_AsyncgenWrappedValue_CheckExact(retval)) {
1782 #if PYTHON_VERSION >= 0x380
1783                     asyncgen_athrow->m_gen->m_running_async = false;
1784 #endif
1785 
1786                     Py_DECREF(retval);
1787 
1788                     SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "async generator ignored GeneratorExit");
1789 
1790                     return NULL;
1791                 }
1792             }
1793         } else {
1794             PyObject *exception_type;
1795             PyObject *exception_value = NULL;
1796             PyTracebackObject *exception_tb = NULL;
1797 
1798             if (unlikely(!PyArg_UnpackTuple(asyncgen_athrow->m_args, "athrow", 1, 3, &exception_type, &exception_value,
1799                                             &exception_tb))) {
1800                 return NULL;
1801             }
1802 
1803             // Handing ownership of exception over, we need not release it ourselves
1804             Py_INCREF(exception_type);
1805             Py_XINCREF(exception_value);
1806             Py_XINCREF(exception_tb);
1807 
1808             retval =
1809                 _Nuitka_Asyncgen_throw2(asyncgen, 0, /* Do not close generator when PyExc_GeneratorExit is passed */
1810                                         exception_type, exception_value, exception_tb);
1811 
1812             retval = Nuitka_Asyncgen_unwrap_value(asyncgen, retval);
1813         }
1814 
1815         if (retval == NULL) {
1816             goto check_error;
1817         }
1818 
1819         return retval;
1820     }
1821 
1822     assert(asyncgen_athrow->m_state == AWAITABLE_STATE_ITER);
1823 
1824     retval = _Nuitka_Asyncgen_send(asyncgen, arg, false, NULL, NULL, NULL);
1825 
1826     if (asyncgen_athrow->m_args) {
1827         return Nuitka_Asyncgen_unwrap_value(asyncgen, retval);
1828     } else {
1829         /* We are here to close if no args. */
1830         if (retval) {
1831             if (_PyAsyncGenWrappedValue_CheckExact(retval) || Nuitka_AsyncgenWrappedValue_CheckExact(retval)) {
1832 #if PYTHON_VERSION >= 0x380
1833                 asyncgen_athrow->m_gen->m_running_async = false;
1834 #endif
1835                 Py_DECREF(retval);
1836 
1837                 SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "async generator ignored GeneratorExit");
1838 
1839                 return NULL;
1840             }
1841 
1842             return retval;
1843         }
1844     }
1845 
1846 check_error:
1847 #if PYTHON_VERSION >= 0x380
1848     asyncgen_athrow->m_gen->m_running_async = false;
1849 #endif
1850 
1851     if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) {
1852         asyncgen_athrow->m_state = AWAITABLE_STATE_CLOSED;
1853 
1854         if (asyncgen_athrow->m_args == NULL) {
1855             CLEAR_ERROR_OCCURRED();
1856             SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
1857         }
1858     } else if (PyErr_ExceptionMatches(PyExc_GeneratorExit)) {
1859         asyncgen_athrow->m_state = AWAITABLE_STATE_CLOSED;
1860 
1861 #if PYTHON_VERSION >= 0x380
1862         if (asyncgen_athrow->m_args == NULL) {
1863 #endif
1864             CLEAR_ERROR_OCCURRED();
1865             SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
1866 #if PYTHON_VERSION >= 0x380
1867         }
1868 #endif
1869     }
1870 
1871     return NULL;
1872 }
1873 
Nuitka_AsyncgenAthrow_throw(struct Nuitka_AsyncgenAthrowObject * asyncgen_athrow,PyObject * args)1874 static PyObject *Nuitka_AsyncgenAthrow_throw(struct Nuitka_AsyncgenAthrowObject *asyncgen_athrow, PyObject *args) {
1875 #if _DEBUG_ASYNCGEN
1876     PRINT_ASYNCGENATHROW_STATUS("Enter", asyncgen_athrow);
1877     PRINT_COROUTINE_VALUE("args", args);
1878     PRINT_NEW_LINE();
1879 #endif
1880 
1881     PyObject *retval;
1882 
1883 #if PYTHON_VERSION < 0x375
1884     if (asyncgen_athrow->m_state == AWAITABLE_STATE_INIT) {
1885         SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "can't send non-None value to a just-started coroutine");
1886 
1887         return NULL;
1888     }
1889 #endif
1890 
1891     if (asyncgen_athrow->m_state == AWAITABLE_STATE_CLOSED) {
1892 #if PYTHON_VERSION < 0x390
1893         SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
1894 #else
1895         SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "cannot reuse already awaited aclose()/athrow()");
1896 #endif
1897 
1898         return NULL;
1899     }
1900 
1901     retval = Nuitka_Asyncgen_throw(asyncgen_athrow->m_gen, args);
1902 
1903     if (asyncgen_athrow->m_args) {
1904         return Nuitka_Asyncgen_unwrap_value(asyncgen_athrow->m_gen, retval);
1905     } else {
1906         if (retval != NULL) {
1907             if (_PyAsyncGenWrappedValue_CheckExact(retval) || Nuitka_AsyncgenWrappedValue_CheckExact(retval)) {
1908 #if PYTHON_VERSION >= 0x380
1909                 asyncgen_athrow->m_gen->m_running_async = false;
1910 #endif
1911                 Py_DECREF(retval);
1912 
1913                 SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "async generator ignored GeneratorExit");
1914 
1915                 return NULL;
1916             }
1917         }
1918 
1919 #if PYTHON_VERSION >= 0x390
1920         if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) || PyErr_ExceptionMatches(PyExc_GeneratorExit)) {
1921             SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
1922         }
1923 #endif
1924 
1925         return retval;
1926     }
1927 }
1928 
Nuitka_AsyncgenAthrow_tp_iternext(struct Nuitka_AsyncgenAthrowObject * asyncgen_athrow)1929 static PyObject *Nuitka_AsyncgenAthrow_tp_iternext(struct Nuitka_AsyncgenAthrowObject *asyncgen_athrow) {
1930     return Nuitka_AsyncgenAthrow_send(asyncgen_athrow, Py_None);
1931 }
1932 
Nuitka_AsyncgenAthrow_close(struct Nuitka_AsyncgenAthrowObject * asyncgen_athrow)1933 static PyObject *Nuitka_AsyncgenAthrow_close(struct Nuitka_AsyncgenAthrowObject *asyncgen_athrow) {
1934     asyncgen_athrow->m_state = AWAITABLE_STATE_CLOSED;
1935 
1936     Py_INCREF(Py_None);
1937     return Py_None;
1938 }
1939 
1940 static PyMethodDef Nuitka_AsyncgenAthrow_methods[] = {
1941     {"send", (PyCFunction)Nuitka_AsyncgenAthrow_send, METH_O, NULL},
1942     {"throw", (PyCFunction)Nuitka_AsyncgenAthrow_throw, METH_VARARGS, NULL},
1943     {"close", (PyCFunction)Nuitka_AsyncgenAthrow_close, METH_NOARGS, NULL},
1944     {NULL}};
1945 
1946 static PyAsyncMethods Nuitka_AsyncgenAthrow_as_async = {
1947     PyObject_SelfIter, /* am_await */
1948     0,                 /* am_aiter */
1949     0                  /* am_anext */
1950 };
1951 
1952 static PyTypeObject Nuitka_AsyncgenAthrow_Type = {
1953     PyVarObject_HEAD_INIT(NULL, 0) "compiled_async_generator_athrow", /* tp_name */
1954     sizeof(struct Nuitka_AsyncgenAthrowObject),                       /* tp_basicsize */
1955     0,                                                                /* tp_itemsize */
1956     (destructor)Nuitka_AsyncgenAthrow_dealloc,                        /* tp_dealloc */
1957     0,                                                                /* tp_print */
1958     0,                                                                /* tp_getattr */
1959     0,                                                                /* tp_setattr */
1960     &Nuitka_AsyncgenAthrow_as_async,                                  /* tp_as_async */
1961     0,                                                                /* tp_repr */
1962     0,                                                                /* tp_as_number */
1963     0,                                                                /* tp_as_sequence */
1964     0,                                                                /* tp_as_mapping */
1965     0,                                                                /* tp_hash */
1966     0,                                                                /* tp_call */
1967     0,                                                                /* tp_str */
1968     PyObject_GenericGetAttr,                                          /* tp_getattro */
1969     0,                                                                /* tp_setattro */
1970     0,                                                                /* tp_as_buffer */
1971     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,                          /* tp_flags */
1972     0,                                                                /* tp_doc */
1973     (traverseproc)Nuitka_AsyncgenAthrow_traverse,                     /* tp_traverse */
1974     0,                                                                /* tp_clear */
1975     0,                                                                /* tp_richcompare */
1976     0,                                                                /* tp_weaklistoffset */
1977     PyObject_SelfIter,                                                /* tp_iter */
1978     (iternextfunc)Nuitka_AsyncgenAthrow_tp_iternext,                  /* tp_iternext */
1979     Nuitka_AsyncgenAthrow_methods,                                    /* tp_methods */
1980     0,                                                                /* tp_members */
1981     0,                                                                /* tp_getset */
1982     0,                                                                /* tp_base */
1983     0,                                                                /* tp_dict */
1984     0,                                                                /* tp_descr_get */
1985     0,                                                                /* tp_descr_set */
1986     0,                                                                /* tp_dictoffset */
1987     0,                                                                /* tp_init */
1988     0,                                                                /* tp_alloc */
1989     0,                                                                /* tp_new */
1990     0,                                                                /* tp_free */
1991     0,                                                                /* tp_is_gc */
1992     0,                                                                /* tp_bases */
1993     0,                                                                /* tp_mro */
1994     0,                                                                /* tp_cache */
1995     0,                                                                /* tp_subclasses */
1996     0,                                                                /* tp_weaklist */
1997     0,                                                                /* tp_del */
1998     0,                                                                /* tp_version_tag */
1999     (destructor)Nuitka_Asyncgen_tp_finalize,                          /* tp_finalize */
2000 };
2001 
Nuitka_AsyncgenAthrow_New(struct Nuitka_AsyncgenObject * asyncgen,PyObject * args)2002 static PyObject *Nuitka_AsyncgenAthrow_New(struct Nuitka_AsyncgenObject *asyncgen, PyObject *args) {
2003     CHECK_OBJECT(asyncgen);
2004     CHECK_OBJECT_X(args);
2005 
2006 #if _DEBUG_REFCOUNTS
2007     count_active_Nuitka_AsyncgenAthrow_Type += 1;
2008     count_allocated_Nuitka_AsyncgenAthrow_Type += 1;
2009 #endif
2010 
2011     struct Nuitka_AsyncgenAthrowObject *result;
2012 
2013     allocateFromFreeListFixed(free_list_asyncgen_athrows, struct Nuitka_AsyncgenAthrowObject,
2014                               Nuitka_AsyncgenAthrow_Type);
2015 
2016     Py_INCREF(asyncgen);
2017     result->m_gen = asyncgen;
2018 
2019     Py_XINCREF(args);
2020     result->m_args = args;
2021 
2022     result->m_state = AWAITABLE_STATE_INIT;
2023 
2024     Nuitka_GC_Track(result);
2025     return (PyObject *)result;
2026 }
2027 
_initCompiledAsyncgenTypes(void)2028 static void _initCompiledAsyncgenTypes(void) {
2029     PyType_Ready(&Nuitka_Asyncgen_Type);
2030     PyType_Ready(&Nuitka_AsyncgenAsend_Type);
2031     PyType_Ready(&Nuitka_AsyncgenAthrow_Type);
2032     PyType_Ready(&Nuitka_AsyncgenValueWrapper_Type);
2033 }
2034