static void Nuitka_Generator_entry_point( int address_1, int address_2 ) { // Restore the pointer from integers should it be necessary, depending on // the platform. This requires pointers to be no larger that to "int" value. int addresses[2] = { address_1, address_2 }; struct Nuitka_GeneratorObject *generator = (struct Nuitka_GeneratorObject *)*(uintptr_t *)&addresses[0]; #else static void Nuitka_Generator_entry_point( struct Nuitka_GeneratorObject *generator ) { #endif ((generator_code)generator->m_code)( generator ); swapFiber( &generator->m_yielder_context, &generator->m_caller_context ); }
static PyObject *_YIELD_FROM( struct Nuitka_GeneratorObject *generator, PyObject *value ) { // This is the value, propagated back and forth the sub-generator and the // yield from consumer. PyObject *send_value = Py_None; while( 1 ) { // Send iteration value to the sub-generator, which may be a CPython // generator object, something with an iterator next, or a send method, // where the later is only required if values other than "None" need to // be passed in. PyObject *retval; // Exception, was thrown into us, need to send that to sub-generator. if ( generator->m_exception_type ) { // The yielding generator is being closed, but we also are tasked to // immediately close the currently running sub-generator. if ( EXCEPTION_MATCH_BOOL_SINGLE( generator->m_exception_type, PyExc_GeneratorExit ) ) { PyObject *close_method = PyObject_GetAttr( value, const_str_plain_close ); if ( close_method ) { PyObject *close_value = PyObject_Call( close_method, const_tuple_empty, NULL ); Py_DECREF( close_method ); if (unlikely( close_value == NULL )) { return NULL; } Py_DECREF( close_value ); } else { PyObject *error = GET_ERROR_OCCURRED(); if ( error != NULL && !EXCEPTION_MATCH_BOOL_SINGLE( error, PyExc_AttributeError ) ) { PyErr_WriteUnraisable( (PyObject *)value ); } } RAISE_GENERATOR_EXCEPTION( generator ); return NULL; } PyObject *throw_method = PyObject_GetAttr( value, const_str_plain_throw ); if ( throw_method ) { retval = PyObject_CallFunctionObjArgs( throw_method, generator->m_exception_type, generator->m_exception_value, generator->m_exception_tb, NULL ); Py_DECREF( throw_method ); if (unlikely( send_value == NULL )) { if ( EXCEPTION_MATCH_BOOL_SINGLE( GET_ERROR_OCCURRED(), PyExc_StopIteration ) ) { return ERROR_GET_STOP_ITERATION_VALUE(); } return NULL; } generator->m_exception_type = NULL; generator->m_exception_value = NULL; generator->m_exception_tb = NULL; } else if ( EXCEPTION_MATCH_BOOL_SINGLE( GET_ERROR_OCCURRED(), PyExc_AttributeError ) ) { CLEAR_ERROR_OCCURRED(); RAISE_GENERATOR_EXCEPTION( generator ); return NULL; } else { assert( ERROR_OCCURRED() ); Py_CLEAR( generator->m_exception_type ); Py_CLEAR( generator->m_exception_value ); Py_CLEAR( generator->m_exception_tb ); return NULL; } } else if ( PyGen_CheckExact( value ) ) { retval = PyGen_Send( (PyGenObject *)value, Py_None ); } else if ( send_value == Py_None && Py_TYPE( value )->tp_iternext != NULL ) { retval = Py_TYPE( value )->tp_iternext( value ); } else { // Bug compatibility here, before 3.3 tuples were unrolled in calls, which is what // PyObject_CallMethod does. #if PYTHON_VERSION >= 340 retval = PyObject_CallMethodObjArgs( value, const_str_plain_send, send_value, NULL ); #else retval = PyObject_CallMethod( value, (char *)"send", (char *)"O", send_value ); #endif } // Check the sub-generator result if ( retval == NULL ) { PyObject *error = GET_ERROR_OCCURRED(); if ( error == NULL ) { return INCREASE_REFCOUNT( Py_None ) ; } // The sub-generator has given an exception. In case of // StopIteration, we need to check the value, as it is going to be // the expression value of this "yield from", and we are done. All // other errors, we need to raise. if (likely( EXCEPTION_MATCH_BOOL_SINGLE( error, PyExc_StopIteration ) )) { return ERROR_GET_STOP_ITERATION_VALUE(); } return NULL; } else { generator->m_yielded = retval; #if PYTHON_VERSION >= 350 generator->m_yieldfrom = value; #endif // Return to the calling context. swapFiber( &generator->m_yielder_context, &generator->m_caller_context ); #if PYTHON_VERSION >= 350 generator->m_yieldfrom = NULL; #endif send_value = generator->m_yielded; CHECK_OBJECT( send_value ); } } }
static PyObject *Nuitka_Generator_send( struct Nuitka_GeneratorObject *generator, PyObject *value ) { if ( generator->m_status == status_Unused && value != NULL && value != Py_None ) { PyErr_Format( PyExc_TypeError, "can't send non-None value to a just-started generator" ); return NULL; } if ( generator->m_status != status_Finished ) { PyThreadState *thread_state = PyThreadState_GET(); #if PYTHON_VERSION < 300 PyObject *saved_exception_type = thread_state->exc_type; Py_XINCREF( saved_exception_type ); PyObject *saved_exception_value = thread_state->exc_value; Py_XINCREF( saved_exception_value ); PyTracebackObject *saved_exception_traceback = (PyTracebackObject *)thread_state->exc_traceback; Py_XINCREF( saved_exception_traceback ); #endif if ( generator->m_running ) { PyErr_Format( PyExc_ValueError, "generator already executing" ); return NULL; } if ( generator->m_status == status_Unused ) { // Prepare the generator context to run. int res = prepareFiber( &generator->m_yielder_context, (void *)Nuitka_Generator_entry_point, (uintptr_t)generator ); if ( res != 0 ) { PyErr_Format( PyExc_MemoryError, "generator cannot be allocated" ); return NULL; } generator->m_status = status_Running; } generator->m_yielded = value; // Put the generator back on the frame stack. PyFrameObject *return_frame = thread_state->frame; #ifndef __NUITKA_NO_ASSERT__ if ( return_frame ) { assertFrameObject( return_frame ); } #endif if ( generator->m_frame ) { // It would be nice if our frame were still alive. Nobody had the // right to release it. assertFrameObject( generator->m_frame ); // It's not supposed to be on the top right now. assert( return_frame != generator->m_frame ); Py_XINCREF( return_frame ); generator->m_frame->f_back = return_frame; thread_state->frame = generator->m_frame; } // Continue the yielder function while preventing recursion. generator->m_running = true; swapFiber( &generator->m_caller_context, &generator->m_yielder_context ); generator->m_running = false; thread_state = PyThreadState_GET(); // Remove the generator from the frame stack. if ( generator->m_frame ) { assert( thread_state->frame == generator->m_frame ); assertFrameObject( generator->m_frame ); Py_CLEAR( generator->m_frame->f_back ); } thread_state->frame = return_frame; if ( generator->m_yielded == NULL ) { assert( ERROR_OCCURRED() ); generator->m_status = status_Finished; Py_XDECREF( generator->m_frame ); generator->m_frame = NULL; Nuitka_Generator_release_closure( generator ); assert( ERROR_OCCURRED() ); #if PYTHON_VERSION < 300 Py_XDECREF( saved_exception_type ); Py_XDECREF( saved_exception_value ); Py_XDECREF( saved_exception_traceback ); #endif #if PYTHON_VERSION >= 350 if ( generator->m_code_object->co_flags & CO_FUTURE_GENERATOR_STOP && GET_ERROR_OCCURRED() == PyExc_StopIteration ) { PyObject *saved_exception_type, *saved_exception_value; PyTracebackObject *saved_exception_tb; FETCH_ERROR_OCCURRED( &saved_exception_type, &saved_exception_value, &saved_exception_tb ); NORMALIZE_EXCEPTION( &saved_exception_type, &saved_exception_value, &saved_exception_tb ); PyErr_Format( PyExc_RuntimeError, "generator raised StopIteration" ); PyObject *exception_type, *exception_value; PyTracebackObject *exception_tb; FETCH_ERROR_OCCURRED( &exception_type, &exception_value, &exception_tb ); RAISE_EXCEPTION_WITH_CAUSE( &exception_type, &exception_value, &exception_tb, saved_exception_value ); CHECK_OBJECT( exception_value ); CHECK_OBJECT( saved_exception_value ); Py_INCREF( saved_exception_value ); PyException_SetContext( exception_value, saved_exception_value ); Py_DECREF( saved_exception_type ); Py_XDECREF( saved_exception_tb ); RESTORE_ERROR_OCCURRED( exception_type, exception_value, exception_tb ); } #endif return NULL; } else { #if PYTHON_VERSION < 300 SET_CURRENT_EXCEPTION( saved_exception_type, saved_exception_value, saved_exception_traceback ); #endif return generator->m_yielded; } } else { PyErr_SetObject( PyExc_StopIteration, (PyObject *)NULL ); return NULL; } }
static PyObject *Nuitka_Generator_send( Nuitka_GeneratorObject *generator, PyObject *value ) { if ( generator->m_status == status_Unused && value != NULL && value != Py_None ) { PyErr_Format( PyExc_TypeError, "can't send non-None value to a just-started generator" ); return NULL; } if ( generator->m_status != status_Finished ) { PyThreadState *thread_state = PyThreadState_GET(); #if PYTHON_VERSION < 300 PyObject *saved_exception_type = INCREASE_REFCOUNT_X( thread_state->exc_type ); PyObject *saved_exception_value = INCREASE_REFCOUNT_X( thread_state->exc_value ); PyTracebackObject *saved_exception_traceback = INCREASE_REFCOUNT_X( (PyTracebackObject *)thread_state->exc_traceback ); #endif if ( generator->m_running ) { PyErr_Format( PyExc_ValueError, "generator already executing" ); return NULL; } if ( generator->m_status == status_Unused ) { generator->m_status = status_Running; // Prepare the generator context to run. prepareFiber( &generator->m_yielder_context, generator->m_code, (intptr_t)generator ); } generator->m_yielded = value; // Put the generator back on the frame stack. PyFrameObject *return_frame = thread_state->frame; #ifndef __NUITKA_NO_ASSERT__ if ( return_frame ) { assertFrameObject( return_frame ); } #endif if ( generator->m_frame ) { // It would be nice if our frame were still alive. Nobody had the // right to release it. assertFrameObject( generator->m_frame ); // It's not supposed to be on the top right now. assert( return_frame != generator->m_frame ); Py_XINCREF( return_frame ); generator->m_frame->f_back = return_frame; thread_state->frame = generator->m_frame; } // Continue the yielder function while preventing recursion. generator->m_running = true; swapFiber( &generator->m_caller_context, &generator->m_yielder_context ); generator->m_running = false; thread_state = PyThreadState_GET(); // Remove the generator from the frame stack. if ( generator->m_frame ) { assert( thread_state->frame == generator->m_frame ); assertFrameObject( generator->m_frame ); Py_CLEAR( generator->m_frame->f_back ); } thread_state->frame = return_frame; if ( generator->m_yielded == NULL ) { assert( ERROR_OCCURED() ); generator->m_status = status_Finished; Py_XDECREF( generator->m_frame ); generator->m_frame = NULL; if ( generator->m_context ) { // Surpressing exception in cleanup, to restore later before // return. generator->m_cleanup( generator->m_context ); generator->m_context = NULL; } assert( ERROR_OCCURED() ); #if PYTHON_VERSION < 300 Py_XDECREF( saved_exception_type ); Py_XDECREF( saved_exception_value ); Py_XDECREF( saved_exception_traceback ); #endif return NULL; } else { #if PYTHON_VERSION < 300 SET_CURRENT_EXCEPTION( saved_exception_type, saved_exception_value, saved_exception_traceback ); #endif return generator->m_yielded; } } else { PyErr_SetObject( PyExc_StopIteration, (PyObject *)NULL ); return NULL; } }