416 lines
12 KiB
C
416 lines
12 KiB
C
// 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 "pythoncapi_compat.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_SET_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_SET_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 present 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_CallOneArg 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_CallOneArg(PyExc_StopIteration, value);
|
|
if (e == NULL) {
|
|
return -1;
|
|
}
|
|
PyErr_SetObject(PyExc_StopIteration, e);
|
|
Py_DECREF(e);
|
|
return 0;
|
|
}
|
|
|
|
// Copied from dictobject.c and dictobject.h, these are not Public before
|
|
// Python 3.8. Also remove some error checks that we do in the callers.
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
PyDictObject *dv_dict;
|
|
} _CPyDictViewObject;
|
|
|
|
static PyObject *
|
|
_CPyDictView_New(PyObject *dict, PyTypeObject *type)
|
|
{
|
|
_CPyDictViewObject *dv = PyObject_GC_New(_CPyDictViewObject, type);
|
|
if (dv == NULL)
|
|
return NULL;
|
|
Py_INCREF(dict);
|
|
dv->dv_dict = (PyDictObject *)dict;
|
|
PyObject_GC_Track(dv);
|
|
return (PyObject *)dv;
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >=10
|
|
static int
|
|
_CPyObject_HasAttrId(PyObject *v, _Py_Identifier *name) {
|
|
PyObject *tmp = NULL;
|
|
int result = _PyObject_LookupAttrId(v, name, &tmp);
|
|
if (tmp) {
|
|
Py_DECREF(tmp);
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
#define _CPyObject_HasAttrId _PyObject_HasAttrId
|
|
#endif
|
|
|
|
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION < 9
|
|
// OneArgs and NoArgs functions got added in 3.9
|
|
#define _PyObject_CallMethodIdNoArgs(self, name) \
|
|
_PyObject_CallMethodIdObjArgs((self), (name), NULL)
|
|
#define _PyObject_CallMethodIdOneArg(self, name, arg) \
|
|
_PyObject_CallMethodIdObjArgs((self), (name), (arg), NULL)
|
|
#endif
|
|
|
|
#endif
|