<commit_before>//     Copyright 2016, <NAME>, mailto:<EMAIL>
//
//     Part of "Nuitka", an optimizing Python compiler that is compatible and
//     integrates with CPython, but also works on its own.
//
//     Licensed under the Apache License, Version 2.0 (the "License");
//     you may not use this file except in compliance with the License.
//     You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//     Unless required by applicable law or agreed to in writing, software
//     distributed under the License is distributed on an "AS IS" BASIS,
//     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//     See the License for the specific language governing permissions and
//     limitations under the License.
//
#include "nuitka/prelude.hpp"

static PyObject *Nuitka_Coroutine_get_name( Nuitka_CoroutineObject *coroutine)
{
    return INCREASE_REFCOUNT( coroutine->m_name );
}

static int Nuitka_Coroutine_set_name( Nuitka_CoroutineObject *coroutine, PyObject *value )
{
    // Cannot be deleted, not be non-unicode value.
    if (unlikely( ( value == NULL ) || !PyUnicode_Check( value ) ))
    {
        PyErr_Format(
            PyExc_TypeError,
            "__name__ must be set to a string object"
        );

        return -1;
    }

    PyObject *tmp = coroutine->m_name;
    Py_INCREF( value );
    coroutine->m_name = value;
    Py_DECREF( tmp );

    return 0;
}

static PyObject *Nuitka_Coroutine_get_qualname( Nuitka_CoroutineObject *coroutine )
{
    return INCREASE_REFCOUNT( coroutine->m_qualname );
}

static int Nuitka_Coroutine_set_qualname( Nuitka_CoroutineObject *coroutine, PyObject *value )
{
    // Cannot be deleted, not be non-unicode value.
    if (unlikely( ( value == NULL ) || !PyUnicode_Check( value ) ))
    {
        PyErr_Format(
            PyExc_TypeError,
            "__qualname__ must be set to a string object"
        );

        return -1;
    }

    PyObject *tmp = coroutine->m_qualname;
    Py_INCREF( value );
    coroutine->m_qualname = value;
    Py_DECREF( tmp );

    return 0;
}

static PyObject *Nuitka_Coroutine_get_cr_await( Nuitka_CoroutineObject *coroutine )
{
    if ( coroutine->m_yieldfrom )
    {
        Py_INCREF( coroutine->m_yieldfrom );
        return coroutine->m_yieldfrom;
    }
    else
    {
        Py_INCREF( Py_None );
        return Py_None;
    }
}

static PyObject *Nuitka_Coroutine_get_code( Nuitka_CoroutineObject *coroutine )
{
    return INCREASE_REFCOUNT( (PyObject *)coroutine->m_code_object );
}

static int Nuitka_Coroutine_set_code( Nuitka_CoroutineObject *coroutine, PyObject *value )
{
    PyErr_Format( PyExc_RuntimeError, "cr_code is not writable in Nuitka" );
    return -1;
}

static PyObject *Nuitka_Coroutine_get_frame( Nuitka_CoroutineObject *coroutine )
{
    if ( coroutine->m_frame )
    {
        return INCREASE_REFCOUNT( (PyObject *)coroutine->m_frame );
    }
    else
    {
        return INCREASE_REFCOUNT( Py_None );
    }
}

static int Nuitka_Coroutine_set_frame( Nuitka_CoroutineObject *coroutine, PyObject *value )
{
    PyErr_Format( PyExc_RuntimeError, "gi_frame is not writable in Nuitka" );
    return -1;
}


static void Nuitka_Coroutine_release_closure( Nuitka_CoroutineObject *coroutine )
{
    if ( coroutine->m_closure )
    {
        for( Py_ssize_t i = 0; i < coroutine->m_closure_given; i++ )
        {
            Py_DECREF( coroutine->m_closure[ i ] );
        }

        free( coroutine->m_closure );
        coroutine->m_closure = NULL;
    }
}


// For the coroutine object fiber entry point, we may need to follow what
// "makecontext" will support and that is only a list of integers, but we will need
// to push a pointer through it, and so it's two of them, which might be fully
// sufficient.

#ifdef _NUITKA_MAKECONTEXT_INTS
static void Nuitka_Coroutine_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
    };

    Nuitka_CoroutineObject *coroutine = (Nuitka_CoroutineObject *)*(uintptr_t *)&addresses[0];
#else
static void Nuitka_Coroutine_entry_point( Nuitka_CoroutineObject *coroutine )
{
#endif
    ((coroutine_code)coroutine->m_code)( coroutine );

    swapFiber( &coroutine->m_yielder_context, &coroutine->m_caller_context );
}


static PyObject *Nuitka_Coroutine_send( Nuitka_CoroutineObject *coroutine, PyObject *value )
{
    if ( coroutine->m_status == status_Unused && value != NULL && value != Py_None )
    {
        PyErr_Format( PyExc_TypeError, "can't send non-None value to a just-started coroutine" );
        return NULL;
    }

    if ( coroutine->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 ( coroutine->m_running )
        {
            PyErr_Format( PyExc_ValueError, "coroutine already executing" );
            return NULL;
        }

        if ( coroutine->m_status == status_Unused )
        {
            // Prepare the generator context to run.
            int res = prepareFiber( &coroutine->m_yielder_context, (void *)Nuitka_Coroutine_entry_point, (uintptr_t)coroutine );

            if ( res != 0 )
            {
                PyErr_Format( PyExc_MemoryError, "coroutine cannot be allocated" );
                return NULL;
            }

            coroutine->m_status = status_Running;
        }

        coroutine->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 ( coroutine->m_frame )
        {
            // It would be nice if our frame were still alive. Nobody had the
            // right to release it.
            assertFrameObject( coroutine->m_frame );

            // It's not supposed to be on the top right now.
            assert( return_frame != coroutine->m_frame );

            Py_XINCREF( return_frame );
            coroutine->m_frame->f_back = return_frame;

            thread_state->frame = coroutine->m_frame;
        }

        // Continue the yielder function while preventing recursion.
        coroutine->m_running = true;

        swapFiber( &coroutine->m_caller_context, &coroutine->m_yielder_context );

        coroutine->m_running = false;

        thread_state = PyThreadState_GET();

        // Remove the generator from the frame stack.
        if ( coroutine->m_frame )
        {
            assert( thread_state->frame == coroutine->m_frame );
            assertFrameObject( coroutine->m_frame );

            Py_CLEAR( coroutine->m_frame->f_back );
        }

        thread_state->frame = return_frame;

        if ( coroutine->m_returned != NULL )
        {
            coroutine->m_status = status_Finished;

            Py_XDECREF( coroutine->m_frame );
            coroutine->m_frame = NULL;

            Nuitka_Coroutine_release_closure( coroutine );

            PyObject *result = coroutine->m_returned;

            if ( result == Py_None )
            {
                PyErr_SetObject( PyExc_StopIteration, Py_None );
                Py_DECREF( Py_None );
            }
            else
            {
                PyObject *exc_result  = PyObject_CallFunctionObjArgs( PyExc_StopIteration, result, NULL );
                Py_DECREF( result );

                if (likely( exc_result != NULL ))
                {
                    PyErr_SetObject( PyExc_StopIteration, exc_result );
                    Py_DECREF( exc_result );
                }
            }

            return NULL;
        }
        else if ( coroutine->m_yielded == NULL )
        {
            assert( ERROR_OCCURRED() );

            coroutine->m_status = status_Finished;

            Py_XDECREF( coroutine->m_frame );
            coroutine->m_frame = NULL;

            Nuitka_Coroutine_release_closure( coroutine );

            assert( ERROR_OCCURRED() );

            if ( 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,
                    "coroutine 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 );
            }

            return NULL;
        }
        else
        {
            return coroutine->m_yielded;
        }
    }
    else
    {
        PyErr_SetObject( PyExc_StopIteration, (PyObject *)NULL );

        return NULL;
    }
}

PyObject *Nuitka_Coroutine_close( Nuitka_CoroutineObject *coroutine, PyObject *args )
{
    if ( coroutine->m_status == status_Running )
    {
        coroutine->m_exception_type = INCREASE_REFCOUNT( PyExc_GeneratorExit );
        coroutine->m_exception_value = NULL;
        coroutine->m_exception_tb = NULL;

        PyObject *result = Nuitka_Coroutine_send( coroutine, Py_None );

        if (unlikely( result ))
        {
            Py_DECREF( result );

            PyErr_Format( PyExc_RuntimeError, "coroutine ignored GeneratorExit" );
            return NULL;
        }
        else
        {
            PyObject *error = GET_ERROR_OCCURRED();
            assert( error != NULL );

            if ( EXCEPTION_MATCH_GENERATOR( error ) )
            {
                CLEAR_ERROR_OCCURRED();

                return INCREASE_REFCOUNT( Py_None );
            }

            return NULL;
        }
    }

    return INCREASE_REFCOUNT( Py_None );
}

static PyObject *Nuitka_Coroutine_throw( Nuitka_CoroutineObject *coroutine, PyObject *args )
{
    assert( coroutine->m_exception_type == NULL );
    assert( coroutine->m_exception_value == NULL );
    assert( coroutine->m_exception_tb == NULL );

    int res = PyArg_UnpackTuple( args, "throw", 1, 3, &coroutine->m_exception_type, &coroutine->m_exception_value, (PyObject **)&coroutine->m_exception_tb );

    if (unlikely( res == 0 ))
    {
        coroutine->m_exception_type = NULL;
        coroutine->m_exception_value = NULL;
        coroutine->m_exception_tb = NULL;

        return NULL;
    }

    if ( (PyObject *)coroutine->m_exception_tb == Py_None )
    {
        coroutine->m_exception_tb = NULL;
    }
    else if ( coroutine->m_exception_tb != NULL && !PyTraceBack_Check( coroutine->m_exception_tb ) )
    {
        coroutine->m_exception_type = NULL;
        coroutine->m_exception_value = NULL;
        coroutine->m_exception_tb = NULL;

        PyErr_Format( PyExc_TypeError, "throw() third argument must be a traceback object" );
        return NULL;
    }

    if ( PyExceptionClass_Check( coroutine->m_exception_type ))
    {
        Py_INCREF( coroutine->m_exception_type );
        Py_XINCREF( coroutine->m_exception_value );
        Py_XINCREF( coroutine->m_exception_tb );

        NORMALIZE_EXCEPTION( &coroutine->m_exception_type, &coroutine->m_exception_value, &coroutine->m_exception_tb );
    }
    else if ( PyExceptionInstance_Check( coroutine->m_exception_type ) )
    {
        if ( coroutine->m_exception_value && coroutine->m_exception_value != Py_None )
        {
            coroutine->m_exception_type = NULL;
            coroutine->m_exception_value = NULL;
            coroutine->m_exception_tb = NULL;

            PyErr_Format( PyExc_TypeError, "instance exception may not have a separate value" );
            return NULL;
        }
        coroutine->m_exception_value = coroutine->m_exception_type;
        Py_INCREF( coroutine->m_exception_value );
        coroutine->m_exception_type = PyExceptionInstance_Class( coroutine->m_exception_type );
        Py_INCREF( coroutine->m_exception_type );
        Py_XINCREF( coroutine->m_exception_tb );
    }
    else
    {
        PyErr_Format(
            PyExc_TypeError,
            "exceptions must be classes or instances deriving from BaseException, not %s",
            Py_TYPE( coroutine->m_exception_type )->tp_name
        );

        coroutine->m_exception_type = NULL;
        coroutine->m_exception_value = NULL;
        coroutine->m_exception_tb = NULL;

        return NULL;
    }

    if ( ( coroutine->m_exception_tb != NULL ) && ( (PyObject *)coroutine->m_exception_tb != Py_None ) && ( !PyTraceBack_Check( coroutine->m_exception_tb ) ) )
    {
        PyErr_Format( PyExc_TypeError, "throw() third argument must be a traceback object" );
        return NULL;
    }

    if ( coroutine->m_status != status_Finished )
    {
        PyObject *result = Nuitka_Coroutine_send( coroutine, Py_None );

        return result;
    }
    else
    {
        RESTORE_ERROR_OCCURRED( coroutine->m_exception_type, coroutine->m_exception_value, coroutine->m_exception_tb );

        coroutine->m_exception_type = NULL;
        coroutine->m_exception_value = NULL;
        coroutine->m_exception_tb = NULL;

        return NULL;
    }
}

static void Nuitka_Coroutine_tp_del( Nuitka_CoroutineObject *coroutine )
{
    if ( coroutine->m_status != status_Running )
    {
        return;
    }

    // Revive temporarily.
    assert( Py_REFCNT( coroutine ) == 0 );
    Py_REFCNT( coroutine ) = 1;

    PyObject *error_type, *error_value;
    PyTracebackObject *error_traceback;

    FETCH_ERROR_OCCURRED( &error_type, &error_value, &error_traceback );

    PyObject *close_result = Nuitka_Coroutine_close( coroutine, NULL );

    if (unlikely( close_result == NULL ))
    {
        PyErr_WriteUnraisable( (PyObject *)coroutine );
    }
    else
    {
        Py_DECREF( close_result );
    }

    /* Restore the saved exception. */
    RESTORE_ERROR_OCCURRED( error_type, error_value, error_traceback );

    assert( Py_REFCNT( coroutine ) > 0 );
    Py_REFCNT( coroutine ) -= 1;

    Py_ssize_t refcnt = Py_REFCNT( coroutine );

    if (unlikely( refcnt != 0 ))
    {
        _Py_NewReference( (PyObject *)coroutine );
        Py_REFCNT( coroutine ) = refcnt;

        _Py_DEC_REFTOTAL;
    }
}

static void Nuitka_Coroutine_tp_dealloc( Nuitka_CoroutineObject *coroutine )
{
    assert( Py_REFCNT( coroutine ) == 0 );
    Py_REFCNT( coroutine ) = 1;

    // Save the current exception, if any, we must preserve it.
    PyObject *save_exception_type, *save_exception_value;
    PyTracebackObject *save_exception_tb;
    FETCH_ERROR_OCCURRED( &save_exception_type, &save_exception_value, &save_exception_tb );

    PyObject *close_result = Nuitka_Coroutine_close( coroutine, NULL );

    if (unlikely( close_result == NULL ))
    {<commit_msg>Python3.5: Fix corruption of coroutine reference counts during "finalize".<commit_after>//     Copyright 2016, <NAME>, mailto:<EMAIL>
//
//     Part of "Nuitka", an optimizing Python compiler that is compatible and
//     integrates with CPython, but also works on its own.
//
//     Licensed under the Apache License, Version 2.0 (the "License");
//     you may not use this file except in compliance with the License.
//     You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//     Unless required by applicable law or agreed to in writing, software
//     distributed under the License is distributed on an "AS IS" BASIS,
//     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//     See the License for the specific language governing permissions and
//     limitations under the License.
//
#include "nuitka/prelude.hpp"

static PyObject *Nuitka_Coroutine_get_name( Nuitka_CoroutineObject *coroutine)
{
    return INCREASE_REFCOUNT( coroutine->m_name );
}

static int Nuitka_Coroutine_set_name( Nuitka_CoroutineObject *coroutine, PyObject *value )
{
    // Cannot be deleted, not be non-unicode value.
    if (unlikely( ( value == NULL ) || !PyUnicode_Check( value ) ))
    {
        PyErr_Format(
            PyExc_TypeError,
            "__name__ must be set to a string object"
        );

        return -1;
    }

    PyObject *tmp = coroutine->m_name;
    Py_INCREF( value );
    coroutine->m_name = value;
    Py_DECREF( tmp );

    return 0;
}

static PyObject *Nuitka_Coroutine_get_qualname( Nuitka_CoroutineObject *coroutine )
{
    return INCREASE_REFCOUNT( coroutine->m_qualname );
}

static int Nuitka_Coroutine_set_qualname( Nuitka_CoroutineObject *coroutine, PyObject *value )
{
    // Cannot be deleted, not be non-unicode value.
    if (unlikely( ( value == NULL ) || !PyUnicode_Check( value ) ))
    {
        PyErr_Format(
            PyExc_TypeError,
            "__qualname__ must be set to a string object"
        );

        return -1;
    }

    PyObject *tmp = coroutine->m_qualname;
    Py_INCREF( value );
    coroutine->m_qualname = value;
    Py_DECREF( tmp );

    return 0;
}

static PyObject *Nuitka_Coroutine_get_cr_await( Nuitka_CoroutineObject *coroutine )
{
    if ( coroutine->m_yieldfrom )
    {
        Py_INCREF( coroutine->m_yieldfrom );
        return coroutine->m_yieldfrom;
    }
    else
    {
        Py_INCREF( Py_None );
        return Py_None;
    }
}

static PyObject *Nuitka_Coroutine_get_code( Nuitka_CoroutineObject *coroutine )
{
    return INCREASE_REFCOUNT( (PyObject *)coroutine->m_code_object );
}

static int Nuitka_Coroutine_set_code( Nuitka_CoroutineObject *coroutine, PyObject *value )
{
    PyErr_Format( PyExc_RuntimeError, "cr_code is not writable in Nuitka" );
    return -1;
}

static PyObject *Nuitka_Coroutine_get_frame( Nuitka_CoroutineObject *coroutine )
{
    if ( coroutine->m_frame )
    {
        return INCREASE_REFCOUNT( (PyObject *)coroutine->m_frame );
    }
    else
    {
        return INCREASE_REFCOUNT( Py_None );
    }
}

static int Nuitka_Coroutine_set_frame( Nuitka_CoroutineObject *coroutine, PyObject *value )
{
    PyErr_Format( PyExc_RuntimeError, "gi_frame is not writable in Nuitka" );
    return -1;
}


static void Nuitka_Coroutine_release_closure( Nuitka_CoroutineObject *coroutine )
{
    if ( coroutine->m_closure )
    {
        for( Py_ssize_t i = 0; i < coroutine->m_closure_given; i++ )
        {
            Py_DECREF( coroutine->m_closure[ i ] );
        }

        free( coroutine->m_closure );
        coroutine->m_closure = NULL;
    }
}


// For the coroutine object fiber entry point, we may need to follow what
// "makecontext" will support and that is only a list of integers, but we will need
// to push a pointer through it, and so it's two of them, which might be fully
// sufficient.

#ifdef _NUITKA_MAKECONTEXT_INTS
static void Nuitka_Coroutine_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
    };

    Nuitka_CoroutineObject *coroutine = (Nuitka_CoroutineObject *)*(uintptr_t *)&addresses[0];
#else
static void Nuitka_Coroutine_entry_point( Nuitka_CoroutineObject *coroutine )
{
#endif
    ((coroutine_code)coroutine->m_code)( coroutine );

    swapFiber( &coroutine->m_yielder_context, &coroutine->m_caller_context );
}


static PyObject *Nuitka_Coroutine_send( Nuitka_CoroutineObject *coroutine, PyObject *value )
{
    if ( coroutine->m_status == status_Unused && value != NULL && value != Py_None )
    {
        PyErr_Format( PyExc_TypeError, "can't send non-None value to a just-started coroutine" );
        return NULL;
    }

    if ( coroutine->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 ( coroutine->m_running )
        {
            PyErr_Format( PyExc_ValueError, "coroutine already executing" );
            return NULL;
        }

        if ( coroutine->m_status == status_Unused )
        {
            // Prepare the generator context to run.
            int res = prepareFiber( &coroutine->m_yielder_context, (void *)Nuitka_Coroutine_entry_point, (uintptr_t)coroutine );

            if ( res != 0 )
            {
                PyErr_Format( PyExc_MemoryError, "coroutine cannot be allocated" );
                return NULL;
            }

            coroutine->m_status = status_Running;
        }

        coroutine->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 ( coroutine->m_frame )
        {
            // It would be nice if our frame were still alive. Nobody had the
            // right to release it.
            assertFrameObject( coroutine->m_frame );

            // It's not supposed to be on the top right now.
            assert( return_frame != coroutine->m_frame );

            Py_XINCREF( return_frame );
            coroutine->m_frame->f_back = return_frame;

            thread_state->frame = coroutine->m_frame;
        }

        // Continue the yielder function while preventing recursion.
        coroutine->m_running = true;

        swapFiber( &coroutine->m_caller_context, &coroutine->m_yielder_context );

        coroutine->m_running = false;

        thread_state = PyThreadState_GET();

        // Remove the generator from the frame stack.
        if ( coroutine->m_frame )
        {
            assert( thread_state->frame == coroutine->m_frame );
            assertFrameObject( coroutine->m_frame );

            Py_CLEAR( coroutine->m_frame->f_back );
        }

        thread_state->frame = return_frame;

        if ( coroutine->m_returned != NULL )
        {
            coroutine->m_status = status_Finished;

            Py_XDECREF( coroutine->m_frame );
            coroutine->m_frame = NULL;

            Nuitka_Coroutine_release_closure( coroutine );

            PyObject *result = coroutine->m_returned;

            if ( result == Py_None )
            {
                PyErr_SetObject( PyExc_StopIteration, Py_None );
                Py_DECREF( Py_None );
            }
            else
            {
                PyObject *exc_result  = PyObject_CallFunctionObjArgs( PyExc_StopIteration, result, NULL );
                Py_DECREF( result );

                if (likely( exc_result != NULL ))
                {
                    PyErr_SetObject( PyExc_StopIteration, exc_result );
                    Py_DECREF( exc_result );
                }
            }

            return NULL;
        }
        else if ( coroutine->m_yielded == NULL )
        {
            assert( ERROR_OCCURRED() );

            coroutine->m_status = status_Finished;

            Py_XDECREF( coroutine->m_frame );
            coroutine->m_frame = NULL;

            Nuitka_Coroutine_release_closure( coroutine );

            assert( ERROR_OCCURRED() );

            if ( 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,
                    "coroutine 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 );
            }

            return NULL;
        }
        else
        {
            return coroutine->m_yielded;
        }
    }
    else
    {
        PyErr_SetObject( PyExc_StopIteration, (PyObject *)NULL );

        return NULL;
    }
}

PyObject *Nuitka_Coroutine_close( Nuitka_CoroutineObject *coroutine, PyObject *args )
{
    if ( coroutine->m_status == status_Running )
    {
        coroutine->m_exception_type = INCREASE_REFCOUNT( PyExc_GeneratorExit );
        coroutine->m_exception_value = NULL;
        coroutine->m_exception_tb = NULL;

        PyObject *result = Nuitka_Coroutine_send( coroutine, Py_None );

        if (unlikely( result ))
        {
            Py_DECREF( result );

            PyErr_Format( PyExc_RuntimeError, "coroutine ignored GeneratorExit" );
            return NULL;
        }
        else
        {
            PyObject *error = GET_ERROR_OCCURRED();
            assert( error != NULL );

            if ( EXCEPTION_MATCH_GENERATOR( error ) )
            {
                CLEAR_ERROR_OCCURRED();

                return INCREASE_REFCOUNT( Py_None );
            }

            return NULL;
        }
    }

    return INCREASE_REFCOUNT( Py_None );
}

static PyObject *Nuitka_Coroutine_throw( Nuitka_CoroutineObject *coroutine, PyObject *args )
{
    assert( coroutine->m_exception_type == NULL );
    assert( coroutine->m_exception_value == NULL );
    assert( coroutine->m_exception_tb == NULL );

    int res = PyArg_UnpackTuple( args, "throw", 1, 3, &coroutine->m_exception_type, &coroutine->m_exception_value, (PyObject **)&coroutine->m_exception_tb );

    if (unlikely( res == 0 ))
    {
        coroutine->m_exception_type = NULL;
        coroutine->m_exception_value = NULL;
        coroutine->m_exception_tb = NULL;

        return NULL;
    }

    if ( (PyObject *)coroutine->m_exception_tb == Py_None )
    {
        coroutine->m_exception_tb = NULL;
    }
    else if ( coroutine->m_exception_tb != NULL && !PyTraceBack_Check( coroutine->m_exception_tb ) )
    {
        coroutine->m_exception_type = NULL;
        coroutine->m_exception_value = NULL;
        coroutine->m_exception_tb = NULL;

        PyErr_Format( PyExc_TypeError, "throw() third argument must be a traceback object" );
        return NULL;
    }

    if ( PyExceptionClass_Check( coroutine->m_exception_type ))
    {
        Py_INCREF( coroutine->m_exception_type );
        Py_XINCREF( coroutine->m_exception_value );
        Py_XINCREF( coroutine->m_exception_tb );

        NORMALIZE_EXCEPTION( &coroutine->m_exception_type, &coroutine->m_exception_value, &coroutine->m_exception_tb );
    }
    else if ( PyExceptionInstance_Check( coroutine->m_exception_type ) )
    {
        if ( coroutine->m_exception_value && coroutine->m_exception_value != Py_None )
        {
            coroutine->m_exception_type = NULL;
            coroutine->m_exception_value = NULL;
            coroutine->m_exception_tb = NULL;

            PyErr_Format( PyExc_TypeError, "instance exception may not have a separate value" );
            return NULL;
        }
        coroutine->m_exception_value = coroutine->m_exception_type;
        Py_INCREF( coroutine->m_exception_value );
        coroutine->m_exception_type = PyExceptionInstance_Class( coroutine->m_exception_type );
        Py_INCREF( coroutine->m_exception_type );
        Py_XINCREF( coroutine->m_exception_tb );
    }
    else
    {
        PyErr_Format(
            PyExc_TypeError,
            "exceptions must be classes or instances deriving from BaseException, not %s",
            Py_TYPE( coroutine->m_exception_type )->tp_name
        );

        coroutine->m_exception_type = NULL;
        coroutine->m_exception_value = NULL;
        coroutine->m_exception_tb = NULL;

        return NULL;
    }

    if ( ( coroutine->m_exception_tb != NULL ) && ( (PyObject *)coroutine->m_exception_tb != Py_None ) && ( !PyTraceBack_Check( coroutine->m_exception_tb ) ) )
    {
        PyErr_Format( PyExc_TypeError, "throw() third argument must be a traceback object" );
        return NULL;
    }

    if ( coroutine->m_status != status_Finished )
    {
        PyObject *result = Nuitka_Coroutine_send( coroutine, Py_None );

        return result;
    }
    else
    {
        RESTORE_ERROR_OCCURRED( coroutine->m_exception_type, coroutine->m_exception_value, coroutine->m_exception_tb );

        coroutine->m_exception_type = NULL;
        coroutine->m_exception_value = NULL;
        coroutine->m_exception_tb = NULL;

        return NULL;
    }
}

static void Nuitka_Coroutine_tp_del( Nuitka_CoroutineObject *coroutine )
{
    // Revive temporarily.
    assert( Py_REFCNT( coroutine ) == 0 );
    Py_REFCNT( coroutine ) = 1;

    PyObject *error_type, *error_value;
    PyTracebackObject *error_traceback;

    FETCH_ERROR_OCCURRED( &error_type, &error_value, &error_traceback );

    PyObject *close_result = Nuitka_Coroutine_close( coroutine, NULL );

    if (unlikely( close_result == NULL ))
    {
        PyErr_WriteUnraisable( (PyObject *)coroutine );
    }
    else
    {
        Py_DECREF( close_result );
    }

    /* Restore the saved exception. */
    RESTORE_ERROR_OCCURRED( error_type, error_value, error_traceback );
}

static void Nuitka_Coroutine_tp_dealloc( Nuitka_CoroutineObject *coroutine )
{
    assert( Py_REFCNT( coroutine ) == 0 );
    Py_REFCNT( coroutine ) = 1;

    // Save the current exception, if any, we must preserve it.
    PyObject *save_exception_type, *save_exception_value;
    PyTracebackObject *save_exception_tb;
    FETCH_ERROR_OCCURRED( &save_exception_type, &save_exception_value, &save_exception_tb );

    PyObject *close_result = Nuitka_Coroutine_close( coroutine, NULL );

    if (unlikely( close_result == NULL ))
    {
{'fraction_non_alphanumeric': 0.08969172390002195, 'fraction_numerical': 0.0021638912409445857, 'mean_word_length': 2.412306046013911, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 237, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5520028', 'n_tokens_mistral': 9871, 'n_tokens_neox': 10051, 'n_words': 2104}