| // 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 |
| |
| #ifndef CPY_PYTHONSUPPORT_H |
| #define CPY_PYTHONSUPPORT_H |
| |
| #include <stdbool.h> |
| #include <Python.h> |
| #include <frameobject.h> |
| #include <assert.h> |
| #include "mypyc_util.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| #if 0 |
| } // why isn't emacs smart enough to not indent this |
| #endif |
| |
| ///////////////////////////////////////// |
| // Adapted from bltinmodule.c in Python 3.7.0 |
| #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 7 |
| _Py_IDENTIFIER(__mro_entries__); |
| static 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_LookupAttrId(base, &PyId___mro_entries__, &meth) < 0) { |
| goto error; |
| } |
| if (!meth) { |
| if (new_bases) { |
| if (PyList_Append(new_bases, base) < 0) { |
| goto error; |
| } |
| } |
| continue; |
| } |
| new_base = _PyObject_FastCall(meth, stack, 1); |
| 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; |
| } |
| #else |
| static PyObject* |
| update_bases(PyObject *bases) |
| { |
| return bases; |
| } |
| #endif |
| |
| // From Python 3.7's typeobject.c |
| #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 6 |
| _Py_IDENTIFIER(__init_subclass__); |
| static int |
| init_subclass(PyTypeObject *type, PyObject *kwds) |
| { |
| PyObject *super, *func, *result; |
| PyObject *args[2] = {(PyObject *)type, (PyObject *)type}; |
| |
| super = _PyObject_FastCall((PyObject *)&PySuper_Type, args, 2); |
| if (super == NULL) { |
| return -1; |
| } |
| |
| func = _PyObject_GetAttrId(super, &PyId___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; |
| } |
| |
| #else |
| static int |
| init_subclass(PyTypeObject *type, PyObject *kwds) |
| { |
| return 0; |
| } |
| #endif |
| |
| // Adapted from longobject.c in Python 3.7.0 |
| |
| /* This function adapted from PyLong_AsLongLongAndOverflow, but with |
| * some safety checks removed and specialized to only work for objects |
| * that are already longs. |
| * About half of the win this provides, though, just comes from being |
| * able to inline the function, which in addition to saving function call |
| * overhead allows the out-parameter overflow flag to be collapsed into |
| * control flow. |
| * Additionally, we check against the possible range of CPyTagged, not of |
| * Py_ssize_t. */ |
| static inline 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); |
| |
| if (likely(i == 1)) { |
| res = v->ob_digit[0]; |
| } else if (likely(i == 0)) { |
| res = 0; |
| } else if (i == -1) { |
| res = -(sdigit)v->ob_digit[0]; |
| } else { |
| sign = 1; |
| x = 0; |
| if (i < 0) { |
| sign = -1; |
| i = -(i); |
| } |
| while (--i >= 0) { |
| prev = x; |
| x = (x << PyLong_SHIFT) + v->ob_digit[i]; |
| if ((x >> PyLong_SHIFT) != prev) { |
| *overflow = sign; |
| goto exit; |
| } |
| } |
| /* Haven't lost any bits, but casting to long requires extra |
| * care (see comment above). |
| */ |
| 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; |
| } |
| |
| // Adapted from listobject.c in Python 3.7.0 |
| static int |
| list_resize(PyListObject *self, Py_ssize_t newsize) |
| { |
| PyObject **items; |
| size_t new_allocated, num_allocated_bytes; |
| Py_ssize_t allocated = self->allocated; |
| |
| /* Bypass realloc() when a previous overallocation is large enough |
| to accommodate the newsize. If the newsize falls lower than half |
| the allocated size, then proceed with the realloc() to shrink the list. |
| */ |
| if (allocated >= newsize && newsize >= (allocated >> 1)) { |
| assert(self->ob_item != NULL || newsize == 0); |
| Py_SIZE(self) = newsize; |
| return 0; |
| } |
| |
| /* This over-allocates proportional to the list size, making room |
| * for additional growth. The over-allocation is mild, but is |
| * enough to give linear-time amortized behavior over a long |
| * sequence of appends() in the presence of a poorly-performing |
| * system realloc(). |
| * The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ... |
| * Note: new_allocated won't overflow because the largest possible value |
| * is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t. |
| */ |
| new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6); |
| if (new_allocated > (size_t)PY_SSIZE_T_MAX / sizeof(PyObject *)) { |
| PyErr_NoMemory(); |
| return -1; |
| } |
| |
| if (newsize == 0) |
| new_allocated = 0; |
| num_allocated_bytes = new_allocated * sizeof(PyObject *); |
| items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes); |
| if (items == NULL) { |
| PyErr_NoMemory(); |
| return -1; |
| } |
| self->ob_item = items; |
| Py_SIZE(self) = newsize; |
| self->allocated = new_allocated; |
| return 0; |
| } |
| |
| // Changed to use PyList_SetSlice instead of the internal list_ass_slice |
| static PyObject * |
| list_pop_impl(PyListObject *self, Py_ssize_t index) |
| { |
| PyObject *v; |
| int status; |
| |
| if (Py_SIZE(self) == 0) { |
| /* Special-case most common failure cause */ |
| PyErr_SetString(PyExc_IndexError, "pop from empty list"); |
| return NULL; |
| } |
| if (index < 0) |
| index += Py_SIZE(self); |
| if (index < 0 || index >= Py_SIZE(self)) { |
| PyErr_SetString(PyExc_IndexError, "pop index out of range"); |
| return NULL; |
| } |
| v = self->ob_item[index]; |
| if (index == Py_SIZE(self) - 1) { |
| status = list_resize(self, Py_SIZE(self) - 1); |
| if (status >= 0) |
| return v; /* and v now owns the reference the list had */ |
| else |
| return NULL; |
| } |
| Py_INCREF(v); |
| status = PyList_SetSlice((PyObject *)self, index, index+1, (PyObject *)NULL); |
| if (status < 0) { |
| Py_DECREF(v); |
| return NULL; |
| } |
| return v; |
| } |
| |
| // Tweaked to directly use CPyTagged |
| static CPyTagged |
| list_count(PyListObject *self, PyObject *value) |
| { |
| Py_ssize_t count = 0; |
| Py_ssize_t i; |
| |
| for (i = 0; i < Py_SIZE(self); i++) { |
| int cmp = PyObject_RichCompareBool(self->ob_item[i], value, Py_EQ); |
| if (cmp > 0) |
| count++; |
| else if (cmp < 0) |
| return CPY_INT_TAG; |
| } |
| return CPyTagged_ShortFromSsize_t(count); |
| } |
| |
| #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION < 8 |
| static PyObject * |
| _PyDict_GetItemStringWithError(PyObject *v, const char *key) |
| { |
| PyObject *kv, *rv; |
| kv = PyUnicode_FromString(key); |
| if (kv == NULL) { |
| return NULL; |
| } |
| rv = PyDict_GetItemWithError(v, kv); |
| Py_DECREF(kv); |
| return rv; |
| } |
| #endif |
| |
| #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION < 6 |
| /* _PyUnicode_EqualToASCIIString got added in 3.5.3 (argh!) so we can't actually know |
| * whether it will be precent at runtime, so we just assume we don't have it in 3.5. */ |
| #define CPyUnicode_EqualToASCIIString(x, y) (PyUnicode_CompareWithASCIIString((x), (y)) == 0) |
| #elif PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 6 |
| #define CPyUnicode_EqualToASCIIString(x, y) _PyUnicode_EqualToASCIIString(x, y) |
| #endif |
| |
| // Adapted from genobject.c in Python 3.7.2 |
| // Copied because it wasn't in 3.5.2 and it is undocumented anyways. |
| /* |
| * Set StopIteration with specified value. Value can be arbitrary object |
| * or NULL. |
| * |
| * Returns 0 if StopIteration is set and -1 if any other exception is set. |
| */ |
| static int |
| CPyGen_SetStopIterationValue(PyObject *value) |
| { |
| PyObject *e; |
| |
| if (value == NULL || |
| (!PyTuple_Check(value) && !PyExceptionInstance_Check(value))) |
| { |
| /* Delay exception instantiation if we can */ |
| PyErr_SetObject(PyExc_StopIteration, value); |
| return 0; |
| } |
| /* Construct an exception instance manually with |
| * PyObject_CallFunctionObjArgs and pass it to PyErr_SetObject. |
| * |
| * We do this to handle a situation when "value" is a tuple, in which |
| * case PyErr_SetObject would set the value of StopIteration to |
| * the first element of the tuple. |
| * |
| * (See PyErr_SetObject/_PyErr_CreateException code for details.) |
| */ |
| e = PyObject_CallFunctionObjArgs(PyExc_StopIteration, value, NULL); |
| if (e == NULL) { |
| return -1; |
| } |
| PyErr_SetObject(PyExc_StopIteration, e); |
| Py_DECREF(e); |
| return 0; |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif |