text: // 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
  