nbsdgames/bubbob/statesaver.c
2021-02-04 08:37:39 +01:00

611 lines
15 KiB
C

/** High-performance deep copy.
This one can copy running generators and their frames!
statesaver.copy(x) -> recursive copy of x
You have precise control over what is copied and what should be shared.
By default, *only* common built-in types are copied. Unrecognized
object types are shared. The copied built-in types are:
- tuple
- list
- dict
- functions, for possibly mutable func_defaults (func_globals is shared)
- methods, for im_self and im_func (im_class is shared)
- running or stopped generators (yeah!)
- sequence iterators
Old-style class instances are only copied if they have an
inst_build() method, which is called with no argument and must
return a new instance whose __dict__ is not filled (it will be
filled by the copying mecanisms). Suggested implementation:
def inst_build(self):
return new.instance(self.__class__)
New-style class instances are not supported (i.e. always shared).
**/
#include <Python.h>
#include <compile.h>
#include <frameobject.h>
#include <eval.h>
static PyObject* copyrec(PyObject* o); /* forward */
static PyObject* empty_iterator;
static PyObject* genbuild(PyObject* g)
{
PyObject* x;
PyFrameObject* f;
PyCodeObject* co;
PyObject** dummy;
int i, res, ncells, nfrees;
x = PyObject_GetAttrString(g, "gi_running");
if (x == NULL)
return NULL;
res = PyObject_IsTrue(x);
Py_DECREF(x);
if (res < 0)
return NULL;
if (res) {
PyErr_SetString(PyExc_ValueError, "generator is running");
return NULL;
}
x = PyObject_GetAttrString(g, "gi_frame");
if (x == NULL)
return NULL;
if (!PyFrame_Check(x)) {
if (x == Py_None) {
/* Python 2.5 only: exhausted generators have g.gi_frame == None */
Py_DECREF(x);
Py_INCREF(empty_iterator);
return empty_iterator;
}
PyErr_SetString(PyExc_TypeError, "g.gi_frame must be a frame object");
goto error;
}
f = (PyFrameObject*) x;
co = f->f_code;
if (!(co->co_flags & CO_GENERATOR)) {
PyErr_SetString(PyExc_ValueError, "the frame is not from a generator");
goto error;
}
if (f->f_stacktop == NULL) {
Py_DECREF(f);
Py_INCREF(g); /* exhausted -- can return 'g' itself */
return g;
}
ncells = PyTuple_GET_SIZE(co->co_cellvars);
nfrees = PyTuple_GET_SIZE(co->co_freevars);
if (nfrees || ncells) {
PyErr_SetString(PyExc_ValueError, "generator has cell or free vars");
goto error;
}
if (co->co_argcount == 0)
dummy = NULL;
else
{
dummy = (PyObject**) malloc(co->co_argcount * sizeof(PyObject*));
if (dummy == NULL)
{
PyErr_NoMemory();
goto error;
}
for (i=0; i<co->co_argcount; i++)
dummy[i] = Py_None;
}
x = PyEval_EvalCodeEx(co, f->f_globals, f->f_locals,
dummy, co->co_argcount, NULL, 0,
NULL, 0, NULL);
if (dummy)
free(dummy);
Py_DECREF(f);
return x;
error:
Py_DECREF(x);
return NULL;
}
static int gencopy(PyObject* g2, PyObject* g)
{
PyObject* x;
PyFrameObject* f = NULL;
PyFrameObject* f2 = NULL;
PyCodeObject* co;
int i, res;
if (g != g2)
{
if (g2->ob_type != g->ob_type)
{
if (g2 == empty_iterator)
return 0;
PyErr_SetString(PyExc_TypeError, "type mismatch");
return -1;
}
x = PyObject_GetAttrString(g, "gi_frame");
if (x == NULL)
return -1;
if (!PyFrame_Check(x)) {
PyErr_SetString(PyExc_TypeError, "g.gi_frame must be a frame object");
Py_DECREF(x);
goto error;
}
f = (PyFrameObject*) x;
co = f->f_code;
x = PyObject_GetAttrString(g2, "gi_frame");
if (x == NULL)
return -1;
if (!PyFrame_Check(x)) {
PyErr_SetString(PyExc_TypeError, "returned gi_frame");
Py_DECREF(x);
goto error;
}
f2 = (PyFrameObject*) x;
if (f2->f_code != co) {
PyErr_SetString(PyExc_TypeError, "generator code mismatch");
goto error;
}
if (f2->f_stacktop != NULL)
while (f2->f_stacktop != f2->f_localsplus)
{
f2->f_stacktop--;
Py_XDECREF(*f2->f_stacktop);
}
res = f->f_stacktop - f->f_localsplus;
f2->f_lasti = f->f_lasti;
f2->f_iblock = f->f_iblock;
memcpy(f2->f_blockstack, f->f_blockstack, sizeof(PyTryBlock)*f->f_iblock);
f2->f_stacktop = f2->f_localsplus;
for (i=0; i<res; i++)
{
x = f->f_localsplus[i];
if (x != NULL)
x = copyrec(x);
*f2->f_stacktop++ = x;
}
}
return 0;
error:
Py_XDECREF(f);
Py_XDECREF(f2);
return -1;
}
typedef struct {
PyObject_HEAD
long it_index;
PyObject *it_seq; /* Set to NULL when iterator is exhausted */
} seqiterobject;
static PyObject* seqiterbuild(PyObject* o)
{
seqiterobject* iter = (seqiterobject*) o;
if (iter->it_seq == NULL)
{
Py_INCREF(iter); /* exhausted */
return (PyObject*) iter;
}
else
return PySeqIter_New(iter->it_seq);
}
static int seqitercopy(PyObject* o2, PyObject* o)
{
PyObject* x;
seqiterobject* iter = (seqiterobject*) o;
seqiterobject* iter2 = (seqiterobject*) o2;
iter2->it_index = iter->it_index;
if (iter->it_seq != NULL)
{
x = copyrec(iter->it_seq);
Py_XDECREF(iter2->it_seq);
iter2->it_seq = x;
}
return 0;
}
#if PY_VERSION_HEX >= 0x02030000 /* 2.3 */
/* pff */
typedef struct {
PyObject_HEAD
long it_index;
PyListObject *it_seq; /* Set to NULL when iterator is exhausted */
} listiterobject;
static PyTypeObject* PyListIter_TypePtr;
static PyObject* listiterbuild(PyObject* o)
{
listiterobject* iter = (listiterobject*) o;
if (iter->it_seq == NULL)
{
Py_INCREF(iter); /* exhausted */
return (PyObject*) iter;
}
else
return PyList_Type.tp_iter((PyObject*) iter->it_seq);
}
static int listitercopy(PyObject* o2, PyObject* o)
{
PyObject* x;
listiterobject* iter = (listiterobject*) o;
listiterobject* iter2 = (listiterobject*) o2;
iter2->it_index = iter->it_index;
if (iter->it_seq != NULL)
{
x = copyrec((PyObject*) iter->it_seq);
Py_XDECREF(iter2->it_seq);
iter2->it_seq = (PyListObject*) x;
}
return 0;
}
typedef struct {
PyObject_HEAD
long it_index;
PyTupleObject *it_seq; /* Set to NULL when iterator is exhausted */
} tupleiterobject;
static PyTypeObject* PyTupleIter_TypePtr;
static PyObject* tupleiterbuild(PyObject* o)
{
tupleiterobject* iter = (tupleiterobject*) o;
if (iter->it_seq == NULL)
{
Py_INCREF(iter); /* exhausted */
return (PyObject*) iter;
}
else
return PyTuple_Type.tp_iter((PyObject*) iter->it_seq);
}
static int tupleitercopy(PyObject* o2, PyObject* o)
{
PyObject* x;
tupleiterobject* iter = (tupleiterobject*) o;
tupleiterobject* iter2 = (tupleiterobject*) o2;
iter2->it_index = iter->it_index;
if (iter->it_seq != NULL)
{
x = copyrec((PyObject*) iter->it_seq);
Py_XDECREF(iter2->it_seq);
iter2->it_seq = (PyTupleObject*) x;
}
return 0;
}
#endif /* PY_VERSION_HEX >= 0x02030000 */
/* HACKS HACKS HACKS */
typedef struct {
PyObject_HEAD
PyObject* o;
} KeyObject;
#define KEYS_BY_BLOCK 1024
struct key_block {
KeyObject keys[KEYS_BY_BLOCK];
struct key_block* next;
};
static long key_hash(KeyObject* k)
{
return (long)(k->o);
}
static PyObject* key_richcmp(KeyObject* k1, KeyObject* k2, int op)
{
PyObject* r;
assert(op == 2 /*PyCmp_EQ*/ );
r = k1->o == k2->o ? Py_True : Py_False;
Py_INCREF(r);
return r;
}
static PyTypeObject keytype = {
PyObject_HEAD_INIT(NULL)
0,
"key",
sizeof(KeyObject),
0,
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
(hashfunc)key_hash, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
(richcmpfunc)key_richcmp, /* tp_richcompare */
};
/* global state */
static PyObject* ss_memo;
static struct key_block* ss_block;
static int ss_next_in_block;
static PyObject *ss_error, *ss_errinst, *ss_errtb;
static PyObject* str_inst_build;
static PyTypeObject* GeneratorType;
/* never returns NULL, and never returns with a Python exception set! */
static PyObject* copyrec(PyObject* o)
{
PyTypeObject* t;
PyObject* n;
PyObject* key;
KeyObject* fkey;
if (o == Py_None || o->ob_type == &PyInt_Type ||
o->ob_type == &PyString_Type || o->ob_type == &PyFloat_Type ||
o == empty_iterator)
{
Py_INCREF(o);
return o;
}
if (ss_next_in_block < 0)
{
struct key_block* b = (struct key_block*) malloc(sizeof(struct key_block));
if (!b) { PyErr_NoMemory(); goto fail1; }
b->next = ss_block;
ss_block = b;
ss_next_in_block = KEYS_BY_BLOCK - 1;
}
fkey = ss_block->keys + ss_next_in_block;
fkey->ob_refcnt = 1;
fkey->ob_type = &keytype;
fkey->o = o;
key = (PyObject*) fkey;
n = PyDict_GetItem(ss_memo, key);
if (n)
{
Py_INCREF(n);
return n;
}
ss_next_in_block--;
Py_INCREF(o); /* reference stored in 'fkey->o' */
t = o->ob_type;
if (t == &PyTuple_Type)
{
int i, count = PyTuple_GET_SIZE(o);
n = PyTuple_New(count);
if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail;
for (i=0; i<count; i++)
PyTuple_SET_ITEM(n, i, copyrec(PyTuple_GET_ITEM(o, i)));
return n;
}
if (t == &PyList_Type)
{
int i, count = PyList_GET_SIZE(o);
n = PyList_New(count);
if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail;
for (i=0; i<count; i++)
PyList_SET_ITEM(n, i, copyrec(PyList_GET_ITEM(o, i)));
return n;
}
if (t == &PyDict_Type)
{
int i = 0;
PyObject* dictkey;
PyObject* dictvalue;
n = PyDict_New();
if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail;
while (PyDict_Next(o, &i, &dictkey, &dictvalue))
if (PyDict_SetItem(n, copyrec(dictkey), copyrec(dictvalue)))
goto fail;
return n;
}
if (t == &PyInstance_Type)
{
int i = 0;
PyObject* dictkey;
PyObject* dictvalue;
PyObject* dsrc;
PyObject* ddest;
PyObject* inst_build = PyObject_GetAttr(o, str_inst_build);
if (inst_build == NULL)
{
PyErr_Clear();
goto unmodified;
}
n = PyObject_CallObject(inst_build, NULL);
Py_DECREF(inst_build);
if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail;
dsrc = ((PyInstanceObject*) o)->in_dict;
ddest = ((PyInstanceObject*) n)->in_dict;
while (PyDict_Next(dsrc, &i, &dictkey, &dictvalue))
if (PyDict_SetItem(ddest, copyrec(dictkey), copyrec(dictvalue)))
goto fail;
return n;
}
if (t == &PyFunction_Type)
{
int i, count;
PyObject* tsrc = PyFunction_GET_DEFAULTS(o);
PyObject* tdest;
if (!tsrc) goto unmodified;
count = PyTuple_GET_SIZE(tsrc);
if (count == 0) goto unmodified;
n = PyFunction_New(PyFunction_GET_CODE(o), PyFunction_GET_GLOBALS(o));
if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail;
tdest = PyTuple_New(count);
if (!tdest) goto fail;
for (i=0; i<count; i++)
PyTuple_SET_ITEM(tdest, i, copyrec(PyTuple_GET_ITEM(tsrc, i)));
i = PyFunction_SetDefaults(n, tdest);
Py_DECREF(tdest);
if (i) goto fail;
return n;
}
if (t == &PyMethod_Type)
{
PyObject* x;
n = PyMethod_New(PyMethod_GET_FUNCTION(o),
PyMethod_GET_SELF(o),
PyMethod_GET_CLASS(o));
if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail;
x = copyrec(PyMethod_GET_FUNCTION(n));
Py_DECREF(PyMethod_GET_FUNCTION(n));
PyMethod_GET_FUNCTION(n) = x;
x = copyrec(PyMethod_GET_SELF(n));
Py_DECREF(PyMethod_GET_SELF(n));
PyMethod_GET_SELF(n) = x;
return n;
}
if (t == GeneratorType)
{
n = genbuild(o);
if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail;
if (gencopy(n, o)) goto fail;
return n;
}
if (t == &PySeqIter_Type)
{
n = seqiterbuild(o);
if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail;
if (seqitercopy(n, o)) goto fail;
return n;
}
#if PY_VERSION_HEX >= 0x02030000 /* 2.3 */
if (t == PyListIter_TypePtr)
{
n = listiterbuild(o);
if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail;
if (listitercopy(n, o)) goto fail;
return n;
}
if (t == PyTupleIter_TypePtr)
{
n = tupleiterbuild(o);
if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail;
if (tupleitercopy(n, o)) goto fail;
return n;
}
#endif
ss_next_in_block++;
return o; /* reference no longer stored in 'fkey->o' */
unmodified:
PyDict_SetItem(ss_memo, key, o);
Py_INCREF(o);
return o;
fail1:
n = NULL;
fail:
Py_INCREF(o);
Py_XDECREF(n);
if (ss_error == NULL)
PyErr_Fetch(&ss_error, &ss_errinst, &ss_errtb);
else
PyErr_Clear();
return o;
}
static PyObject* sscopy(PyObject* self, PyObject* o)
{
PyObject* n;
ss_memo = PyDict_New();
if (!ss_memo)
return NULL;
ss_block = NULL;
ss_next_in_block = -1;
ss_error = NULL;
ss_errinst = NULL;
ss_errtb = NULL;
n = copyrec(o);
Py_DECREF(ss_memo);
while (ss_block)
{
int i;
struct key_block* b = ss_block;
ss_block = b->next;
for (i=ss_next_in_block+1; i<KEYS_BY_BLOCK; i++)
Py_DECREF(b->keys[i].o);
free(b);
ss_next_in_block = -1;
}
if (ss_error && !PyErr_Occurred())
PyErr_Restore(ss_error, ss_errinst, ss_errtb);
else
{
Py_XDECREF(ss_error);
Py_XDECREF(ss_errinst);
Py_XDECREF(ss_errtb);
}
if (PyErr_Occurred())
{
Py_DECREF(n);
n = NULL;
}
return n;
}
static PyMethodDef StateSaverMethods[] = {
{"copy", sscopy, METH_O},
{NULL, NULL} /* Sentinel */
};
void initstatesaver(void)
{
PyObject* m;
PyObject* x, *y;
m = Py_InitModule("statesaver", StateSaverMethods);
if (m == NULL)
return;
keytype.ob_type = &PyType_Type;
str_inst_build = PyString_InternFromString("inst_build");
m = PyImport_ImportModule("types");
if (!m) return;
GeneratorType = (PyTypeObject*) PyObject_GetAttrString(m, "GeneratorType");
if (!GeneratorType) return;
x = PyTuple_New(0);
if (!x) return;
empty_iterator = PyObject_GetIter(x);
Py_DECREF(x);
if (!empty_iterator) return;
PyTupleIter_TypePtr = empty_iterator->ob_type;
x = PyList_New(0);
if (!x) return;
y = PyList_Type.tp_iter(x);
if (y) PyListIter_TypePtr = y->ob_type;
Py_XDECREF(y);
Py_DECREF(x);
if (!y) return;
}