示例#1
0
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 );
}
示例#2
0
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 );
        }
    }
}
示例#3
0
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;
    }
}