
/* Module support implementation */

#include "Python.h"
#include "pycore_abstract.h"   // _PyIndex_Check()
#include "pycore_object.h"     // _PyType_IsReady()
#include "pycore_unicodeobject.h"     // _PyUnicodeWriter_FormatV()

typedef double va_double;

static PyObject *va_build_value(const char *, va_list);


int
_Py_convert_optional_to_ssize_t(PyObject *obj, void *result)
{
    Py_ssize_t limit;
    if (obj == Py_None) {
        return 1;
    }
    else if (_PyIndex_Check(obj)) {
        limit = PyNumber_AsSsize_t(obj, PyExc_OverflowError);
        if (limit == -1 && PyErr_Occurred()) {
            return 0;
        }
    }
    else {
        PyErr_Format(PyExc_TypeError,
                     "argument should be integer or None, not '%.200s'",
                     Py_TYPE(obj)->tp_name);
        return 0;
    }
    *((Py_ssize_t *)result) = limit;
    return 1;
}

int
_Py_convert_optional_to_non_negative_ssize_t(PyObject *obj, void *result)
{
    if (!_Py_convert_optional_to_ssize_t(obj, result)) {
        return 0;
    }
    if (obj != Py_None && *((Py_ssize_t *)result) < 0) {
        PyErr_SetString(PyExc_ValueError, "argument cannot be negative");
        return 0;
    }
    return 1;
}


/* Helper for mkvalue() to scan the length of a format */

static Py_ssize_t
countformat(const char *format, char endchar)
{
    Py_ssize_t count = 0;
    int level = 0;
    while (level > 0 || *format != endchar) {
        switch (*format) {
        case '\0':
            /* Premature end */
            PyErr_SetString(PyExc_SystemError,
                            "unmatched paren in format");
            return -1;
        case '(':
        case '[':
        case '{':
            if (level == 0) {
                count++;
            }
            level++;
            break;
        case ')':
        case ']':
        case '}':
            level--;
            break;
        case '#':
        case '&':
        case ',':
        case ':':
        case ' ':
        case '\t':
            break;
        default:
            if (level == 0) {
                count++;
            }
        }
        format++;
    }
    return count;
}


/* Generic function to create a value -- the inverse of getargs() */
/* After an original idea and first implementation by Steven Miale */

static PyObject *do_mktuple(const char**, va_list *, char, Py_ssize_t);
static int do_mkstack(PyObject **, const char**, va_list *, char, Py_ssize_t);
static PyObject *do_mklist(const char**, va_list *, char, Py_ssize_t);
static PyObject *do_mkdict(const char**, va_list *, char, Py_ssize_t);
static PyObject *do_mkvalue(const char**, va_list *);

static int
check_end(const char **p_format, char endchar)
{
    const char *f = *p_format;
    while (*f != endchar) {
        if (*f != ' ' && *f != '\t' && *f != ',' && *f != ':') {
            PyErr_SetString(PyExc_SystemError,
                            "Unmatched paren in format");
            return 0;
        }
        f++;
    }
    if (endchar) {
        f++;
    }
    *p_format = f;
    return 1;
}

static void
do_ignore(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n)
{
    assert(PyErr_Occurred());
    PyObject *v = PyTuple_New(n);
    for (Py_ssize_t i = 0; i < n; i++) {
        PyObject *exc = PyErr_GetRaisedException();
        PyObject *w = do_mkvalue(p_format, p_va);
        PyErr_SetRaisedException(exc);
        if (w != NULL) {
            if (v != NULL) {
                PyTuple_SET_ITEM(v, i, w);
            }
            else {
                Py_DECREF(w);
            }
        }
    }
    Py_XDECREF(v);
    if (!check_end(p_format, endchar)) {
        return;
    }
}

static PyObject *
do_mkdict(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n)
{
    PyObject *d;
    Py_ssize_t i;
    if (n < 0)
        return NULL;
    if (n % 2) {
        PyErr_SetString(PyExc_SystemError,
                        "Bad dict format");
        do_ignore(p_format, p_va, endchar, n);
        return NULL;
    }
    /* Note that we can't bail immediately on error as this will leak
       refcounts on any 'N' arguments. */
    if ((d = PyDict_New()) == NULL) {
        do_ignore(p_format, p_va, endchar, n);
        return NULL;
    }
    for (i = 0; i < n; i+= 2) {
        PyObject *k, *v;

        k = do_mkvalue(p_format, p_va);
        if (k == NULL) {
            do_ignore(p_format, p_va, endchar, n - i - 1);
            Py_DECREF(d);
            return NULL;
        }
        v = do_mkvalue(p_format, p_va);
        if (v == NULL || PyDict_SetItem(d, k, v) < 0) {
            do_ignore(p_format, p_va, endchar, n - i - 2);
            Py_DECREF(k);
            Py_XDECREF(v);
            Py_DECREF(d);
            return NULL;
        }
        Py_DECREF(k);
        Py_DECREF(v);
    }
    if (!check_end(p_format, endchar)) {
        Py_DECREF(d);
        return NULL;
    }
    return d;
}

static PyObject *
do_mklist(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n)
{
    PyObject *v;
    Py_ssize_t i;
    if (n < 0)
        return NULL;
    /* Note that we can't bail immediately on error as this will leak
       refcounts on any 'N' arguments. */
    v = PyList_New(n);
    if (v == NULL) {
        do_ignore(p_format, p_va, endchar, n);
        return NULL;
    }
    for (i = 0; i < n; i++) {
        PyObject *w = do_mkvalue(p_format, p_va);
        if (w == NULL) {
            do_ignore(p_format, p_va, endchar, n - i - 1);
            Py_DECREF(v);
            return NULL;
        }
        PyList_SET_ITEM(v, i, w);
    }
    if (!check_end(p_format, endchar)) {
        Py_DECREF(v);
        return NULL;
    }
    return v;
}

static int
do_mkstack(PyObject **stack, const char **p_format, va_list *p_va,
           char endchar, Py_ssize_t n)
{
    Py_ssize_t i;

    if (n < 0) {
        return -1;
    }
    /* Note that we can't bail immediately on error as this will leak
       refcounts on any 'N' arguments. */
    for (i = 0; i < n; i++) {
        PyObject *w = do_mkvalue(p_format, p_va);
        if (w == NULL) {
            do_ignore(p_format, p_va, endchar, n - i - 1);
            goto error;
        }
        stack[i] = w;
    }
    if (!check_end(p_format, endchar)) {
        goto error;
    }
    return 0;

error:
    n = i;
    for (i=0; i < n; i++) {
        Py_DECREF(stack[i]);
    }
    return -1;
}

static PyObject *
do_mktuple(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n)
{
    PyObject *v;
    Py_ssize_t i;
    if (n < 0)
        return NULL;
    /* Note that we can't bail immediately on error as this will leak
       refcounts on any 'N' arguments. */
    if ((v = PyTuple_New(n)) == NULL) {
        do_ignore(p_format, p_va, endchar, n);
        return NULL;
    }
    for (i = 0; i < n; i++) {
        PyObject *w = do_mkvalue(p_format, p_va);
        if (w == NULL) {
            do_ignore(p_format, p_va, endchar, n - i - 1);
            Py_DECREF(v);
            return NULL;
        }
        PyTuple_SET_ITEM(v, i, w);
    }
    if (!check_end(p_format, endchar)) {
        Py_DECREF(v);
        return NULL;
    }
    return v;
}

static PyObject *
do_mkvalue(const char **p_format, va_list *p_va)
{
    for (;;) {
        switch (*(*p_format)++) {
        case '(':
            return do_mktuple(p_format, p_va, ')',
                              countformat(*p_format, ')'));

        case '[':
            return do_mklist(p_format, p_va, ']',
                             countformat(*p_format, ']'));

        case '{':
            return do_mkdict(p_format, p_va, '}',
                             countformat(*p_format, '}'));

        case 'b':
        case 'B':
        case 'h':
        case 'i':
            return PyLong_FromLong((long)va_arg(*p_va, int));

        case 'H':
            return PyLong_FromLong((long)va_arg(*p_va, unsigned int));

        case 'I':
        {
            unsigned int n;
            n = va_arg(*p_va, unsigned int);
            return PyLong_FromUnsignedLong(n);
        }

        case 'n':
#if SIZEOF_SIZE_T!=SIZEOF_LONG
            return PyLong_FromSsize_t(va_arg(*p_va, Py_ssize_t));
#endif
            /* Fall through from 'n' to 'l' if Py_ssize_t is long */
            _Py_FALLTHROUGH;
        case 'l':
            return PyLong_FromLong(va_arg(*p_va, long));

        case 'k':
        {
            unsigned long n;
            n = va_arg(*p_va, unsigned long);
            return PyLong_FromUnsignedLong(n);
        }

        case 'L':
            return PyLong_FromLongLong((long long)va_arg(*p_va, long long));

        case 'K':
            return PyLong_FromUnsignedLongLong(
                va_arg(*p_va, unsigned long long));

        case 'u':
        {
            PyObject *v;
            const wchar_t *u = va_arg(*p_va, wchar_t*);
            Py_ssize_t n;
            if (**p_format == '#') {
                ++*p_format;
                n = va_arg(*p_va, Py_ssize_t);
            }
            else
                n = -1;
            if (u == NULL) {
                v = Py_NewRef(Py_None);
            }
            else {
                if (n < 0)
                    n = wcslen(u);
                v = PyUnicode_FromWideChar(u, n);
            }
            return v;
        }
        case 'f':
        case 'd':
            return PyFloat_FromDouble(
                (double)va_arg(*p_va, va_double));

        case 'D':
            return PyComplex_FromCComplex(
                *((Py_complex *)va_arg(*p_va, Py_complex *)));

        case 'c':
        {
            char p[1];
            p[0] = (char)va_arg(*p_va, int);
            return PyBytes_FromStringAndSize(p, 1);
        }
        case 'C':
        {
            int i = va_arg(*p_va, int);
            return PyUnicode_FromOrdinal(i);
        }
        case 'p':
        {
            int i = va_arg(*p_va, int);
            return PyBool_FromLong(i);
        }

        case 's':
        case 'z':
        case 'U':   /* XXX deprecated alias */
        {
            PyObject *v;
            const char *str = va_arg(*p_va, const char *);
            Py_ssize_t n;
            if (**p_format == '#') {
                ++*p_format;
                n = va_arg(*p_va, Py_ssize_t);
            }
            else
                n = -1;
            if (str == NULL) {
                v = Py_NewRef(Py_None);
            }
            else {
                if (n < 0) {
                    size_t m = strlen(str);
                    if (m > PY_SSIZE_T_MAX) {
                        PyErr_SetString(PyExc_OverflowError,
                            "string too long for Python string");
                        return NULL;
                    }
                    n = (Py_ssize_t)m;
                }
                v = PyUnicode_FromStringAndSize(str, n);
            }
            return v;
        }

        case 'y':
        {
            PyObject *v;
            const char *str = va_arg(*p_va, const char *);
            Py_ssize_t n;
            if (**p_format == '#') {
                ++*p_format;
                n = va_arg(*p_va, Py_ssize_t);
            }
            else
                n = -1;
            if (str == NULL) {
                v = Py_NewRef(Py_None);
            }
            else {
                if (n < 0) {
                    size_t m = strlen(str);
                    if (m > PY_SSIZE_T_MAX) {
                        PyErr_SetString(PyExc_OverflowError,
                            "string too long for Python bytes");
                        return NULL;
                    }
                    n = (Py_ssize_t)m;
                }
                v = PyBytes_FromStringAndSize(str, n);
            }
            return v;
        }

        case 'N':
        case 'S':
        case 'O':
        if (**p_format == '&') {
            typedef PyObject *(*converter)(void *);
            converter func = va_arg(*p_va, converter);
            void *arg = va_arg(*p_va, void *);
            ++*p_format;
            return (*func)(arg);
        }
        else {
            PyObject *v;
            v = va_arg(*p_va, PyObject *);
            if (v != NULL) {
                if (*(*p_format - 1) != 'N')
                    Py_INCREF(v);
            }
            else if (!PyErr_Occurred())
                /* If a NULL was passed
                 * because a call that should
                 * have constructed a value
                 * failed, that's OK, and we
                 * pass the error on; but if
                 * no error occurred it's not
                 * clear that the caller knew
                 * what she was doing. */
                PyErr_SetString(PyExc_SystemError,
                    "NULL object passed to Py_BuildValue");
            return v;
        }

        case ':':
        case ',':
        case ' ':
        case '\t':
            break;

        default:
            PyErr_SetString(PyExc_SystemError,
                "bad format char passed to Py_BuildValue");
            return NULL;

        }
    }
}


PyObject *
Py_BuildValue(const char *format, ...)
{
    va_list va;
    PyObject* retval;
    va_start(va, format);
    retval = va_build_value(format, va);
    va_end(va);
    return retval;
}

PyAPI_FUNC(PyObject *) /* abi only */
_Py_BuildValue_SizeT(const char *format, ...)
{
    va_list va;
    PyObject* retval;
    va_start(va, format);
    retval = va_build_value(format, va);
    va_end(va);
    return retval;
}

PyObject *
Py_VaBuildValue(const char *format, va_list va)
{
    return va_build_value(format, va);
}

PyAPI_FUNC(PyObject *) /* abi only */
_Py_VaBuildValue_SizeT(const char *format, va_list va)
{
    return va_build_value(format, va);
}

static PyObject *
va_build_value(const char *format, va_list va)
{
    const char *f = format;
    Py_ssize_t n = countformat(f, '\0');
    va_list lva;
    PyObject *retval;

    if (n < 0)
        return NULL;
    if (n == 0) {
        Py_RETURN_NONE;
    }
    va_copy(lva, va);
    if (n == 1) {
        retval = do_mkvalue(&f, &lva);
    } else {
        retval = do_mktuple(&f, &lva, '\0', n);
    }
    va_end(lva);
    return retval;
}

PyObject **
_Py_VaBuildStack(PyObject **small_stack, Py_ssize_t small_stack_len,
                const char *format, va_list va, Py_ssize_t *p_nargs)
{
    const char *f;
    Py_ssize_t n;
    va_list lva;
    PyObject **stack;
    int res;

    n = countformat(format, '\0');
    if (n < 0) {
        *p_nargs = 0;
        return NULL;
    }

    if (n == 0) {
        *p_nargs = 0;
        return small_stack;
    }

    if (n <= small_stack_len) {
        stack = small_stack;
    }
    else {
        stack = PyMem_Malloc(n * sizeof(stack[0]));
        if (stack == NULL) {
            PyErr_NoMemory();
            return NULL;
        }
    }

    va_copy(lva, va);
    f = format;
    res = do_mkstack(stack, &f, &lva, '\0', n);
    va_end(lva);

    if (res < 0) {
        if (stack != small_stack) {
            PyMem_Free(stack);
        }
        return NULL;
    }

    *p_nargs = n;
    return stack;
}


int
PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value)
{
    if (!PyModule_Check(mod)) {
        PyErr_SetString(PyExc_TypeError,
                        "PyModule_AddObjectRef() first argument "
                        "must be a module");
        return -1;
    }
    if (!value) {
        if (!PyErr_Occurred()) {
            PyErr_SetString(PyExc_SystemError,
                            "PyModule_AddObjectRef() must be called "
                            "with an exception raised if value is NULL");
        }
        return -1;
    }

    PyObject *dict = PyModule_GetDict(mod);
    if (dict == NULL) {
        /* Internal error -- modules must have a dict! */
        PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__",
                     PyModule_GetName(mod));
        return -1;
    }
    return PyDict_SetItemString(dict, name, value);
}

int
PyModule_Add(PyObject *mod, const char *name, PyObject *value)
{
    int res = PyModule_AddObjectRef(mod, name, value);
    Py_XDECREF(value);
    return res;
}

int
PyModule_AddObject(PyObject *mod, const char *name, PyObject *value)
{
    int res = PyModule_AddObjectRef(mod, name, value);
    if (res == 0) {
        Py_DECREF(value);
    }
    return res;
}

int
PyModule_AddIntConstant(PyObject *m, const char *name, long value)
{
    return PyModule_Add(m, name, PyLong_FromLong(value));
}

int
PyModule_AddStringConstant(PyObject *m, const char *name, const char *value)
{
    return PyModule_Add(m, name, PyUnicode_FromString(value));
}

int
PyModule_AddType(PyObject *module, PyTypeObject *type)
{
    if (!_PyType_IsReady(type) && PyType_Ready(type) < 0) {
        return -1;
    }

    const char *name = _PyType_Name(type);
    assert(name != NULL);

    return PyModule_AddObjectRef(module, name, (PyObject *)type);
}

static int _abiinfo_raise(const char *module_name, const char *format, ...)
{
    PyUnicodeWriter *writer = PyUnicodeWriter_Create(0);
    if (!writer) {
        return -1;
    }
    if (module_name) {
        if (PyUnicodeWriter_WriteUTF8(writer, module_name, -1) < 0) {
            PyUnicodeWriter_Discard(writer);
            return -1;
        }
        if (PyUnicodeWriter_WriteASCII(writer, ": ", 2) < 0) {
            PyUnicodeWriter_Discard(writer);
            return -1;
        }
    }
    va_list vargs;
    va_start(vargs, format);
    if (_PyUnicodeWriter_FormatV(writer, format, vargs) < 0) {
        va_end(vargs);
        PyUnicodeWriter_Discard(writer);
        return -1;
    }

    va_end(vargs);
    PyObject *message = PyUnicodeWriter_Finish(writer);
    if (!message) {
        return -1;
    }
    PyErr_SetObject(PyExc_ImportError, message);
    Py_DECREF(message);
    return -1;
}

int PyABIInfo_Check(PyABIInfo *info, const char *module_name)
{
    if (!info) {
        return _abiinfo_raise(module_name, "NULL PyABIInfo");
    }

    /* abiinfo_major_version */
    if (info->abiinfo_major_version == 0) {
        return 0;
    }
    if (info->abiinfo_major_version > 1) {
        return _abiinfo_raise(module_name, "PyABIInfo version too high");
    }

    /* Internal ABI */
    if (info->flags & PyABIInfo_INTERNAL) {
        if (info->abi_version && (info->abi_version != PY_VERSION_HEX)) {
            return _abiinfo_raise(
                module_name,
                "incompatible internal ABI (0x%x != 0x%x)",
                info->abi_version, PY_VERSION_HEX);
        }
    }

#define XY_MASK 0xffff0000
    if (info->flags & PyABIInfo_STABLE) {
        /* Greater-than major.minor version check */
        if (info->abi_version) {
            if ((info->abi_version & XY_MASK) > (PY_VERSION_HEX & XY_MASK)) {
                return _abiinfo_raise(
                    module_name,
                    "incompatible future stable ABI version (%d.%d)",
                    ((info->abi_version) >> 24) & 0xff,
                    ((info->abi_version) >> 16) & 0xff);
            }
            if (info->abi_version < Py_PACK_VERSION(3, 2)) {
                return _abiinfo_raise(
                    module_name,
                    "invalid stable ABI version (%d.%d)",
                    ((info->abi_version) >> 24) & 0xff,
                    ((info->abi_version) >> 16) & 0xff);
            }
        }
        if (info->flags & PyABIInfo_INTERNAL) {
            return _abiinfo_raise(module_name,
                                  "cannot use both internal and stable ABI");
        }
    }
    else {
        /* Exact major.minor version check */
        if (info->abi_version) {
            if ((info->abi_version & XY_MASK) != (PY_VERSION_HEX & XY_MASK)) {
                return _abiinfo_raise(
                    module_name,
                    "incompatible ABI version (%d.%d)",
                    ((info->abi_version) >> 24) & 0xff,
                    ((info->abi_version) >> 16) & 0xff);
            }
        }
    }
#undef XY_MASK

    /* Free-threading/GIL */
    uint16_t gilflags = info->flags & (PyABIInfo_GIL | PyABIInfo_FREETHREADED);
#if Py_GIL_DISABLED
    if (gilflags == PyABIInfo_GIL) {
        return _abiinfo_raise(module_name,
                              "incompatible with free-threaded CPython");
    }
#else
    if (gilflags == PyABIInfo_FREETHREADED) {
        return _abiinfo_raise(module_name,
                              "only compatible with free-threaded CPython");
    }
#endif

    return 0;
}


/* Exported functions for version helper macros */

#undef Py_PACK_FULL_VERSION
uint32_t
Py_PACK_FULL_VERSION(int x, int y, int z, int level, int serial)
{
    return _Py_PACK_FULL_VERSION(x, y, z, level, serial);
}

#undef Py_PACK_VERSION
uint32_t
Py_PACK_VERSION(int x, int y)
{
    return _Py_PACK_VERSION(x, y);
}
