| // Collects code that was copied in from cpython, for a couple of different reasons: |
| // * We wanted to modify it to produce a more efficient version for our uses |
| // * We needed to call it and it was static :( |
| // * We wanted to call it and needed to backport it |
| |
| #include "pythonsupport.h" |
| |
| ///////////////////////////////////////// |
| // Adapted from bltinmodule.c in Python 3.7.0 |
| PyObject* |
| update_bases(PyObject *bases) |
| { |
| Py_ssize_t i, j; |
| PyObject *base, *meth, *new_base, *result, *new_bases = NULL; |
| PyObject *stack[1] = {bases}; |
| assert(PyTuple_Check(bases)); |
| |
| Py_ssize_t nargs = PyTuple_GET_SIZE(bases); |
| for (i = 0; i < nargs; i++) { |
| base = PyTuple_GET_ITEM(bases, i); |
| if (PyType_Check(base)) { |
| if (new_bases) { |
| /* If we already have made a replacement, then we append every normal base, |
| otherwise just skip it. */ |
| if (PyList_Append(new_bases, base) < 0) { |
| goto error; |
| } |
| } |
| continue; |
| } |
| if (PyObject_GetOptionalAttr(base, mypyc_interned_str.__mro_entries__, &meth) < 0) { |
| goto error; |
| } |
| if (!meth) { |
| if (new_bases) { |
| if (PyList_Append(new_bases, base) < 0) { |
| goto error; |
| } |
| } |
| continue; |
| } |
| new_base = PyObject_Vectorcall(meth, stack, 1, NULL); |
| Py_DECREF(meth); |
| if (!new_base) { |
| goto error; |
| } |
| if (!PyTuple_Check(new_base)) { |
| PyErr_SetString(PyExc_TypeError, |
| "__mro_entries__ must return a tuple"); |
| Py_DECREF(new_base); |
| goto error; |
| } |
| if (!new_bases) { |
| /* If this is a first successful replacement, create new_bases list and |
| copy previously encountered bases. */ |
| if (!(new_bases = PyList_New(i))) { |
| goto error; |
| } |
| for (j = 0; j < i; j++) { |
| base = PyTuple_GET_ITEM(bases, j); |
| PyList_SET_ITEM(new_bases, j, base); |
| Py_INCREF(base); |
| } |
| } |
| j = PyList_GET_SIZE(new_bases); |
| if (PyList_SetSlice(new_bases, j, j, new_base) < 0) { |
| goto error; |
| } |
| Py_DECREF(new_base); |
| } |
| if (!new_bases) { |
| return bases; |
| } |
| result = PyList_AsTuple(new_bases); |
| Py_DECREF(new_bases); |
| return result; |
| |
| error: |
| Py_XDECREF(new_bases); |
| return NULL; |
| } |
| |
| // From Python 3.7's typeobject.c |
| int |
| init_subclass(PyTypeObject *type, PyObject *kwds) |
| { |
| PyObject *super, *func, *result; |
| PyObject *args[2] = {(PyObject *)type, (PyObject *)type}; |
| |
| super = PyObject_Vectorcall((PyObject *)&PySuper_Type, args, 2, NULL); |
| if (super == NULL) { |
| return -1; |
| } |
| |
| func = PyObject_GetAttr(super, mypyc_interned_str.__init_subclass__); |
| Py_DECREF(super); |
| if (func == NULL) { |
| return -1; |
| } |
| |
| result = _PyObject_FastCallDict(func, NULL, 0, kwds); |
| Py_DECREF(func); |
| if (result == NULL) { |
| return -1; |
| } |
| |
| Py_DECREF(result); |
| return 0; |
| } |
| |
| #if CPY_3_12_FEATURES |
| |
| // Slow path of CPyLong_AsSsize_tAndOverflow (non-inlined) |
| Py_ssize_t |
| CPyLong_AsSsize_tAndOverflow_(PyObject *vv, int *overflow) |
| { |
| PyLongObject *v = (PyLongObject *)vv; |
| size_t x, prev; |
| Py_ssize_t res; |
| Py_ssize_t i; |
| int sign; |
| |
| *overflow = 0; |
| |
| res = -1; |
| i = CPY_LONG_TAG(v); |
| |
| sign = 1; |
| x = 0; |
| if (i & CPY_SIGN_NEGATIVE) { |
| sign = -1; |
| } |
| i >>= CPY_NON_SIZE_BITS; |
| while (--i >= 0) { |
| prev = x; |
| x = (x << PyLong_SHIFT) + CPY_LONG_DIGIT(v, i); |
| if ((x >> PyLong_SHIFT) != prev) { |
| *overflow = sign; |
| goto exit; |
| } |
| } |
| /* Haven't lost any bits, but casting to long requires extra |
| * care. |
| */ |
| if (x <= (size_t)CPY_TAGGED_MAX) { |
| res = (Py_ssize_t)x * sign; |
| } |
| else if (sign < 0 && x == CPY_TAGGED_ABS_MIN) { |
| res = CPY_TAGGED_MIN; |
| } |
| else { |
| *overflow = sign; |
| /* res is already set to -1 */ |
| } |
| exit: |
| return res; |
| } |
| |
| #else |
| |
| // Slow path of CPyLong_AsSsize_tAndOverflow (non-inlined, Python 3.11 and earlier) |
| Py_ssize_t |
| CPyLong_AsSsize_tAndOverflow_(PyObject *vv, int *overflow) |
| { |
| /* This version by Tim Peters */ |
| PyLongObject *v = (PyLongObject *)vv; |
| size_t x, prev; |
| Py_ssize_t res; |
| Py_ssize_t i; |
| int sign; |
| |
| *overflow = 0; |
| |
| res = -1; |
| i = Py_SIZE(v); |
| |
| sign = 1; |
| x = 0; |
| if (i < 0) { |
| sign = -1; |
| i = -(i); |
| } |
| while (--i >= 0) { |
| prev = x; |
| x = (x << PyLong_SHIFT) + CPY_LONG_DIGIT(v, i); |
| if ((x >> PyLong_SHIFT) != prev) { |
| *overflow = sign; |
| goto exit; |
| } |
| } |
| /* Haven't lost any bits, but casting to long requires extra |
| * care. |
| */ |
| if (x <= (size_t)CPY_TAGGED_MAX) { |
| res = (Py_ssize_t)x * sign; |
| } |
| else if (sign < 0 && x == CPY_TAGGED_ABS_MIN) { |
| res = CPY_TAGGED_MIN; |
| } |
| else { |
| *overflow = sign; |
| /* res is already set to -1 */ |
| } |
| exit: |
| return res; |
| } |
| |
| |
| #endif |