// @generated by UpstreamBorrow.py.
// See the Buck target fbcode//cinderx/UpstreamBorrow:gen_borrowed.c.

// -*- mode: c++ -*- vi: set ft=cpp:
// Copyright (c) Meta Platforms, Inc. and affiliates.

// This file is processed by UpstreamBorrow.py. To see the generated output:
// buck build -m ovr_config//third-party/python/constraints:3.14 \
//     --out=- fbcode//cinderx/Jit:generators-borrowed_gen_borrowed.c

// clang-format off

#include "cinderx/Jit/generators_borrowed.h"

#include "internal/pycore_object.h"
#include "internal/pycore_genobject.h"

#include "cinderx/Jit/generators_core.h"

#undef _PyObject_GC_TRACK
#define _PyObject_GC_TRACK PyObject_GC_Track

#define anextawaitableobject_CAST(op)   ((anextawaitableobject *)(op))
static void
anextawaitable_dealloc(PyObject *op)
{
    anextawaitableobject *obj = anextawaitableobject_CAST(op);
    _PyObject_GC_UNTRACK(obj);
    Py_XDECREF(obj->wrapped);
    Py_XDECREF(obj->default_value);
    PyObject_GC_Del(obj);
}
void Ci_anextawaitable_dealloc(anextawaitableobject* obj) {
  PyTypeObject *type = Py_TYPE(obj);
  anextawaitable_dealloc((PyObject*)obj);
  // Heap types increment their type, so we need to decrement it here:
  Py_DECREF(type);
}

static int
anextawaitable_traverse(PyObject *op, visitproc visit, void *arg)
{
    anextawaitableobject *obj = anextawaitableobject_CAST(op);
    Py_VISIT(obj->wrapped);
    Py_VISIT(obj->default_value);
    return 0;
}
int Ci_anextawaitable_traverse(anextawaitableobject* obj, visitproc visit, void* arg) {
  return anextawaitable_traverse((PyObject*)obj, visit, arg);
}

// We want to use anextawaitable_getiter but make it aware of JITed co-routines.
// This involves redefining _PyCoro_GetAwaitableIter to be JIT aware and then
// updating PyCoro_CheckExact to also recognize JIT co-routines.
static inline bool coro_inexact(PyObject* obj) {
  return JitCoro_CheckExact(obj) || PyCoro_CheckExact(obj);
}

#define _PyCoro_GetAwaitableIter JitCoro_GetAwaitableIter
#pragma push_macro("PyCoro_CheckExact")
#undef PyCoro_CheckExact
#define PyCoro_CheckExact(OBJ) coro_inexact(OBJ)

static PyObject *
anextawaitable_getiter(anextawaitableobject *obj)
{
    assert(obj->wrapped != NULL);
    PyObject *awaitable = _PyCoro_GetAwaitableIter(obj->wrapped);
    if (awaitable == NULL) {
        return NULL;
    }
    if (Py_TYPE(awaitable)->tp_iternext == NULL) {
        /* _PyCoro_GetAwaitableIter returns a Coroutine, a Generator,
         * or an iterator. Of these, only coroutines lack tp_iternext.
         */
        assert(PyCoro_CheckExact(awaitable));
        unaryfunc getter = Py_TYPE(awaitable)->tp_as_async->am_await;
        PyObject *new_awaitable = getter(awaitable);
        if (new_awaitable == NULL) {
            Py_DECREF(awaitable);
            return NULL;
        }
        Py_SETREF(awaitable, new_awaitable);
        if (!PyIter_Check(awaitable)) {
            PyErr_Format(PyExc_TypeError,
                         "%T.__await__() must return an iterable, not %T",
                         obj, awaitable);
            Py_DECREF(awaitable);
            return NULL;
        }
    }
    return awaitable;
}

#undef _PyCoro_GetAwaitableIter
#pragma pop_macro("PyCoro_CheckExact")

static PyObject *
anextawaitable_proxy(anextawaitableobject *obj, char *meth, PyObject *arg)
{
    PyObject *awaitable = anextawaitable_getiter(obj);
    if (awaitable == NULL) {
        return NULL;
    }
    // When specified, 'arg' may be a tuple (if coming from a METH_VARARGS
    // method) or a single object (if coming from a METH_O method).
    PyObject *ret = arg == NULL
        ? PyObject_CallMethod(awaitable, meth, NULL)
        : PyObject_CallMethod(awaitable, meth, "O", arg);
    Py_DECREF(awaitable);
    if (ret != NULL) {
        return ret;
    }
    if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) {
        /* `anextawaitableobject` is only used by `anext()` when
         * a default value is provided. So when we have a StopAsyncIteration
         * exception we replace it with a `StopIteration(default)`, as if
         * it was the return value of `__anext__()` coroutine.
         */
        PyErr_Clear();
        _PyGen_SetStopIterationValue(obj->default_value);
    }
    return NULL;
}
static PyObject *
anextawaitable_iternext(PyObject *op)
{
    /* Consider the following class:
     *
     *     class A:
     *         async def __anext__(self):
     *             ...
     *     a = A()
     *
     * Then `await anext(a)` should call
     * a.__anext__().__await__().__next__()
     *
     * On the other hand, given
     *
     *     async def agen():
     *         yield 1
     *         yield 2
     *     gen = agen()
     *
     * Then `await anext(gen)` can just call
     * gen.__anext__().__next__()
     */
    anextawaitableobject *obj = anextawaitableobject_CAST(op);
    PyObject *awaitable = anextawaitable_getiter(obj);
    if (awaitable == NULL) {
        return NULL;
    }
    PyObject *result = (*Py_TYPE(awaitable)->tp_iternext)(awaitable);
    Py_DECREF(awaitable);
    if (result != NULL) {
        return result;
    }
    if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) {
        PyErr_Clear();
        _PyGen_SetStopIterationValue(obj->default_value);
    }
    return NULL;
}
PyObject* Ci_anextawaitable_iternext(anextawaitableobject* obj) {
  return anextawaitable_iternext((PyObject*)obj);
}

static PyObject *
anextawaitable_send(PyObject *op, PyObject *arg)
{
    anextawaitableobject *obj = anextawaitableobject_CAST(op);
    return anextawaitable_proxy(obj, "send", arg);
}
PyObject* Ci_anextawaitable_send(anextawaitableobject* obj, PyObject* arg) {
  return anextawaitable_send((PyObject*)obj, arg);
}

static PyObject *
anextawaitable_throw(PyObject *op, PyObject *args)
{
    anextawaitableobject *obj = anextawaitableobject_CAST(op);
    return anextawaitable_proxy(obj, "throw", args);
}
PyObject* Ci_anextawaitable_throw(
    anextawaitableobject* obj,
    PyObject* arg) {
  return anextawaitable_throw((PyObject*)obj, arg);
}

static PyObject *
anextawaitable_close(PyObject *op, PyObject *Py_UNUSED(dummy))
{
    anextawaitableobject *obj = anextawaitableobject_CAST(op);
    return anextawaitable_proxy(obj, "close", NULL);
}
PyObject* Ci_anextawaitable_close(
    anextawaitableobject* obj,
    PyObject* arg) {
    return anextawaitable_close((PyObject*)obj, arg);
}
