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 Coroutines.
19  *
20  * Unlike in CPython, we have one type for just coroutines, this doesn't do generators
21  * nor asyncgen.
22  *
23  * It strives to be full replacement for normal coroutines.
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_Coroutine_Type = 0;
38 int count_allocated_Nuitka_Coroutine_Type = 0;
39 int count_released_Nuitka_Coroutine_Type = 0;
40 int count_active_Nuitka_CoroutineWrapper_Type = 0;
41 int count_allocated_Nuitka_CoroutineWrapper_Type = 0;
42 int count_released_Nuitka_CoroutineWrapper_Type = 0;
43 int count_active_Nuitka_AIterWrapper_Type = 0;
44 int count_allocated_Nuitka_AIterWrapper_Type = 0;
45 int count_released_Nuitka_AIterWrapper_Type = 0;
46 #endif
47 
48 static PyObject *_Nuitka_Coroutine_send(struct Nuitka_CoroutineObject *coroutine, PyObject *value, bool closing,
49                                         PyObject *exception_type, PyObject *exception_value,
50                                         PyTracebackObject *exception_tb);
51 
Nuitka_Coroutine_get_name(struct Nuitka_CoroutineObject * coroutine)52 static PyObject *Nuitka_Coroutine_get_name(struct Nuitka_CoroutineObject *coroutine) {
53     CHECK_OBJECT(coroutine);
54 
55     Py_INCREF(coroutine->m_name);
56     return coroutine->m_name;
57 }
58 
Nuitka_Coroutine_set_name(struct Nuitka_CoroutineObject * coroutine,PyObject * value)59 static int Nuitka_Coroutine_set_name(struct Nuitka_CoroutineObject *coroutine, PyObject *value) {
60     CHECK_OBJECT(coroutine);
61     CHECK_OBJECT_X(value);
62 
63     // Cannot be deleted, not be non-unicode value.
64     if (unlikely((value == NULL) || !PyUnicode_Check(value))) {
65         SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_TypeError, "__name__ must be set to a string object");
66 
67         return -1;
68     }
69 
70     PyObject *tmp = coroutine->m_name;
71     Py_INCREF(value);
72     coroutine->m_name = value;
73     Py_DECREF(tmp);
74 
75     return 0;
76 }
77 
Nuitka_Coroutine_get_qualname(struct Nuitka_CoroutineObject * coroutine)78 static PyObject *Nuitka_Coroutine_get_qualname(struct Nuitka_CoroutineObject *coroutine) {
79     CHECK_OBJECT(coroutine);
80 
81     Py_INCREF(coroutine->m_qualname);
82     return coroutine->m_qualname;
83 }
84 
Nuitka_Coroutine_set_qualname(struct Nuitka_CoroutineObject * coroutine,PyObject * value)85 static int Nuitka_Coroutine_set_qualname(struct Nuitka_CoroutineObject *coroutine, PyObject *value) {
86     CHECK_OBJECT(coroutine);
87     CHECK_OBJECT_X(value);
88 
89     // Cannot be deleted, not be non-unicode value.
90     if (unlikely((value == NULL) || !PyUnicode_Check(value))) {
91         SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_TypeError, "__qualname__ must be set to a string object");
92 
93         return -1;
94     }
95 
96     PyObject *tmp = coroutine->m_qualname;
97     Py_INCREF(value);
98     coroutine->m_qualname = value;
99     Py_DECREF(tmp);
100 
101     return 0;
102 }
103 
Nuitka_Coroutine_get_cr_await(struct Nuitka_CoroutineObject * coroutine)104 static PyObject *Nuitka_Coroutine_get_cr_await(struct Nuitka_CoroutineObject *coroutine) {
105     CHECK_OBJECT(coroutine);
106     CHECK_OBJECT_X(coroutine->m_yieldfrom);
107 
108     if (coroutine->m_yieldfrom) {
109         Py_INCREF(coroutine->m_yieldfrom);
110         return coroutine->m_yieldfrom;
111     } else {
112         Py_INCREF(Py_None);
113         return Py_None;
114     }
115 }
116 
Nuitka_Coroutine_get_code(struct Nuitka_CoroutineObject * coroutine)117 static PyObject *Nuitka_Coroutine_get_code(struct Nuitka_CoroutineObject *coroutine) {
118     CHECK_OBJECT(coroutine);
119     CHECK_OBJECT(coroutine->m_code_object);
120 
121     Py_INCREF(coroutine->m_code_object);
122     return (PyObject *)coroutine->m_code_object;
123 }
124 
Nuitka_Coroutine_set_code(struct Nuitka_CoroutineObject * coroutine,PyObject * value)125 static int Nuitka_Coroutine_set_code(struct Nuitka_CoroutineObject *coroutine, PyObject *value) {
126     CHECK_OBJECT(coroutine);
127 
128     SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "cr_code is not writable in Nuitka");
129     return -1;
130 }
131 
Nuitka_Coroutine_get_frame(struct Nuitka_CoroutineObject * coroutine)132 static PyObject *Nuitka_Coroutine_get_frame(struct Nuitka_CoroutineObject *coroutine) {
133     CHECK_OBJECT(coroutine);
134     CHECK_OBJECT_X(coroutine->m_frame);
135 
136     if (coroutine->m_frame) {
137         Py_INCREF(coroutine->m_frame);
138         return (PyObject *)coroutine->m_frame;
139     } else {
140         Py_INCREF(Py_None);
141         return Py_None;
142     }
143 }
144 
Nuitka_Coroutine_set_frame(struct Nuitka_CoroutineObject * coroutine,PyObject * value)145 static int Nuitka_Coroutine_set_frame(struct Nuitka_CoroutineObject *coroutine, PyObject *value) {
146     CHECK_OBJECT(coroutine);
147     CHECK_OBJECT_X(value);
148 
149     SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "gi_frame is not writable in Nuitka");
150     return -1;
151 }
152 
Nuitka_Coroutine_release_closure(struct Nuitka_CoroutineObject * coroutine)153 static void Nuitka_Coroutine_release_closure(struct Nuitka_CoroutineObject *coroutine) {
154     CHECK_OBJECT(coroutine);
155 
156     for (Py_ssize_t i = 0; i < coroutine->m_closure_given; i++) {
157         CHECK_OBJECT(coroutine->m_closure[i]);
158         Py_DECREF(coroutine->m_closure[i]);
159     }
160 
161     coroutine->m_closure_given = 0;
162 }
163 
164 // Note: Shared with asyncgen.
_Nuitka_YieldFromCore(PyObject * yieldfrom,PyObject * send_value,PyObject ** returned_value,bool mode)165 static PyObject *_Nuitka_YieldFromCore(PyObject *yieldfrom, PyObject *send_value, PyObject **returned_value,
166                                        bool mode) {
167     // Send iteration value to the sub-generator, which may be a CPython
168     // generator object, something with an iterator next, or a send method,
169     // where the later is only required if values other than "None" need to
170     // be passed in.
171     CHECK_OBJECT(yieldfrom);
172     CHECK_OBJECT_X(send_value);
173 
174     assert(send_value != NULL || ERROR_OCCURRED());
175 
176     PyObject *retval;
177 
178     PyObject *exception_type, *exception_value;
179     PyTracebackObject *exception_tb;
180 
181     FETCH_ERROR_OCCURRED(&exception_type, &exception_value, &exception_tb);
182 
183     if (exception_type != NULL) {
184         // Exception, was thrown into us, need to send that to sub-generator.
185         // We acquired ownership of the published exception and need to release it potentially.
186 
187         // Transfer exception owner this.
188         retval = _Nuitka_YieldFromPassExceptionTo(yieldfrom, exception_type, exception_value, exception_tb);
189 
190         if (unlikely(send_value == NULL)) {
191             PyObject *error = GET_ERROR_OCCURRED();
192 
193             if (error != NULL && EXCEPTION_MATCH_BOOL_SINGLE(error, PyExc_StopIteration)) {
194                 *returned_value = ERROR_GET_STOP_ITERATION_VALUE();
195 
196                 assert(!ERROR_OCCURRED());
197                 return NULL;
198             }
199         }
200     } else if (PyGen_CheckExact(yieldfrom) || PyCoro_CheckExact(yieldfrom)) {
201         retval = Nuitka_PyGen_Send((PyGenObject *)yieldfrom, Py_None);
202     } else if (send_value == Py_None && Nuitka_CoroutineWrapper_Check(yieldfrom)) {
203         struct Nuitka_CoroutineObject *yieldfrom_coroutine =
204             ((struct Nuitka_CoroutineWrapperObject *)yieldfrom)->m_coroutine;
205 
206         retval = _Nuitka_Coroutine_send(yieldfrom_coroutine, Py_None, mode ? false : true, NULL, NULL, NULL);
207     } else if (send_value == Py_None && Py_TYPE(yieldfrom)->tp_iternext != NULL) {
208         retval = Py_TYPE(yieldfrom)->tp_iternext(yieldfrom);
209     } else {
210 #if 0
211         // TODO: Add slow mode traces.
212         PRINT_ITEM(yieldfrom);
213         PRINT_NEW_LINE();
214 #endif
215 
216         retval = PyObject_CallMethodObjArgs(yieldfrom, const_str_plain_send, send_value, NULL);
217     }
218 
219     // Check the sub-generator result
220     if (retval == NULL) {
221         PyObject *error = GET_ERROR_OCCURRED();
222 
223         if (error == NULL) {
224             Py_INCREF(Py_None);
225             *returned_value = Py_None;
226         } else if (likely(EXCEPTION_MATCH_BOOL_SINGLE(error, PyExc_StopIteration))) {
227             // The sub-generator has given an exception. In case of
228             // StopIteration, we need to check the value, as it is going to be
229             // the expression value of this "yield from", and we are done. All
230             // other errors, we need to raise.
231             *returned_value = ERROR_GET_STOP_ITERATION_VALUE();
232             assert(*returned_value != NULL);
233             assert(!ERROR_OCCURRED());
234         } else {
235             *returned_value = NULL;
236         }
237 
238         return NULL;
239     } else {
240         assert(!ERROR_OCCURRED());
241         return retval;
242     }
243 }
244 
_Nuitka_YieldFromCoroutineCore(struct Nuitka_CoroutineObject * coroutine,PyObject * send_value,bool mode)245 static PyObject *_Nuitka_YieldFromCoroutineCore(struct Nuitka_CoroutineObject *coroutine, PyObject *send_value,
246                                                 bool mode) {
247     CHECK_OBJECT(coroutine);
248     CHECK_OBJECT_X(send_value);
249 
250     PyObject *yieldfrom = coroutine->m_yieldfrom;
251     CHECK_OBJECT(yieldfrom);
252 
253     // Need to make it unaccessible while using it.
254     coroutine->m_yieldfrom = NULL;
255 
256     PyObject *returned_value;
257     PyObject *yielded = _Nuitka_YieldFromCore(yieldfrom, send_value, &returned_value, mode);
258 
259     if (yielded == NULL) {
260         assert(coroutine->m_yieldfrom == NULL);
261         Py_DECREF(yieldfrom);
262 
263         yielded = ((coroutine_code)coroutine->m_code)(coroutine, returned_value);
264     } else {
265         assert(coroutine->m_yieldfrom == NULL);
266         coroutine->m_yieldfrom = yieldfrom;
267     }
268 
269     return yielded;
270 }
271 
272 #if _DEBUG_COROUTINE
_PRINT_COROUTINE_STATUS(char const * descriptor,char const * context,struct Nuitka_CoroutineObject * coroutine)273 NUITKA_MAY_BE_UNUSED static void _PRINT_COROUTINE_STATUS(char const *descriptor, char const *context,
274                                                          struct Nuitka_CoroutineObject *coroutine) {
275     char const *status;
276 
277     switch (coroutine->m_status) {
278     case status_Finished:
279         status = "(finished)";
280         break;
281     case status_Running:
282         status = "(running)";
283         break;
284     case status_Unused:
285         status = "(unused)";
286         break;
287     default:
288         status = "(ILLEGAL)";
289         break;
290     }
291 
292     PRINT_STRING(descriptor);
293     PRINT_STRING(" : ");
294     PRINT_STRING(context);
295     PRINT_STRING(" ");
296     PRINT_ITEM((PyObject *)coroutine);
297     PRINT_STRING(" ");
298     PRINT_REFCOUNT((PyObject *)coroutine);
299     PRINT_STRING(status);
300     PRINT_NEW_LINE();
301 }
302 
303 #define PRINT_COROUTINE_STATUS(context, coroutine) _PRINT_COROUTINE_STATUS(__FUNCTION__, context, coroutine)
304 
305 #endif
306 
Nuitka_YieldFromCoroutineNext(struct Nuitka_CoroutineObject * coroutine)307 static PyObject *Nuitka_YieldFromCoroutineNext(struct Nuitka_CoroutineObject *coroutine) {
308     CHECK_OBJECT(coroutine);
309 
310 #if _DEBUG_COROUTINE
311     PRINT_COROUTINE_STATUS("Enter", coroutine);
312     PRINT_NEW_LINE();
313 #endif
314     PyObject *result = _Nuitka_YieldFromCoroutineCore(coroutine, Py_None, true);
315 #if _DEBUG_COROUTINE
316     PRINT_COROUTINE_STATUS("Leave", coroutine);
317     PRINT_CURRENT_EXCEPTION();
318     PRINT_NEW_LINE();
319 #endif
320     return result;
321 }
322 
Nuitka_YieldFromCoroutineInitial(struct Nuitka_CoroutineObject * coroutine,PyObject * send_value)323 static PyObject *Nuitka_YieldFromCoroutineInitial(struct Nuitka_CoroutineObject *coroutine, PyObject *send_value) {
324     CHECK_OBJECT(coroutine);
325     CHECK_OBJECT_X(send_value);
326 
327 #if _DEBUG_COROUTINE
328     PRINT_COROUTINE_STATUS("Enter", coroutine);
329     PRINT_NEW_LINE();
330 #endif
331     PyObject *result = _Nuitka_YieldFromCoroutineCore(coroutine, send_value, false);
332 #if _DEBUG_COROUTINE
333     PRINT_COROUTINE_STATUS("Leave", coroutine);
334     PRINT_CURRENT_EXCEPTION();
335     PRINT_NEW_LINE();
336 #endif
337     return result;
338 }
339 
340 static void Nuitka_SetStopIterationValue(PyObject *value);
341 
342 // This function is called when sending a value or exception to be handled in the coroutine
343 // Note:
344 //   Exception arguments are passed for ownership and must be released before returning. The
345 //   value of exception_type may be NULL, and the actual exception will not necessarily
346 //   be normalized.
347 
_Nuitka_Coroutine_send(struct Nuitka_CoroutineObject * coroutine,PyObject * value,bool closing,PyObject * exception_type,PyObject * exception_value,PyTracebackObject * exception_tb)348 static PyObject *_Nuitka_Coroutine_send(struct Nuitka_CoroutineObject *coroutine, PyObject *value, bool closing,
349                                         PyObject *exception_type, PyObject *exception_value,
350                                         PyTracebackObject *exception_tb) {
351     CHECK_OBJECT(coroutine);
352     assert(Nuitka_Coroutine_Check((PyObject *)coroutine));
353     CHECK_OBJECT_X(exception_type);
354     CHECK_OBJECT_X(exception_value);
355     CHECK_OBJECT_X(exception_tb);
356     CHECK_OBJECT_X(value);
357 
358 #if _DEBUG_COROUTINE
359     PRINT_COROUTINE_STATUS("Enter", coroutine);
360     PRINT_COROUTINE_STRING("closing", closing ? "(closing) " : "(not closing) ");
361     PRINT_COROUTINE_VALUE("value", value);
362     PRINT_EXCEPTION(exception_type, exception_value, exception_tb);
363     PRINT_CURRENT_EXCEPTION();
364     PRINT_NEW_LINE();
365 #endif
366 
367     if (value != NULL) {
368         assert(exception_type == NULL);
369         assert(exception_value == NULL);
370         assert(exception_tb == NULL);
371     }
372 
373     if (coroutine->m_status == status_Unused && value != NULL && value != Py_None) {
374         // No exception if value is given.
375 
376         SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_TypeError, "can't send non-None value to a just-started coroutine");
377         return NULL;
378     }
379 
380     if (coroutine->m_status != status_Finished) {
381         if (coroutine->m_running) {
382             SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_ValueError, "coroutine already executing");
383             return NULL;
384         }
385 
386         PyThreadState *thread_state = PyThreadState_GET();
387 
388         // Put the coroutine back on the frame stack.
389 
390         // First take of running frame from the stack, owning a reference.
391         PyFrameObject *return_frame = thread_state->frame;
392 #ifndef __NUITKA_NO_ASSERT__
393         if (return_frame) {
394             assertFrameObject((struct Nuitka_FrameObject *)return_frame);
395         }
396 #endif
397 
398         if (coroutine->m_resume_frame) {
399             // It would be nice if our frame were still alive. Nobody had the
400             // right to release it.
401             assertFrameObject(coroutine->m_resume_frame);
402 
403             // It's not supposed to be on the top right now.
404             assert(return_frame != &coroutine->m_resume_frame->m_frame);
405 
406             thread_state->frame = &coroutine->m_resume_frame->m_frame;
407             coroutine->m_resume_frame = NULL;
408         }
409 
410         // Consider it as running.
411         if (coroutine->m_status == status_Unused) {
412             coroutine->m_status = status_Running;
413         }
414 
415         // Continue the yielder function while preventing recursion.
416         coroutine->m_running = true;
417 
418         // Check for thrown exception, publish it to the coroutine code.
419         if (unlikely(exception_type)) {
420             assert(value == NULL);
421 
422             // Transfer exception ownership to published.
423             RESTORE_ERROR_OCCURRED(exception_type, exception_value, exception_tb);
424         }
425 
426         if (coroutine->m_frame) {
427             Nuitka_Frame_MarkAsExecuting(coroutine->m_frame);
428         }
429 
430 #if _DEBUG_COROUTINE
431         PRINT_COROUTINE_STATUS("Switching to coroutine", coroutine);
432         PRINT_COROUTINE_VALUE("value", value);
433         PRINT_CURRENT_EXCEPTION();
434         PRINT_NEW_LINE();
435         // dumpFrameStack();
436 #endif
437 
438         PyObject *yielded;
439 
440         if (coroutine->m_yieldfrom == NULL) {
441             yielded = ((coroutine_code)coroutine->m_code)(coroutine, value);
442         } else {
443             yielded = Nuitka_YieldFromCoroutineInitial(coroutine, value);
444         }
445 
446         // If the coroutine returns with m_yieldfrom set, it wants us to yield
447         // from that value from now on.
448         while (yielded == NULL && coroutine->m_yieldfrom != NULL) {
449             yielded = Nuitka_YieldFromCoroutineNext(coroutine);
450         }
451 
452         if (coroutine->m_frame) {
453             Nuitka_Frame_MarkAsNotExecuting(coroutine->m_frame);
454         }
455 
456         coroutine->m_running = false;
457 
458         thread_state = PyThreadState_GET();
459 
460         // Remove the back frame from coroutine if it's there.
461         if (coroutine->m_frame) {
462             // assert(thread_state->frame == &coroutine->m_frame->m_frame);
463             assertFrameObject(coroutine->m_frame);
464 
465             Py_CLEAR(coroutine->m_frame->m_frame.f_back);
466 
467             // Remember where to resume from.
468             coroutine->m_resume_frame = (struct Nuitka_FrameObject *)thread_state->frame;
469         }
470 
471         // Return back to the frame that called us.
472         thread_state->frame = return_frame;
473 
474 #if _DEBUG_COROUTINE
475         PRINT_COROUTINE_STATUS("Returned from coroutine", coroutine);
476         // dumpFrameStack();
477 #endif
478 
479 #ifndef __NUITKA_NO_ASSERT__
480         if (return_frame) {
481             assertFrameObject((struct Nuitka_FrameObject *)return_frame);
482         }
483 #endif
484 
485         if (yielded == NULL) {
486 #if _DEBUG_COROUTINE
487             PRINT_COROUTINE_STATUS("finishing from yield", coroutine);
488             PRINT_COROUTINE_STRING("closing", closing ? "(closing) " : "(not closing) ");
489             PRINT_STRING("-> finishing sets status_Finished\n");
490             PRINT_COROUTINE_VALUE("return_value", coroutine->m_returned);
491             PRINT_CURRENT_EXCEPTION();
492             PRINT_NEW_LINE();
493 #endif
494             coroutine->m_status = status_Finished;
495 
496             if (coroutine->m_frame != NULL) {
497                 coroutine->m_frame->m_frame.f_gen = NULL;
498                 Py_DECREF(coroutine->m_frame);
499                 coroutine->m_frame = NULL;
500             }
501 
502             Nuitka_Coroutine_release_closure(coroutine);
503 
504             // Create StopIteration if necessary, i.e. return value that is not "None" was
505             // given. TODO: Push this further down the user line, we might be able to avoid
506             // it for some uses, e.g. quick iteration entirely.
507             if (coroutine->m_returned) {
508                 if (coroutine->m_returned != Py_None) {
509                     Nuitka_SetStopIterationValue(coroutine->m_returned);
510                 }
511 
512                 Py_DECREF(coroutine->m_returned);
513                 coroutine->m_returned = NULL;
514 
515 #if _DEBUG_COROUTINE
516                 PRINT_COROUTINE_STATUS("Return value to exception set", coroutine);
517                 PRINT_CURRENT_EXCEPTION();
518                 PRINT_NEW_LINE();
519 #endif
520             } else {
521                 PyObject *error = GET_ERROR_OCCURRED();
522 
523                 if (error == NULL) {
524                     SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
525                 } else if (error == PyExc_StopIteration) {
526                     PyObject *saved_exception_type, *saved_exception_value;
527                     PyTracebackObject *saved_exception_tb;
528 
529                     FETCH_ERROR_OCCURRED(&saved_exception_type, &saved_exception_value, &saved_exception_tb);
530                     NORMALIZE_EXCEPTION(&saved_exception_type, &saved_exception_value, &saved_exception_tb);
531 
532                     PyErr_Format(PyExc_RuntimeError, "coroutine raised StopIteration");
533 
534                     FETCH_ERROR_OCCURRED(&exception_type, &exception_value, &exception_tb);
535 
536                     RAISE_EXCEPTION_WITH_CAUSE(&exception_type, &exception_value, &exception_tb, saved_exception_value);
537 
538                     CHECK_OBJECT(exception_value);
539                     CHECK_OBJECT(saved_exception_value);
540 
541                     Py_INCREF(saved_exception_value);
542                     PyException_SetContext(exception_value, saved_exception_value);
543 
544                     Py_DECREF(saved_exception_type);
545                     Py_XDECREF(saved_exception_tb);
546 
547                     RESTORE_ERROR_OCCURRED(exception_type, exception_value, exception_tb);
548 
549 #if _DEBUG_COROUTINE
550                     PRINT_COROUTINE_STATUS("Leave with exception set", coroutine);
551                     PRINT_CURRENT_EXCEPTION();
552                     PRINT_NEW_LINE();
553 #endif
554                 }
555             }
556 
557             return NULL;
558         } else {
559             return yielded;
560         }
561     } else {
562         // Release exception if any, we are finished with it and will raise another.
563         Py_XDECREF(exception_type);
564         Py_XDECREF(exception_value);
565         Py_XDECREF(exception_tb);
566 
567         /* This is for status_Finished */
568         assert(coroutine->m_status == status_Finished);
569         /* This check got added in Python 3.5.2 only. It's good to do it, but
570          * not fully compatible, therefore guard it.
571          */
572 #if PYTHON_VERSION >= 0x352 || !defined(_NUITKA_FULL_COMPAT)
573         if (closing == false) {
574 #if _DEBUG_COROUTINE
575             PRINT_COROUTINE_STATUS("Finished coroutine sent into -> RuntimeError", coroutine);
576             PRINT_NEW_LINE();
577 #endif
578             PyErr_Format(PyExc_RuntimeError,
579 #if !defined(_NUITKA_FULL_COMPAT)
580                          "cannot reuse already awaited compiled_coroutine %S", coroutine->m_qualname
581 #else
582                          "cannot reuse already awaited coroutine"
583 #endif
584             );
585         } else
586 #endif
587         {
588             SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
589         }
590 
591         return NULL;
592     }
593 }
594 
Nuitka_Coroutine_send(struct Nuitka_CoroutineObject * coroutine,PyObject * value)595 static PyObject *Nuitka_Coroutine_send(struct Nuitka_CoroutineObject *coroutine, PyObject *value) {
596     CHECK_OBJECT(coroutine);
597     CHECK_OBJECT(value);
598 
599     // TODO: Does it release value ?
600     // Py_INCREF(value);
601     PyObject *result = _Nuitka_Coroutine_send(coroutine, value, false, NULL, NULL, NULL);
602 
603     if (result == NULL) {
604         if (GET_ERROR_OCCURRED() == NULL) {
605             SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
606         }
607     }
608 
609     return result;
610 }
611 
612 // Note: Used by compiled frames.
_Nuitka_Coroutine_close(struct Nuitka_CoroutineObject * coroutine)613 static bool _Nuitka_Coroutine_close(struct Nuitka_CoroutineObject *coroutine) {
614 #if _DEBUG_COROUTINE
615     PRINT_COROUTINE_STATUS("Enter", coroutine);
616 #endif
617     CHECK_OBJECT(coroutine);
618 
619     if (coroutine->m_status == status_Running) {
620         Py_INCREF(PyExc_GeneratorExit);
621 
622         PyObject *result = _Nuitka_Coroutine_send(coroutine, NULL, true, PyExc_GeneratorExit, NULL, NULL);
623 
624         if (unlikely(result)) {
625             Py_DECREF(result);
626 
627             SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "coroutine ignored GeneratorExit");
628             return false;
629         } else {
630             PyObject *error = GET_ERROR_OCCURRED();
631             assert(error != NULL);
632 
633             if (EXCEPTION_MATCH_GENERATOR(error)) {
634                 CLEAR_ERROR_OCCURRED();
635 
636                 return true;
637             }
638 
639             return false;
640         }
641     }
642 
643     return true;
644 }
645 
Nuitka_Coroutine_close(struct Nuitka_CoroutineObject * coroutine)646 static PyObject *Nuitka_Coroutine_close(struct Nuitka_CoroutineObject *coroutine) {
647     bool r = _Nuitka_Coroutine_close(coroutine);
648 
649     if (unlikely(r == false)) {
650         return NULL;
651     } else {
652         Py_INCREF(Py_None);
653         return Py_None;
654     }
655 }
656 
657 #if PYTHON_VERSION >= 0x360
658 static bool Nuitka_AsyncgenAsend_Check(PyObject *object);
659 struct Nuitka_AsyncgenAsendObject;
660 static PyObject *_Nuitka_AsyncgenAsend_throw2(struct Nuitka_AsyncgenAsendObject *asyncgen_asend,
661                                               PyObject *exception_type, PyObject *exception_value,
662                                               PyTracebackObject *exception_tb);
663 #endif
664 
665 static bool _Nuitka_Generator_check_throw2(PyObject **exception_type, PyObject **exception_value,
666                                            PyTracebackObject **exception_tb);
667 
668 // This function is called when yielding to a coroutine through "_Nuitka_YieldFromPassExceptionTo"
669 // and potentially wrapper objects used by generators, or by the throw method itself.
670 // Note:
671 //   Exception arguments are passed for ownership and must be released before returning. The
672 //   value of exception_type will not be NULL, but the actual exception will not necessarily
673 //   be normalized.
_Nuitka_Coroutine_throw2(struct Nuitka_CoroutineObject * coroutine,bool closing,PyObject * exception_type,PyObject * exception_value,PyTracebackObject * exception_tb)674 static PyObject *_Nuitka_Coroutine_throw2(struct Nuitka_CoroutineObject *coroutine, bool closing,
675                                           PyObject *exception_type, PyObject *exception_value,
676                                           PyTracebackObject *exception_tb) {
677     CHECK_OBJECT(coroutine);
678     assert(Nuitka_Coroutine_Check((PyObject *)coroutine));
679     CHECK_OBJECT(exception_type);
680     CHECK_OBJECT_X(exception_value);
681     CHECK_OBJECT_X(exception_tb);
682 
683 #if _DEBUG_COROUTINE
684     PRINT_COROUTINE_STATUS("Enter", coroutine);
685     PRINT_COROUTINE_STRING("closing", closing ? "(closing) " : "(not closing) ");
686     PRINT_COROUTINE_VALUE("yieldfrom", coroutine->m_yieldfrom);
687     PRINT_EXCEPTION(exception_type, exception_value, exception_tb);
688     PRINT_NEW_LINE();
689 #endif
690 
691     if (coroutine->m_yieldfrom != NULL) {
692         if (EXCEPTION_MATCH_BOOL_SINGLE(exception_type, PyExc_GeneratorExit)) {
693             // Coroutines need to close the yield_from.
694             coroutine->m_running = 1;
695             bool res = Nuitka_gen_close_iter(coroutine->m_yieldfrom);
696             coroutine->m_running = 0;
697 
698             if (res == false) {
699                 // Release exception, we are done with it now and pick up the new one.
700                 Py_DECREF(exception_type);
701                 Py_XDECREF(exception_value);
702                 Py_XDECREF(exception_tb);
703 
704                 FETCH_ERROR_OCCURRED(&exception_type, &exception_value, &exception_tb);
705             }
706 
707             // Transferred exception ownership to "_Nuitka_Coroutine_send".
708             return _Nuitka_Coroutine_send(coroutine, NULL, false, exception_type, exception_value, exception_tb);
709         }
710 
711         PyObject *ret;
712 
713 #if _DEBUG_COROUTINE
714         PRINT_COROUTINE_STATUS("Passing to yielded from", coroutine);
715         PRINT_COROUTINE_VALUE("m_yieldfrom", coroutine->m_yieldfrom);
716         PRINT_NEW_LINE();
717 #endif
718 
719         if (PyGen_CheckExact(coroutine->m_yieldfrom) || PyCoro_CheckExact(coroutine->m_yieldfrom)) {
720             PyGenObject *gen = (PyGenObject *)coroutine->m_yieldfrom;
721 
722             // Transferred exception ownership to "Nuitka_UncompiledGenerator_throw".
723             coroutine->m_running = 1;
724             ret = Nuitka_UncompiledGenerator_throw(gen, 1, exception_type, exception_value, exception_tb);
725             coroutine->m_running = 0;
726         } else if (Nuitka_Generator_Check(coroutine->m_yieldfrom)) {
727             struct Nuitka_GeneratorObject *gen = ((struct Nuitka_GeneratorObject *)coroutine->m_yieldfrom);
728             // Transferred exception ownership to "_Nuitka_Generator_throw2".
729             coroutine->m_running = 1;
730             ret = _Nuitka_Generator_throw2(gen, exception_type, exception_value, exception_tb);
731             coroutine->m_running = 0;
732         } else if (Nuitka_Coroutine_Check(coroutine->m_yieldfrom)) {
733             struct Nuitka_CoroutineObject *coro = ((struct Nuitka_CoroutineObject *)coroutine->m_yieldfrom);
734             // Transferred exception ownership to "_Nuitka_Coroutine_throw2".
735             coroutine->m_running = 1;
736             ret = _Nuitka_Coroutine_throw2(coro, true, exception_type, exception_value, exception_tb);
737             coroutine->m_running = 0;
738         } else if (Nuitka_CoroutineWrapper_Check(coroutine->m_yieldfrom)) {
739             struct Nuitka_CoroutineObject *coro =
740                 ((struct Nuitka_CoroutineWrapperObject *)coroutine->m_yieldfrom)->m_coroutine;
741 
742             // Transferred exception ownership to "_Nuitka_Coroutine_throw2".
743             coroutine->m_running = 1;
744             ret = _Nuitka_Coroutine_throw2(coro, true, exception_type, exception_value, exception_tb);
745             coroutine->m_running = 0;
746 #if PYTHON_VERSION >= 0x360
747         } else if (Nuitka_AsyncgenAsend_Check(coroutine->m_yieldfrom)) {
748             struct Nuitka_AsyncgenAsendObject *asyncgen_asend =
749                 ((struct Nuitka_AsyncgenAsendObject *)coroutine->m_yieldfrom);
750 
751             // Transferred exception ownership to "_Nuitka_AsyncgenAsend_throw2".
752             coroutine->m_running = 1;
753             ret = _Nuitka_AsyncgenAsend_throw2(asyncgen_asend, exception_type, exception_value, exception_tb);
754             coroutine->m_running = 0;
755 #endif
756         } else {
757             PyObject *meth = PyObject_GetAttr(coroutine->m_yieldfrom, const_str_plain_throw);
758             if (unlikely(meth == NULL)) {
759                 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
760                     // Release exception, we are done with it now.
761                     Py_DECREF(exception_type);
762                     Py_XDECREF(exception_value);
763                     Py_XDECREF(exception_tb);
764 
765                     return NULL;
766                 }
767 
768                 CLEAR_ERROR_OCCURRED();
769 
770                 // Passing exception ownership to that code.
771                 goto throw_here;
772             }
773 
774             CHECK_OBJECT(exception_type);
775 
776 #if 0
777             // TODO: Add slow mode traces.
778             PRINT_ITEM(coroutine->m_yieldfrom);
779             PRINT_NEW_LINE();
780 #endif
781             coroutine->m_running = 1;
782             ret = PyObject_CallFunctionObjArgs(meth, exception_type, exception_value, exception_tb, NULL);
783             coroutine->m_running = 0;
784 
785             Py_DECREF(meth);
786 
787             // Release exception, we are done with it now.
788             Py_DECREF(exception_type);
789             Py_XDECREF(exception_value);
790             Py_XDECREF(exception_tb);
791         }
792 
793         if (unlikely(ret == NULL)) {
794             // Return value or exception, not to continue with yielding from.
795             if (coroutine->m_yieldfrom != NULL) {
796                 CHECK_OBJECT(coroutine->m_yieldfrom);
797 #if _DEBUG_COROUTINE
798                 PRINT_COROUTINE_STATUS("Null return, yield from removal:", coroutine);
799                 PRINT_COROUTINE_VALUE("yieldfrom", coroutine->m_yieldfrom);
800 #endif
801                 Py_DECREF(coroutine->m_yieldfrom);
802                 coroutine->m_yieldfrom = NULL;
803             }
804 
805             PyObject *val;
806             if (_PyGen_FetchStopIterationValue(&val) == 0) {
807                 CHECK_OBJECT(val);
808 
809 #if _DEBUG_COROUTINE
810                 PRINT_COROUTINE_STATUS("Sending return value into ourselves", coroutine);
811                 PRINT_COROUTINE_VALUE("value", val);
812                 PRINT_NEW_LINE();
813 #endif
814 
815                 ret = _Nuitka_Coroutine_send(coroutine, val, false, NULL, NULL, NULL);
816             } else {
817 #if _DEBUG_COROUTINE
818                 PRINT_COROUTINE_STATUS("Sending exception value into ourselves", coroutine);
819                 PRINT_CURRENT_EXCEPTION();
820                 PRINT_NEW_LINE();
821 #endif
822                 ret = _Nuitka_Coroutine_send(coroutine, NULL, false, NULL, NULL, NULL);
823             }
824 
825 #if _DEBUG_COROUTINE
826             PRINT_COROUTINE_STATUS("Leave with value/exception from sending into ourselves:", coroutine);
827             PRINT_COROUTINE_STRING("closing", closing ? "(closing) " : "(not closing) ");
828             PRINT_COROUTINE_VALUE("return_value", ret);
829             PRINT_CURRENT_EXCEPTION();
830             PRINT_NEW_LINE();
831 #endif
832         } else {
833 #if _DEBUG_COROUTINE
834             PRINT_COROUTINE_STATUS("Leave with return value:", coroutine);
835             PRINT_COROUTINE_STRING("closing", closing ? "(closing) " : "(not closing) ");
836             PRINT_COROUTINE_VALUE("return_value", ret);
837             PRINT_CURRENT_EXCEPTION();
838             PRINT_NEW_LINE();
839 #endif
840         }
841 
842         return ret;
843     }
844 
845 throw_here:
846     // We continue to have exception ownership here.
847 
848     if (unlikely(_Nuitka_Generator_check_throw2(&exception_type, &exception_value, &exception_tb) == false)) {
849         // Exception was released by _Nuitka_Generator_check_throw2 already.
850         return NULL;
851     }
852 
853     if (coroutine->m_status == status_Running) {
854         // Transferred exception ownership to "_Nuitka_Coroutine_send".
855         PyObject *result =
856             _Nuitka_Coroutine_send(coroutine, NULL, false, exception_type, exception_value, exception_tb);
857         return result;
858     } else if (coroutine->m_status == status_Finished) {
859 
860         /* This check got added in Python 3.5.2 only. It's good to do it, but
861          * not fully compatible, therefore guard it.
862          */
863 #if PYTHON_VERSION >= 0x352 || !defined(_NUITKA_FULL_COMPAT)
864         if (closing == false) {
865 #if _DEBUG_COROUTINE
866             PRINT_STRING("Finished coroutine thrown into -> RuntimeError\n");
867             PRINT_ITEM(coroutine->m_qualname);
868             PRINT_NEW_LINE();
869 #endif
870             PyErr_Format(PyExc_RuntimeError,
871 #if !defined(_NUITKA_FULL_COMPAT)
872                          "cannot reuse already awaited compiled_coroutine %S", coroutine->m_qualname
873 #else
874                          "cannot reuse already awaited coroutine"
875 #endif
876             );
877 
878             Py_DECREF(exception_type);
879             Py_XDECREF(exception_value);
880             Py_XDECREF(exception_tb);
881 
882             return NULL;
883         }
884 #endif
885         // Passing exception to publication.
886         RESTORE_ERROR_OCCURRED(exception_type, exception_value, exception_tb);
887 
888         return NULL;
889     } else {
890         if (exception_tb == NULL) {
891             // TODO: Our compiled objects really need a way to store common
892             // stuff in a "shared" part across all instances, and outside of
893             // run time, so we could reuse this.
894             struct Nuitka_FrameObject *frame = MAKE_FUNCTION_FRAME(coroutine->m_code_object, coroutine->m_module, 0);
895             exception_tb = MAKE_TRACEBACK(frame, coroutine->m_code_object->co_firstlineno);
896             Py_DECREF(frame);
897         }
898 
899         // Passing exception to publication.
900         RESTORE_ERROR_OCCURRED(exception_type, exception_value, exception_tb);
901 
902 #if _DEBUG_COROUTINE
903         PRINT_COROUTINE_STATUS("Finishing from exception", coroutine);
904         PRINT_NEW_LINE();
905 #endif
906 
907         coroutine->m_status = status_Finished;
908 
909         return NULL;
910     }
911 }
912 
Nuitka_Coroutine_throw(struct Nuitka_CoroutineObject * coroutine,PyObject * args)913 static PyObject *Nuitka_Coroutine_throw(struct Nuitka_CoroutineObject *coroutine, PyObject *args) {
914     CHECK_OBJECT(coroutine);
915     CHECK_OBJECT_DEEP(args);
916 
917     PyObject *exception_type;
918     PyObject *exception_value = NULL;
919     PyTracebackObject *exception_tb = NULL;
920 
921     // This takes no references, that is for us to do.
922     int res = PyArg_UnpackTuple(args, "throw", 1, 3, &exception_type, &exception_value, &exception_tb);
923 
924     if (unlikely(res == 0)) {
925         return NULL;
926     }
927 
928 #if _DEBUG_COROUTINE
929     PRINT_COROUTINE_STATUS("Enter", coroutine);
930     PRINT_EXCEPTION(exception_type, exception_value, exception_tb);
931     PRINT_NEW_LINE();
932 #endif
933 
934     // Handing ownership of exception over, we need not release it ourselves
935     Py_INCREF(exception_type);
936     Py_XINCREF(exception_value);
937     Py_XINCREF(exception_tb);
938 
939     PyObject *result = _Nuitka_Coroutine_throw2(coroutine, false, exception_type, exception_value, exception_tb);
940 
941     if (result == NULL) {
942         if (GET_ERROR_OCCURRED() == NULL) {
943             SET_CURRENT_EXCEPTION_TYPE0(PyExc_StopIteration);
944         }
945     }
946 
947 #if _DEBUG_COROUTINE
948     PRINT_COROUTINE_STATUS("Leave", coroutine);
949     PRINT_EXCEPTION(exception_type, exception_value, exception_tb);
950     PRINT_COROUTINE_VALUE("return value", result);
951     PRINT_CURRENT_EXCEPTION();
952 #endif
953 
954     return result;
955 }
956 
Nuitka_Coroutine_tp_repr(struct Nuitka_CoroutineObject * coroutine)957 static PyObject *Nuitka_Coroutine_tp_repr(struct Nuitka_CoroutineObject *coroutine) {
958     CHECK_OBJECT(coroutine);
959     CHECK_OBJECT(coroutine->m_qualname);
960 
961     return PyUnicode_FromFormat("<compiled_coroutine object %s at %p>", Nuitka_String_AsString(coroutine->m_qualname),
962                                 coroutine);
963 }
964 
Nuitka_Coroutine_tp_traverse(struct Nuitka_CoroutineObject * coroutine,visitproc visit,void * arg)965 static long Nuitka_Coroutine_tp_traverse(struct Nuitka_CoroutineObject *coroutine, visitproc visit, void *arg) {
966     CHECK_OBJECT(coroutine);
967 
968     // TODO: Identify the impact of not visiting owned objects like module and
969     // frame.
970     Py_VISIT(coroutine->m_yieldfrom);
971 
972     for (Py_ssize_t i = 0; i < coroutine->m_closure_given; i++) {
973         Py_VISIT(coroutine->m_closure[i]);
974     }
975 
976     return 0;
977 }
978 
979 static struct Nuitka_CoroutineWrapperObject *free_list_coro_wrappers = NULL;
980 static int free_list_coro_wrappers_count = 0;
981 
Nuitka_Coroutine_await(struct Nuitka_CoroutineObject * coroutine)982 static PyObject *Nuitka_Coroutine_await(struct Nuitka_CoroutineObject *coroutine) {
983     CHECK_OBJECT(coroutine);
984 
985 #if _DEBUG_COROUTINE
986     PRINT_COROUTINE_STATUS("Enter", coroutine);
987     PRINT_NEW_LINE();
988 #endif
989 
990 #if _DEBUG_REFCOUNTS
991     count_active_Nuitka_CoroutineWrapper_Type += 1;
992     count_allocated_Nuitka_CoroutineWrapper_Type += 1;
993 #endif
994 
995     struct Nuitka_CoroutineWrapperObject *result;
996 
997     allocateFromFreeListFixed(free_list_coro_wrappers, struct Nuitka_CoroutineWrapperObject,
998                               Nuitka_CoroutineWrapper_Type);
999 
1000     if (unlikely(result == NULL)) {
1001         return NULL;
1002     }
1003 
1004     result->m_coroutine = coroutine;
1005     Py_INCREF(result->m_coroutine);
1006 
1007     Nuitka_GC_Track(result);
1008 
1009     return (PyObject *)result;
1010 }
1011 
Nuitka_Coroutine_tp_finalize(struct Nuitka_CoroutineObject * coroutine)1012 static void Nuitka_Coroutine_tp_finalize(struct Nuitka_CoroutineObject *coroutine) {
1013     if (coroutine->m_status != status_Running) {
1014         return;
1015     }
1016 
1017     PyObject *save_exception_type, *save_exception_value;
1018     PyTracebackObject *save_exception_tb;
1019     FETCH_ERROR_OCCURRED(&save_exception_type, &save_exception_value, &save_exception_tb);
1020 
1021     bool close_result = _Nuitka_Coroutine_close(coroutine);
1022 
1023     if (unlikely(close_result == false)) {
1024         PyErr_WriteUnraisable((PyObject *)coroutine);
1025     }
1026 
1027     /* Restore the saved exception if any. */
1028     RESTORE_ERROR_OCCURRED(save_exception_type, save_exception_value, save_exception_tb);
1029 }
1030 
1031 #define MAX_COROUTINE_FREE_LIST_COUNT 100
1032 static struct Nuitka_CoroutineObject *free_list_coros = NULL;
1033 static int free_list_coros_count = 0;
1034 
Nuitka_Coroutine_tp_dealloc(struct Nuitka_CoroutineObject * coroutine)1035 static void Nuitka_Coroutine_tp_dealloc(struct Nuitka_CoroutineObject *coroutine) {
1036 #if _DEBUG_REFCOUNTS
1037     count_active_Nuitka_Coroutine_Type -= 1;
1038     count_released_Nuitka_Coroutine_Type += 1;
1039 #endif
1040 
1041     // Revive temporarily.
1042     assert(Py_REFCNT(coroutine) == 0);
1043     Py_REFCNT(coroutine) = 1;
1044 
1045     // Save the current exception, if any, we must preserve it.
1046     PyObject *save_exception_type, *save_exception_value;
1047     PyTracebackObject *save_exception_tb;
1048     FETCH_ERROR_OCCURRED(&save_exception_type, &save_exception_value, &save_exception_tb);
1049 
1050 #if _DEBUG_COROUTINE
1051     PRINT_COROUTINE_STATUS("Enter", coroutine);
1052     PRINT_NEW_LINE();
1053 #endif
1054 
1055     bool close_result = _Nuitka_Coroutine_close(coroutine);
1056 
1057     if (unlikely(close_result == false)) {
1058         PyErr_WriteUnraisable((PyObject *)coroutine);
1059     }
1060 
1061     Nuitka_Coroutine_release_closure(coroutine);
1062 
1063     // Allow for above code to resurrect the coroutine.
1064     Py_REFCNT(coroutine) -= 1;
1065     if (Py_REFCNT(coroutine) >= 1) {
1066         RESTORE_ERROR_OCCURRED(save_exception_type, save_exception_value, save_exception_tb);
1067         return;
1068     }
1069 
1070     if (coroutine->m_frame) {
1071         coroutine->m_frame->m_frame.f_gen = NULL;
1072         Py_DECREF(coroutine->m_frame);
1073         coroutine->m_frame = NULL;
1074     }
1075 
1076     // Now it is safe to release references and memory for it.
1077     Nuitka_GC_UnTrack(coroutine);
1078 
1079     if (coroutine->m_weakrefs != NULL) {
1080         PyObject_ClearWeakRefs((PyObject *)coroutine);
1081         assert(!ERROR_OCCURRED());
1082     }
1083 
1084     Py_DECREF(coroutine->m_name);
1085     Py_DECREF(coroutine->m_qualname);
1086 
1087     /* Put the object into freelist or release to GC */
1088     releaseToFreeList(free_list_coros, coroutine, MAX_COROUTINE_FREE_LIST_COUNT);
1089 
1090     RESTORE_ERROR_OCCURRED(save_exception_type, save_exception_value, save_exception_tb);
1091 }
1092 
1093 // TODO: Set "__doc__" automatically for method clones of compiled types from
1094 // the documentation of built-in original type.
1095 static PyMethodDef Nuitka_Coroutine_methods[] = {{"send", (PyCFunction)Nuitka_Coroutine_send, METH_O, NULL},
1096                                                  {"throw", (PyCFunction)Nuitka_Coroutine_throw, METH_VARARGS, NULL},
1097                                                  {"close", (PyCFunction)Nuitka_Coroutine_close, METH_NOARGS, NULL},
1098                                                  {NULL}};
1099 
1100 // TODO: Set "__doc__" automatically for method clones of compiled types from
1101 // the documentation of built-in original type.
1102 static PyGetSetDef Nuitka_Coroutine_getsetlist[] = {
1103     {(char *)"__name__", (getter)Nuitka_Coroutine_get_name, (setter)Nuitka_Coroutine_set_name, NULL},
1104     {(char *)"__qualname__", (getter)Nuitka_Coroutine_get_qualname, (setter)Nuitka_Coroutine_set_qualname, NULL},
1105     {(char *)"cr_await", (getter)Nuitka_Coroutine_get_cr_await, (setter)NULL, NULL},
1106     {(char *)"cr_code", (getter)Nuitka_Coroutine_get_code, (setter)Nuitka_Coroutine_set_code, NULL},
1107     {(char *)"cr_frame", (getter)Nuitka_Coroutine_get_frame, (setter)Nuitka_Coroutine_set_frame, NULL},
1108 
1109     {NULL}};
1110 
1111 static PyMemberDef Nuitka_Coroutine_members[] = {
1112     {(char *)"cr_running", T_BOOL, offsetof(struct Nuitka_CoroutineObject, m_running), READONLY},
1113 #if PYTHON_VERSION >= 0x370
1114     {(char *)"cr_origin", T_OBJECT, offsetof(struct Nuitka_CoroutineObject, m_origin), READONLY},
1115 
1116 #endif
1117     {NULL}};
1118 
1119 static PyAsyncMethods Nuitka_Coroutine_as_async = {
1120     (unaryfunc)Nuitka_Coroutine_await, /* am_await */
1121     0,                                 /* am_aiter */
1122     0                                  /* am_anext */
1123 };
1124 
1125 PyTypeObject Nuitka_Coroutine_Type = {
1126     PyVarObject_HEAD_INIT(NULL, 0) "compiled_coroutine",                /* tp_name */
1127     sizeof(struct Nuitka_CoroutineObject),                              /* tp_basicsize */
1128     sizeof(struct Nuitka_CellObject *),                                 /* tp_itemsize */
1129     (destructor)Nuitka_Coroutine_tp_dealloc,                            /* tp_dealloc */
1130     0,                                                                  /* tp_print */
1131     0,                                                                  /* tp_getattr */
1132     0,                                                                  /* tp_setattr */
1133     &Nuitka_Coroutine_as_async,                                         /* tp_as_async */
1134     (reprfunc)Nuitka_Coroutine_tp_repr,                                 /* tp_repr */
1135     0,                                                                  /* tp_as_number */
1136     0,                                                                  /* tp_as_sequence */
1137     0,                                                                  /* tp_as_mapping */
1138     0,                                                                  /* tp_hash */
1139     0,                                                                  /* tp_call */
1140     0,                                                                  /* tp_str */
1141     PyObject_GenericGetAttr,                                            /* tp_getattro */
1142     0,                                                                  /* tp_setattro */
1143     0,                                                                  /* tp_as_buffer */
1144     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
1145     0,                                                                  /* tp_doc */
1146     (traverseproc)Nuitka_Coroutine_tp_traverse,                         /* tp_traverse */
1147     0,                                                                  /* tp_clear */
1148     0,                                                                  /* tp_richcompare */
1149     offsetof(struct Nuitka_CoroutineObject, m_weakrefs),                /* tp_weaklistoffset */
1150     0,                                                                  /* tp_iter */
1151     0,                                                                  /* tp_iternext */
1152     Nuitka_Coroutine_methods,                                           /* tp_methods */
1153     Nuitka_Coroutine_members,                                           /* tp_members */
1154     Nuitka_Coroutine_getsetlist,                                        /* tp_getset */
1155     0,                                                                  /* tp_base */
1156     0,                                                                  /* tp_dict */
1157     0,                                                                  /* tp_descr_get */
1158     0,                                                                  /* tp_descr_set */
1159     0,                                                                  /* tp_dictoffset */
1160     0,                                                                  /* tp_init */
1161     0,                                                                  /* tp_alloc */
1162     0,                                                                  /* tp_new */
1163     0,                                                                  /* tp_free */
1164     0,                                                                  /* tp_is_gc */
1165     0,                                                                  /* tp_bases */
1166     0,                                                                  /* tp_mro */
1167     0,                                                                  /* tp_cache */
1168     0,                                                                  /* tp_subclasses */
1169     0,                                                                  /* tp_weaklist */
1170     0,                                                                  /* tp_del */
1171     0,                                                                  /* tp_version_tag */
1172     (destructor)Nuitka_Coroutine_tp_finalize,                           /* tp_finalize */
1173 };
1174 
Nuitka_CoroutineWrapper_tp_dealloc(struct Nuitka_CoroutineWrapperObject * cw)1175 static void Nuitka_CoroutineWrapper_tp_dealloc(struct Nuitka_CoroutineWrapperObject *cw) {
1176     Nuitka_GC_UnTrack((PyObject *)cw);
1177 
1178     assert(Py_REFCNT(cw) == 0);
1179     Py_REFCNT(cw) = 1;
1180 
1181 #if _DEBUG_REFCOUNTS
1182     count_active_Nuitka_CoroutineWrapper_Type -= 1;
1183     count_released_Nuitka_CoroutineWrapper_Type += 1;
1184 #endif
1185     CHECK_OBJECT(cw->m_coroutine);
1186 
1187     Py_DECREF(cw->m_coroutine);
1188     cw->m_coroutine = NULL;
1189 
1190     assert(Py_REFCNT(cw) == 1);
1191     Py_REFCNT(cw) = 0;
1192 
1193     releaseToFreeList(free_list_coro_wrappers, cw, MAX_COROUTINE_FREE_LIST_COUNT);
1194 }
1195 
Nuitka_CoroutineWrapper_tp_iternext(struct Nuitka_CoroutineWrapperObject * cw)1196 static PyObject *Nuitka_CoroutineWrapper_tp_iternext(struct Nuitka_CoroutineWrapperObject *cw) {
1197     CHECK_OBJECT(cw);
1198 
1199     return Nuitka_Coroutine_send(cw->m_coroutine, Py_None);
1200 }
1201 
Nuitka_CoroutineWrapper_tp_traverse(struct Nuitka_CoroutineWrapperObject * cw,visitproc visit,void * arg)1202 static int Nuitka_CoroutineWrapper_tp_traverse(struct Nuitka_CoroutineWrapperObject *cw, visitproc visit, void *arg) {
1203     CHECK_OBJECT(cw);
1204 
1205     Py_VISIT((PyObject *)cw->m_coroutine);
1206     return 0;
1207 }
1208 
Nuitka_CoroutineWrapper_send(struct Nuitka_CoroutineWrapperObject * cw,PyObject * arg)1209 static PyObject *Nuitka_CoroutineWrapper_send(struct Nuitka_CoroutineWrapperObject *cw, PyObject *arg) {
1210     CHECK_OBJECT(cw);
1211     CHECK_OBJECT(arg);
1212 
1213     return Nuitka_Coroutine_send(cw->m_coroutine, arg);
1214 }
1215 
Nuitka_CoroutineWrapper_throw(struct Nuitka_CoroutineWrapperObject * cw,PyObject * args)1216 static PyObject *Nuitka_CoroutineWrapper_throw(struct Nuitka_CoroutineWrapperObject *cw, PyObject *args) {
1217     CHECK_OBJECT(cw);
1218     CHECK_OBJECT_DEEP(args);
1219 
1220     return Nuitka_Coroutine_throw(cw->m_coroutine, args);
1221 }
1222 
Nuitka_CoroutineWrapper_close(struct Nuitka_CoroutineWrapperObject * cw)1223 static PyObject *Nuitka_CoroutineWrapper_close(struct Nuitka_CoroutineWrapperObject *cw) {
1224     CHECK_OBJECT(cw);
1225 
1226     return Nuitka_Coroutine_close(cw->m_coroutine);
1227 }
1228 
Nuitka_CoroutineWrapper_tp_repr(struct Nuitka_CoroutineWrapperObject * cw)1229 static PyObject *Nuitka_CoroutineWrapper_tp_repr(struct Nuitka_CoroutineWrapperObject *cw) {
1230     CHECK_OBJECT(cw);
1231     CHECK_OBJECT(cw->m_coroutine);
1232     CHECK_OBJECT(cw->m_coroutine->m_qualname);
1233 
1234     return PyUnicode_FromFormat("<compiled_coroutine_wrapper object %s at %p>",
1235                                 Nuitka_String_AsString(cw->m_coroutine->m_qualname), cw);
1236 }
1237 
1238 static PyMethodDef Nuitka_CoroutineWrapper_methods[] = {
1239     {"send", (PyCFunction)Nuitka_CoroutineWrapper_send, METH_O, NULL},
1240     {"throw", (PyCFunction)Nuitka_CoroutineWrapper_throw, METH_VARARGS, NULL},
1241     {"close", (PyCFunction)Nuitka_CoroutineWrapper_close, METH_NOARGS, NULL},
1242     {NULL}};
1243 
1244 PyTypeObject Nuitka_CoroutineWrapper_Type = {
1245     PyVarObject_HEAD_INIT(NULL, 0) "compiled_coroutine_wrapper",
1246     sizeof(struct Nuitka_CoroutineWrapperObject),      /* tp_basicsize */
1247     0,                                                 /* tp_itemsize */
1248     (destructor)Nuitka_CoroutineWrapper_tp_dealloc,    /* tp_dealloc */
1249     0,                                                 /* tp_print */
1250     0,                                                 /* tp_getattr */
1251     0,                                                 /* tp_setattr */
1252     0,                                                 /* tp_as_async */
1253     (reprfunc)Nuitka_CoroutineWrapper_tp_repr,         /* tp_repr */
1254     0,                                                 /* tp_as_number */
1255     0,                                                 /* tp_as_sequence */
1256     0,                                                 /* tp_as_mapping */
1257     0,                                                 /* tp_hash */
1258     0,                                                 /* tp_call */
1259     0,                                                 /* tp_str */
1260     PyObject_GenericGetAttr,                           /* tp_getattro */
1261     0,                                                 /* tp_setattro */
1262     0,                                                 /* tp_as_buffer */
1263     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,           /* tp_flags */
1264     0,                                                 /* tp_doc */
1265     (traverseproc)Nuitka_CoroutineWrapper_tp_traverse, /* tp_traverse */
1266     0,                                                 /* tp_clear */
1267     0,                                                 /* tp_richcompare */
1268     0,                                                 /* tp_weaklistoffset */
1269     PyObject_SelfIter,                                 /* tp_iter */
1270     (iternextfunc)Nuitka_CoroutineWrapper_tp_iternext, /* tp_iternext */
1271     Nuitka_CoroutineWrapper_methods,                   /* tp_methods */
1272     0,                                                 /* tp_members */
1273     0,                                                 /* tp_getset */
1274     0,                                                 /* tp_base */
1275     0,                                                 /* tp_dict */
1276     0,                                                 /* tp_descr_get */
1277     0,                                                 /* tp_descr_set */
1278     0,                                                 /* tp_dictoffset */
1279     0,                                                 /* tp_init */
1280     0,                                                 /* tp_alloc */
1281     0,                                                 /* tp_new */
1282     0,                                                 /* tp_free */
1283 };
1284 
1285 #if PYTHON_VERSION >= 0x370
computeCoroutineOrigin(int origin_depth)1286 static PyObject *computeCoroutineOrigin(int origin_depth) {
1287     PyFrameObject *frame = PyEval_GetFrame();
1288 
1289     int frame_count = 0;
1290 
1291     while (frame != NULL && frame_count < origin_depth) {
1292         frame = frame->f_back;
1293         frame_count += 1;
1294     }
1295 
1296     PyObject *cr_origin = PyTuple_New(frame_count);
1297 
1298     frame = PyEval_GetFrame();
1299 
1300     for (int i = 0; i < frame_count; i++) {
1301         PyObject *frameinfo =
1302             Py_BuildValue("OiO", frame->f_code->co_filename, PyFrame_GetLineNumber(frame), frame->f_code->co_name);
1303 
1304         assert(frameinfo);
1305 
1306         PyTuple_SET_ITEM(cr_origin, i, frameinfo);
1307 
1308         frame = frame->f_back;
1309     }
1310 
1311     return cr_origin;
1312 }
1313 #endif
1314 
Nuitka_Coroutine_New(coroutine_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)1315 PyObject *Nuitka_Coroutine_New(coroutine_code code, PyObject *module, PyObject *name, PyObject *qualname,
1316                                PyCodeObject *code_object, struct Nuitka_CellObject **closure, Py_ssize_t closure_given,
1317                                Py_ssize_t heap_storage_size) {
1318 #if _DEBUG_REFCOUNTS
1319     count_active_Nuitka_Coroutine_Type += 1;
1320     count_allocated_Nuitka_Coroutine_Type += 1;
1321 #endif
1322 
1323     struct Nuitka_CoroutineObject *result;
1324 
1325     // TODO: Change the var part of the type to 1 maybe
1326     Py_ssize_t full_size = closure_given + (heap_storage_size + sizeof(void *) - 1) / sizeof(void *);
1327 
1328     // Macro to assign result memory from GC or free list.
1329     allocateFromFreeList(free_list_coros, struct Nuitka_CoroutineObject, Nuitka_Coroutine_Type, full_size);
1330 
1331     // For quicker access of generator heap.
1332     result->m_heap_storage = &result->m_closure[closure_given];
1333 
1334     result->m_code = (void *)code;
1335 
1336     CHECK_OBJECT(module);
1337     result->m_module = module;
1338 
1339     CHECK_OBJECT(name);
1340     result->m_name = name;
1341     Py_INCREF(name);
1342 
1343     // The "qualname" defaults to NULL for most compact C code.
1344     if (qualname == NULL) {
1345         qualname = name;
1346     }
1347     CHECK_OBJECT(qualname);
1348 
1349     result->m_qualname = qualname;
1350     Py_INCREF(qualname);
1351 
1352     result->m_yieldfrom = NULL;
1353 
1354     memcpy(&result->m_closure[0], closure, closure_given * sizeof(struct Nuitka_CellObject *));
1355     result->m_closure_given = closure_given;
1356 
1357     result->m_weakrefs = NULL;
1358 
1359     result->m_status = status_Unused;
1360     result->m_running = false;
1361     result->m_awaiting = false;
1362 
1363     result->m_yield_return_index = 0;
1364 
1365     result->m_returned = NULL;
1366 
1367     result->m_frame = NULL;
1368     result->m_code_object = code_object;
1369 
1370     result->m_resume_frame = NULL;
1371 
1372 #if PYTHON_VERSION >= 0x370
1373     PyThreadState *tstate = PyThreadState_GET();
1374     int origin_depth = tstate->coroutine_origin_tracking_depth;
1375 
1376     if (origin_depth == 0) {
1377         result->m_origin = NULL;
1378     } else {
1379         result->m_origin = computeCoroutineOrigin(origin_depth);
1380     }
1381 #endif
1382 
1383 #if PYTHON_VERSION >= 0x370
1384     result->m_exc_state.exc_type = NULL;
1385     result->m_exc_state.exc_value = NULL;
1386     result->m_exc_state.exc_traceback = NULL;
1387 #endif
1388 
1389     Nuitka_GC_Track(result);
1390     return (PyObject *)result;
1391 }
1392 
gen_is_coroutine(PyObject * object)1393 static int gen_is_coroutine(PyObject *object) {
1394     if (PyGen_CheckExact(object)) {
1395         PyCodeObject *code = (PyCodeObject *)((PyGenObject *)object)->gi_code;
1396 
1397         if (code->co_flags & CO_ITERABLE_COROUTINE) {
1398             return 1;
1399         }
1400     }
1401 
1402     return 0;
1403 }
1404 
Nuitka_GetAwaitableIter(PyObject * value)1405 static PyObject *Nuitka_GetAwaitableIter(PyObject *value) {
1406     CHECK_OBJECT(value);
1407 
1408 #if _DEBUG_COROUTINE
1409     PRINT_STRING("Nuitka_GetAwaitableIter: Enter ");
1410     PRINT_ITEM(value);
1411     PRINT_NEW_LINE();
1412 #endif
1413 
1414     unaryfunc getter = NULL;
1415 
1416     if (PyCoro_CheckExact(value) || gen_is_coroutine(value)) {
1417         Py_INCREF(value);
1418         return value;
1419     }
1420 
1421     if (Py_TYPE(value)->tp_as_async != NULL) {
1422         getter = Py_TYPE(value)->tp_as_async->am_await;
1423     }
1424 
1425     if (getter != NULL) {
1426         PyObject *result = (*getter)(value);
1427 
1428         if (result != NULL) {
1429             if (unlikely(PyCoro_CheckExact(result) || gen_is_coroutine(result) || Nuitka_Coroutine_Check(result))) {
1430                 Py_DECREF(result);
1431 
1432                 SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_TypeError, "__await__() returned a coroutine");
1433 
1434                 return NULL;
1435             }
1436 
1437             if (unlikely(!HAS_ITERNEXT(result))) {
1438                 PyErr_Format(PyExc_TypeError, "__await__() returned non-iterator of type '%s'",
1439                              Py_TYPE(result)->tp_name);
1440 
1441                 Py_DECREF(result);
1442 
1443                 return NULL;
1444             }
1445         }
1446 
1447         return result;
1448     }
1449 
1450     PyErr_Format(PyExc_TypeError, "object %s can't be used in 'await' expression", Py_TYPE(value)->tp_name);
1451 
1452     return NULL;
1453 }
1454 
1455 #if PYTHON_VERSION >= 0x366
FORMAT_AWAIT_ERROR(PyObject * value,int await_kind)1456 static void FORMAT_AWAIT_ERROR(PyObject *value, int await_kind) {
1457     CHECK_OBJECT(value);
1458 
1459     if (await_kind == await_enter) {
1460         PyErr_Format(PyExc_TypeError,
1461                      "'async with' received an object from __aenter__ that does not implement __await__: %s",
1462                      Py_TYPE(value)->tp_name);
1463     } else if (await_kind == await_exit) {
1464         PyErr_Format(PyExc_TypeError,
1465                      "'async with' received an object from __aexit__ that does not implement __await__: %s",
1466                      Py_TYPE(value)->tp_name);
1467     }
1468 
1469     assert(ERROR_OCCURRED());
1470 }
1471 #endif
1472 
ASYNC_AWAIT(PyObject * awaitable,int await_kind)1473 PyObject *ASYNC_AWAIT(PyObject *awaitable, int await_kind) {
1474     CHECK_OBJECT(awaitable);
1475 
1476 #if _DEBUG_COROUTINE
1477     PRINT_STRING("ASYNC_AWAIT: Enter for awaitable ");
1478     PRINT_STRING(await_kind == await_enter ? "enter" : "exit");
1479     PRINT_STRING(" ");
1480     PRINT_ITEM(awaitable);
1481     PRINT_NEW_LINE();
1482 #endif
1483 
1484     PyObject *awaitable_iter = Nuitka_GetAwaitableIter(awaitable);
1485 
1486     if (unlikely(awaitable_iter == NULL)) {
1487 #if PYTHON_VERSION >= 0x366
1488         FORMAT_AWAIT_ERROR(awaitable, await_kind);
1489 #endif
1490         return NULL;
1491     }
1492 
1493 #if PYTHON_VERSION >= 0x352 || !defined(_NUITKA_FULL_COMPAT)
1494     /* This check got added in Python 3.5.2 only. It's good to do it, but
1495      * not fully compatible, therefore guard it.
1496      */
1497 
1498     if (Nuitka_Coroutine_Check(awaitable)) {
1499         struct Nuitka_CoroutineObject *awaited_coroutine = (struct Nuitka_CoroutineObject *)awaitable;
1500 
1501         if (awaited_coroutine->m_awaiting) {
1502             Py_DECREF(awaitable_iter);
1503 
1504             SET_CURRENT_EXCEPTION_TYPE0_STR(PyExc_RuntimeError, "coroutine is being awaited already");
1505 
1506             return NULL;
1507         }
1508     }
1509 #endif
1510 
1511 #if _DEBUG_COROUTINE
1512     PRINT_STRING("ASYNC_AWAIT: Result ");
1513     PRINT_ITEM(awaitable);
1514     PRINT_NEW_LINE();
1515 #endif
1516 
1517     return awaitable_iter;
1518 }
1519 
1520 #if PYTHON_VERSION >= 0x352
1521 
1522 /* Our "aiter" wrapper clone */
1523 struct Nuitka_AIterWrapper {
1524     /* Python object folklore: */
1525     PyObject_HEAD;
1526 
1527     PyObject *aw_aiter;
1528 };
1529 
Nuitka_AIterWrapper_tp_repr(struct Nuitka_AIterWrapper * aw)1530 static PyObject *Nuitka_AIterWrapper_tp_repr(struct Nuitka_AIterWrapper *aw) {
1531     return PyUnicode_FromFormat("<compiled_aiter_wrapper object of %R at %p>", aw->aw_aiter, aw);
1532 }
1533 
Nuitka_AIterWrapper_iternext(struct Nuitka_AIterWrapper * aw)1534 static PyObject *Nuitka_AIterWrapper_iternext(struct Nuitka_AIterWrapper *aw) {
1535     CHECK_OBJECT(aw);
1536 
1537 #if PYTHON_VERSION < 0x360
1538     SET_CURRENT_EXCEPTION_TYPE0_VALUE0(PyExc_StopIteration, aw->aw_aiter);
1539 #else
1540     if (!PyTuple_Check(aw->aw_aiter) && !PyExceptionInstance_Check(aw->aw_aiter)) {
1541         SET_CURRENT_EXCEPTION_TYPE0_VALUE0(PyExc_StopIteration, aw->aw_aiter);
1542     } else {
1543         PyObject *result = PyObject_CallFunctionObjArgs(PyExc_StopIteration, aw->aw_aiter, NULL);
1544         if (unlikely(result == NULL)) {
1545             return NULL;
1546         }
1547         SET_CURRENT_EXCEPTION_TYPE0_VALUE1(PyExc_StopIteration, result);
1548     }
1549 #endif
1550 
1551     return NULL;
1552 }
1553 
Nuitka_AIterWrapper_traverse(struct Nuitka_AIterWrapper * aw,visitproc visit,void * arg)1554 static int Nuitka_AIterWrapper_traverse(struct Nuitka_AIterWrapper *aw, visitproc visit, void *arg) {
1555     CHECK_OBJECT(aw);
1556 
1557     Py_VISIT((PyObject *)aw->aw_aiter);
1558     return 0;
1559 }
1560 
1561 static struct Nuitka_AIterWrapper *free_list_coroutine_aiter_wrappers = NULL;
1562 static int free_list_coroutine_aiter_wrappers_count = 0;
1563 
Nuitka_AIterWrapper_dealloc(struct Nuitka_AIterWrapper * aw)1564 static void Nuitka_AIterWrapper_dealloc(struct Nuitka_AIterWrapper *aw) {
1565 #if _DEBUG_REFCOUNTS
1566     count_active_Nuitka_AIterWrapper_Type -= 1;
1567     count_released_Nuitka_AIterWrapper_Type += 1;
1568 #endif
1569 
1570     Nuitka_GC_UnTrack((PyObject *)aw);
1571 
1572     CHECK_OBJECT(aw->aw_aiter);
1573     Py_DECREF(aw->aw_aiter);
1574 
1575     /* Put the object into freelist or release to GC */
1576     releaseToFreeList(free_list_coroutine_aiter_wrappers, aw, MAX_COROUTINE_FREE_LIST_COUNT);
1577 }
1578 
1579 static PyAsyncMethods Nuitka_AIterWrapper_as_async = {
1580     PyObject_SelfIter, /* am_await */
1581     0,                 /* am_aiter */
1582     0                  /* am_anext */
1583 };
1584 
1585 PyTypeObject Nuitka_AIterWrapper_Type = {
1586     PyVarObject_HEAD_INIT(NULL, 0) "compiled_aiter_wrapper",
1587     sizeof(struct Nuitka_AIterWrapper),      /* tp_basicsize */
1588     0,                                       /* tp_itemsize */
1589     (destructor)Nuitka_AIterWrapper_dealloc, /* destructor tp_dealloc */
1590     0,                                       /* tp_print */
1591     0,                                       /* tp_getattr */
1592     0,                                       /* tp_setattr */
1593     &Nuitka_AIterWrapper_as_async,           /* tp_as_async */
1594     (reprfunc)Nuitka_AIterWrapper_tp_repr,   /* tp_repr */
1595     0,                                       /* tp_as_number */
1596     0,                                       /* tp_as_sequence */
1597     0,                                       /* tp_as_mapping */
1598     0,                                       /* tp_hash */
1599     0,                                       /* tp_call */
1600     0,                                       /* tp_str */
1601     PyObject_GenericGetAttr,                 /* tp_getattro */
1602     0,                                       /* tp_setattro */
1603     0,                                       /* tp_as_buffer */
1604     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
1605     "A wrapper object for '__aiter__' backwards compatibility.",
1606     (traverseproc)Nuitka_AIterWrapper_traverse, /* tp_traverse */
1607     0,                                          /* tp_clear */
1608     0,                                          /* tp_richcompare */
1609     0,                                          /* tp_weaklistoffset */
1610     PyObject_SelfIter,                          /* tp_iter */
1611     (iternextfunc)Nuitka_AIterWrapper_iternext, /* tp_iternext */
1612     0,                                          /* tp_methods */
1613     0,                                          /* tp_members */
1614     0,                                          /* tp_getset */
1615     0,                                          /* tp_base */
1616     0,                                          /* tp_dict */
1617     0,                                          /* tp_descr_get */
1618     0,                                          /* tp_descr_set */
1619     0,                                          /* tp_dictoffset */
1620     0,                                          /* tp_init */
1621     0,                                          /* tp_alloc */
1622     0,                                          /* tp_new */
1623     0,                                          /* tp_free */
1624 };
1625 
Nuitka_AIterWrapper_New(PyObject * aiter)1626 static PyObject *Nuitka_AIterWrapper_New(PyObject *aiter) {
1627     CHECK_OBJECT(aiter);
1628 
1629 #if _DEBUG_REFCOUNTS
1630     count_active_Nuitka_AIterWrapper_Type += 1;
1631     count_allocated_Nuitka_AIterWrapper_Type += 1;
1632 #endif
1633     struct Nuitka_AIterWrapper *result;
1634 
1635     allocateFromFreeListFixed(free_list_coroutine_aiter_wrappers, struct Nuitka_AIterWrapper, Nuitka_AIterWrapper_Type);
1636 
1637     CHECK_OBJECT(aiter);
1638 
1639     Py_INCREF(aiter);
1640     result->aw_aiter = aiter;
1641 
1642     Nuitka_GC_Track(result);
1643     return (PyObject *)result;
1644 }
1645 
1646 #endif
1647 
ASYNC_MAKE_ITERATOR(PyObject * value)1648 PyObject *ASYNC_MAKE_ITERATOR(PyObject *value) {
1649     CHECK_OBJECT(value);
1650 
1651 #if _DEBUG_COROUTINE
1652     PRINT_STRING("AITER entry:");
1653     PRINT_ITEM(value);
1654 
1655     PRINT_NEW_LINE();
1656 #endif
1657 
1658     unaryfunc getter = NULL;
1659 
1660     if (Py_TYPE(value)->tp_as_async) {
1661         getter = Py_TYPE(value)->tp_as_async->am_aiter;
1662     }
1663 
1664     if (unlikely(getter == NULL)) {
1665         PyErr_Format(PyExc_TypeError, "'async for' requires an object with __aiter__ method, got %s",
1666                      Py_TYPE(value)->tp_name);
1667 
1668         return NULL;
1669     }
1670 
1671     PyObject *iter = (*getter)(value);
1672 
1673     if (unlikely(iter == NULL)) {
1674         return NULL;
1675     }
1676 
1677 #if PYTHON_VERSION >= 0x370
1678     if (unlikely(Py_TYPE(iter)->tp_as_async == NULL || Py_TYPE(iter)->tp_as_async->am_anext == NULL)) {
1679         PyErr_Format(PyExc_TypeError,
1680                      "'async for' received an object from __aiter__ that does not implement __anext__: %s",
1681                      Py_TYPE(iter)->tp_name);
1682 
1683         Py_DECREF(iter);
1684         return NULL;
1685     }
1686 #endif
1687 
1688 #if PYTHON_VERSION >= 0x352
1689     /* Starting with Python 3.5.2 it is acceptable to return an async iterator
1690      * directly, instead of an awaitable.
1691      */
1692     if (Py_TYPE(iter)->tp_as_async != NULL && Py_TYPE(iter)->tp_as_async->am_anext != NULL) {
1693         PyObject *wrapper = Nuitka_AIterWrapper_New(iter);
1694         Py_DECREF(iter);
1695 
1696         iter = wrapper;
1697     }
1698 #endif
1699 
1700     PyObject *awaitable_iter = Nuitka_GetAwaitableIter(iter);
1701 
1702     if (unlikely(awaitable_iter == NULL)) {
1703 #if PYTHON_VERSION >= 0x360
1704         _PyErr_FormatFromCause(
1705 #else
1706         PyErr_Format(
1707 #endif
1708             PyExc_TypeError, "'async for' received an invalid object from __aiter__: %s", Py_TYPE(iter)->tp_name);
1709 
1710         Py_DECREF(iter);
1711 
1712         return NULL;
1713     }
1714 
1715     Py_DECREF(iter);
1716 
1717     return awaitable_iter;
1718 }
1719 
ASYNC_ITERATOR_NEXT(PyObject * value)1720 PyObject *ASYNC_ITERATOR_NEXT(PyObject *value) {
1721     CHECK_OBJECT(value);
1722 
1723 #if _DEBUG_COROUTINE
1724     PRINT_STRING("ANEXT entry:");
1725     PRINT_ITEM(value);
1726 
1727     PRINT_NEW_LINE();
1728 #endif
1729 
1730     unaryfunc getter = NULL;
1731 
1732     if (Py_TYPE(value)->tp_as_async) {
1733         getter = Py_TYPE(value)->tp_as_async->am_anext;
1734     }
1735 
1736     if (unlikely(getter == NULL)) {
1737         PyErr_Format(PyExc_TypeError, "'async for' requires an iterator with __anext__ method, got %s",
1738                      Py_TYPE(value)->tp_name);
1739 
1740         return NULL;
1741     }
1742 
1743     PyObject *next_value = (*getter)(value);
1744 
1745     if (unlikely(next_value == NULL)) {
1746         return NULL;
1747     }
1748 
1749     PyObject *awaitable_iter = Nuitka_GetAwaitableIter(next_value);
1750 
1751     if (unlikely(awaitable_iter == NULL)) {
1752 #if PYTHON_VERSION >= 0x360
1753         _PyErr_FormatFromCause(
1754 #else
1755         PyErr_Format(
1756 #endif
1757             PyExc_TypeError, "'async for' received an invalid object from __anext__: %s", Py_TYPE(next_value)->tp_name);
1758 
1759         Py_DECREF(next_value);
1760 
1761         return NULL;
1762     }
1763 
1764     Py_DECREF(next_value);
1765 
1766     return awaitable_iter;
1767 }
1768 
_initCompiledCoroutineTypes(void)1769 static void _initCompiledCoroutineTypes(void) {
1770     PyType_Ready(&Nuitka_Coroutine_Type);
1771     PyType_Ready(&Nuitka_CoroutineWrapper_Type);
1772 
1773 #if PYTHON_VERSION >= 0x352
1774     PyType_Ready(&Nuitka_AIterWrapper_Type);
1775 #endif
1776 }
1777 
1778 // Chain asyncgen code to coroutine and generator code, as it uses same functions,
1779 // and then we can have some things static if both are in the same compilation unit.
1780 
1781 #if PYTHON_VERSION >= 0x360
1782 #include "CompiledAsyncgenType.c"
1783 #endif
1784