From: Armin R. <ar...@us...> - 2004-12-29 20:43:09
|
Update of /cvsroot/psyco/psyco/c/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv22015/c/Objects Modified Files: compactobject.c compactobject.h pcompactobject.c Log Message: More work on psyco.compact: - getattr, setattr and delattr respect data descriptors now: psyco.compact subclasses should behave very much like object subclasses, including in the presence of properties. The __dict__ attribute is a way to access the underlying attribute if it is hidden by a data descriptor. - can assign to __dict__. It makes a copy of the dict into the instance, though; the dict and the instance don't reflect each other's future changes (they do in plain Python). - there is a better C API in compactobject.h, but it's disabled for now because it's not needed within Psyco. - includes documentation and tests! Index: compactobject.c =================================================================== RCS file: /cvsroot/psyco/psyco/c/Objects/compactobject.c,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** compactobject.c 29 Dec 2004 12:27:46 -0000 1.7 --- compactobject.c 29 Dec 2004 20:42:54 -0000 1.8 *************** *** 86,89 **** --- 86,90 ---- } + #if 0 DEFINEFN PyObject* PyCompact_New(void) *************** *** 97,100 **** --- 98,102 ---- return (PyObject*) o; } + #endif static bool k_same_vinfo(vinfo_t* a, vinfo_t* b) *************** *** 312,330 **** static PyObject* compact_getattro(PyCompactObject* ko, PyObject* attr) { compact_impl_t* impl = ko->k_impl; PyObject* o; ! Py_INCREF(attr); K_INTERN(attr); while (impl->attrname != NULL) { if (impl->attrname == attr) { o = direct_xobj_vinfo(impl->vattr, ko->k_data); ! if (o != NULL || PyErr_Occurred()) ! goto finally; } impl = impl->parent; } ! o = PyObject_GenericGetAttr((PyObject*) ko, attr); ! finally: Py_DECREF(attr); return o; --- 314,376 ---- static PyObject* compact_getattro(PyCompactObject* ko, PyObject* attr) { + PyTypeObject* tp = ko->ob_type; + PyObject* descr; + descrgetfunc f = NULL; compact_impl_t* impl = ko->k_impl; PyObject* o; ! ! if (tp->tp_dict == NULL) { ! if (PyType_Ready(tp) < 0) ! return NULL; ! } ! Py_INCREF(attr); K_INTERN(attr); + + /* Special code for data descriptors first, as in + PyObject_GenericGetAttr() */ + descr = _PyType_Lookup(tp, attr); + if (descr != NULL) { + Py_INCREF(descr); + if (PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) { + f = descr->ob_type->tp_descr_get; + if (f != NULL && PyDescr_IsData(descr)) { + o = f(descr, (PyObject*) ko, (PyObject*) tp); + Py_DECREF(descr); + goto done; + } + } + } + + /* Read data out of the compact memory buffer */ while (impl->attrname != NULL) { if (impl->attrname == attr) { o = direct_xobj_vinfo(impl->vattr, ko->k_data); ! if (o != NULL || PyErr_Occurred()) { ! Py_XDECREF(descr); ! goto done; ! } } impl = impl->parent; } ! ! /* The end of PyObject_GenericGetAttr() */ ! if (f != NULL) { ! o = f(descr, (PyObject*) ko, (PyObject*) tp); ! Py_DECREF(descr); ! goto done; ! } ! ! if (descr != NULL) { ! o = descr; ! /* descr was already increfed above */ ! goto done; ! } ! ! o = NULL; ! PyErr_Format(PyExc_AttributeError, ! "'%.50s' object has no attribute '%.400s'", ! tp->tp_name, PyString_AS_STRING(attr)); ! done: Py_DECREF(attr); return o; *************** *** 450,454 **** static ! int compact_setattro(PyCompactObject* ko, PyObject* attr, PyObject* value) { int err, smin, smax; --- 496,501 ---- static ! int compact_set(PyCompactObject* ko, PyObject* attr, PyObject* value, ! PyObject* pyerr_notfound) { int err, smin, smax; *************** *** 459,465 **** compact_impl_t* p; - /* NB. this assumes that 'attr' is an already-interned string. - PyObject_SetAttr() should have interned it. */ - /* recognize a few obvious object types and optimize accordingly Note that this is not related to Psyco's ability to store --- 506,509 ---- *************** *** 530,535 **** if (source_vi == NULL) { /* deleting a non-existing attribute */ ! err = PyObject_GenericSetAttr((PyObject*) ko, attr, NULL); ! goto finally; } --- 574,579 ---- if (source_vi == NULL) { /* deleting a non-existing attribute */ ! PyErr_SetObject(pyerr_notfound, attr); ! return -1; } *************** *** 547,558 **** } ! static PyObject* compact_getslot(PyCompactObject* ko, PyObject* key) { ! compact_impl_t* impl = ko->k_impl; ! PyObject* o; if (key->ob_type != &PyString_Type) { if (!PyString_Check(key)) { ! PyErr_SetObject(PyExc_KeyError, key); return NULL; } --- 591,628 ---- } ! static ! int compact_setattro(PyCompactObject* ko, PyObject* attr, PyObject* value) { ! PyTypeObject* tp = ko->ob_type; ! PyObject* descr; ! descrsetfunc f; + /* NB. this assumes that 'attr' is an already-interned string. + PyObject_SetAttr() should have interned it. */ + + /* Special code for data descriptors first, as in + PyObject_GenericSetAttr() */ + if (tp->tp_dict == NULL) { + if (PyType_Ready(tp) < 0) + return -1; + } + descr = _PyType_Lookup(tp, attr); + if (descr != NULL && + PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) { + f = descr->ob_type->tp_descr_set; + if (f != NULL && PyDescr_IsData(descr)) + return f(descr, (PyObject*) ko, value); + } + + return compact_set(ko, attr, value, PyExc_AttributeError); + } + + static PyObject* k_interned_key(PyObject* key) + { if (key->ob_type != &PyString_Type) { if (!PyString_Check(key)) { ! PyErr_SetString(PyExc_TypeError, ! "keys in compact objects " ! "must be strings"); return NULL; } *************** *** 566,569 **** --- 636,705 ---- } K_INTERN(key); + return key; + } + + #if 0 + DEFINEFN + PyObject* PyCompact_GetSlot(PyObject* ko, PyObject* key) + { + compact_impl_t* impl; + PyObject* o; + + if (!PyCompact_Check(ko)) { + PyErr_BadInternalCall(); + return NULL; + } + + key = k_interned_key(key); + if (key == NULL) + return NULL; + + impl = ((PyCompactObject*) ko)->k_impl; + while (impl->attrname != NULL) { + if (impl->attrname == key) { + o = direct_xobj_vinfo(impl->vattr, + ((PyCompactObject*) ko)->k_data); + if (o != NULL || PyErr_Occurred()) + goto finally; + } + impl = impl->parent; + } + PyErr_SetObject(PyExc_KeyError, key); + o = NULL; + finally: + Py_DECREF(key); + return o; + } + + DEFINEFN + PyObject* PyCompact_SetSlot(PyObject* ko, PyObject* key, PyObject* value) + { + int err; + + if (!PyCompact_Check(ko)) { + PyErr_BadInternalCall(); + return NULL; + } + + key = k_interned_key(key); + if (key == NULL) + return NULL; + + err = compact_set((PyCompactObject*) ko, key, value, + PyExc_KeyError); + Py_DECREF(key); + return err; + } + #endif + + static PyObject* compact_getslot(PyCompactObject* ko, PyObject* key) + { + compact_impl_t* impl = ko->k_impl; + PyObject* o; + + key = k_interned_key(key); + if (key == NULL) + return NULL; + while (impl->attrname != NULL) { if (impl->attrname == key) { *************** *** 581,584 **** --- 717,757 ---- } + static PyObject* compact_setslot(PyCompactObject* ko, PyObject* args) + { + PyObject* key; + PyObject* value; + int err; + + if (!PyArg_ParseTuple(args, "OO", &key, &value)) + return NULL; + + key = k_interned_key(key); + if (key == NULL) + return NULL; + + err = compact_set(ko, key, value, PyExc_KeyError); + Py_DECREF(key); + if (err < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; + } + + static PyObject* compact_delslot(PyCompactObject* ko, PyObject* key) + { + int err; + + key = k_interned_key(key); + if (key == NULL) + return NULL; + + err = compact_set(ko, key, NULL, PyExc_KeyError); + Py_DECREF(key); + if (err < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; + } + static PyObject* compact_getmembers(PyCompactObject* ko, void* context) { *************** *** 601,610 **** } ! static PyObject* compact_getdict(PyCompactObject* ko, void* context) { PyObject* t = need_cpsyco_obj("compactdictproxy"); if (t == NULL) return NULL; ! return PyObject_CallFunction(t, "O", (PyObject*) ko); } --- 774,834 ---- } ! static PyObject* compact_getdict(PyObject* ko, void* context) { PyObject* t = need_cpsyco_obj("compactdictproxy"); if (t == NULL) return NULL; ! return PyObject_CallFunction(t, "O", ko); ! } ! ! static int compact_setdict(PyObject* ko, PyObject* value, void* context) ! { ! PyObject* nval; ! PyObject* d; ! PyObject* tmp; ! ! if (value == NULL) { ! PyErr_SetString(PyExc_AttributeError, ! "__dict__ attribute cannot be deleted"); ! return -1; ! } ! if (PyDict_Check(value)) { ! nval = value; ! Py_INCREF(nval); ! } ! else { ! /* Force a complete copy of 'value' for the assignment ! x.__dict__ = x.__dict__. Note that we could do better ! and just copy the memory buffer if we detect that ! 'value' is the dict proxy of another compact object. */ ! if (!PyMapping_Check(value)) { ! PyErr_SetString(PyExc_TypeError, ! "__dict__ attribute must be set " ! "to a mapping"); ! return -1; ! } ! nval = PyDict_New(); ! if (nval == NULL) ! return -1; ! if (PyDict_Merge(nval, value, 1) < 0) ! goto error; ! } ! d = compact_getdict(ko, context); ! if (d == NULL) ! goto error; ! tmp = PyObject_CallMethod(d, "clear", ""); ! if (tmp == NULL) ! goto error; ! Py_DECREF(tmp); ! tmp = PyObject_CallMethod(d, "update", "O", nval); ! if (tmp == NULL) ! goto error; ! Py_DECREF(tmp); ! Py_DECREF(nval); ! return 0; ! ! error: ! Py_DECREF(nval); ! return -1; } *************** *** 618,626 **** */ static PyObject * compacttype_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) { int i, n; ! PyObject *name, *bases, *dict, *slots, *result; static char *kwlist[] = {"name", "bases", "dict", 0}; --- 842,852 ---- */ + staticforward PyTypeObject PyCompactType_Type; + static PyObject * compacttype_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) { int i, n; ! PyObject *name, *bases, *dict, *slots, *result, *nbases; static char *kwlist[] = {"name", "bases", "dict", 0}; *************** *** 646,655 **** PyTuple_SET_ITEM(args, 0, name); Py_INCREF(name); ! /* Append 'psyco.compact' to bases if necessary */ n = PyTuple_GET_SIZE(bases); ! if (n == 0 || ! PyTuple_GET_ITEM(bases, n-1) != (PyObject*) &PyCompact_Type) { ! result = PyTuple_New(n+1); ! if (result == NULL) { Py_DECREF(args); return NULL; --- 872,890 ---- PyTuple_SET_ITEM(args, 0, name); Py_INCREF(name); ! /* Append 'psyco.compact' to bases if necessary, i.e. if none of the ! provided bases already has a metaclass of psyco.compacttype. ! The goal is to ensure that all the instances of psyco.compacttype ! are classes that inherit from psyco.compact, but only add ! psyco.compact to the bases if absolutely necessary. */ n = PyTuple_GET_SIZE(bases); ! for (i=0; i<n; i++) { ! if (PyObject_TypeCheck(PyTuple_GET_ITEM(bases, i), ! &PyCompactType_Type)) ! break; ! } ! if (i == n) { ! /* no suitable base found, must append 'psyco.compact' */ ! nbases = PyTuple_New(n+1); ! if (nbases == NULL) { Py_DECREF(args); return NULL; *************** *** 657,670 **** for (i=0; i<n; i++) { PyObject* o = PyTuple_GET_ITEM(bases, i); ! PyTuple_SET_ITEM(result, i, o); Py_INCREF(o); } ! PyTuple_SET_ITEM(result, n, (PyObject*) &PyCompact_Type); Py_INCREF(&PyCompact_Type); - bases = result; } ! else Py_INCREF(bases); ! PyTuple_SET_ITEM(args, 1, bases); /* Insert '__slots__=()' into a copy of 'dict' */ --- 892,906 ---- for (i=0; i<n; i++) { PyObject* o = PyTuple_GET_ITEM(bases, i); ! PyTuple_SET_ITEM(nbases, i, o); Py_INCREF(o); } ! PyTuple_SET_ITEM(nbases, n, (PyObject*) &PyCompact_Type); Py_INCREF(&PyCompact_Type); } ! else { ! nbases = bases; Py_INCREF(bases); ! } ! PyTuple_SET_ITEM(args, 1, nbases); /* Insert '__slots__=()' into a copy of 'dict' */ *************** *** 687,691 **** } ! static PyTypeObject PyCompactType_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ --- 923,927 ---- } ! statichere PyTypeObject PyCompactType_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ *************** *** 739,742 **** --- 975,980 ---- static PyMethodDef compact_methods[] = { {"__getslot__", (PyCFunction)compact_getslot, METH_O, NULL}, + {"__setslot__", (PyCFunction)compact_setslot, METH_VARARGS, NULL}, + {"__delslot__", (PyCFunction)compact_delslot, METH_O, NULL}, {NULL, NULL} /* sentinel */ }; *************** *** 744,748 **** static PyGetSetDef compact_getsets[] = { {"__members__", (getter)compact_getmembers, NULL, NULL}, ! {"__dict__", (getter)compact_getdict, NULL, NULL}, {NULL} }; --- 982,986 ---- static PyGetSetDef compact_getsets[] = { {"__members__", (getter)compact_getmembers, NULL, NULL}, ! {"__dict__", compact_getdict, compact_setdict, NULL}, {NULL} }; Index: compactobject.h =================================================================== RCS file: /cvsroot/psyco/psyco/c/Objects/compactobject.h,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** compactobject.h 11 Aug 2004 23:03:38 -0000 1.1 --- compactobject.h 29 Dec 2004 20:42:54 -0000 1.2 *************** *** 30,33 **** --- 30,36 ---- /* EXTERNFN PyObject* PyCompact_New(void); */ + /* EXTERNFN PyObject* PyCompact_GetSlot(PyObject* ko, PyObject* key); */ + /* EXTERNFN PyObject* PyCompact_SetSlot(PyObject* ko, PyObject* key, */ + /* PyObject* value); */ /* EXTERNFN int PyCompact_Extend(PyObject* ko, compact_impl_t* nimpl); */ /* EXTERNFN compact_impl_t* PyCompact_ExtendImpl(compact_impl_t* oldimpl, */ Index: pcompactobject.c =================================================================== RCS file: /cvsroot/psyco/psyco/c/Objects/pcompactobject.c,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** pcompactobject.c 20 Aug 2004 17:13:38 -0000 1.2 --- pcompactobject.c 29 Dec 2004 20:42:54 -0000 1.3 *************** *** 84,93 **** static vinfo_t* pcompact_getattro(PsycoObject* po, vinfo_t* vk, vinfo_t* vattr) { long l; vinfo_t* vimpl; compact_impl_t* impl; ! vinfo_t* vresult; PyObject* name; ! /* don't try to optimize non-constant attribute names (see explanation in PsycoObject_GenericGetAttr()) */ --- 84,96 ---- static vinfo_t* pcompact_getattro(PsycoObject* po, vinfo_t* vk, vinfo_t* vattr) { + PyTypeObject* tp; + PyObject* descr = NULL; + descrgetfunc f = NULL; long l; vinfo_t* vimpl; compact_impl_t* impl; ! vinfo_t* vresult = NULL; PyObject* name; ! /* don't try to optimize non-constant attribute names (see explanation in PsycoObject_GenericGetAttr()) */ *************** *** 98,115 **** } /* read and temporarily promote the k_impl field of the object */ vimpl = psyco_get_const(po, vk, IMMUT_COMPACT_impl); if (vimpl == NULL) ! return NULL; l = psyco_atcompiletime(po, vimpl); if (l == -1) ! return NULL; psyco_forget_field(po, vk, IMMUT_COMPACT_impl); impl = (compact_impl_t*) l; - - /* use interned strings only */ - name = (PyObject*) CompileTime_Get(vattr->source)->value; - Py_INCREF(name); - K_INTERN(name); while (impl->attrname != NULL) { --- 101,145 ---- } + /* we need the type of 'obj' at compile-time */ + tp = (PyTypeObject*) Psyco_NeedType(po, vk); + if (tp == NULL) + return NULL; + + if (tp->tp_dict == NULL) { + if (PyType_Ready(tp) < 0) { + psyco_virtualize_exception(po); + return NULL; + } + } + + /* use interned strings only */ + name = (PyObject*) CompileTime_Get(vattr->source)->value; + Py_INCREF(name); + K_INTERN(name); + + /* XXX this is broken in the same way as PsycoObject_GenericGetAttr() */ + descr = _PyType_Lookup(tp, name); + if (descr != NULL) { + Py_INCREF(descr); + if (PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) { + f = descr->ob_type->tp_descr_get; + if (f != NULL && PyDescr_IsData(descr)) { + vresult = Psyco_META3(po, f, + CfReturnRef|CfPyErrIfNull, + "lvl", descr, vk, tp); + goto done; + } + } + } + /* read and temporarily promote the k_impl field of the object */ vimpl = psyco_get_const(po, vk, IMMUT_COMPACT_impl); if (vimpl == NULL) ! goto done; l = psyco_atcompiletime(po, vimpl); if (l == -1) ! goto done; psyco_forget_field(po, vk, IMMUT_COMPACT_impl); impl = (compact_impl_t*) l; while (impl->attrname != NULL) { *************** *** 121,131 **** vresult = psy_k_load_vinfo(po, impl->vattr, vk, &vdata); vinfo_xdecref(vdata, po); ! goto finally; } impl = impl->parent; } - vresult = PsycoObject_GenericGetAttr(po, vk, vattr); ! finally: Py_DECREF(name); return vresult; --- 151,178 ---- vresult = psy_k_load_vinfo(po, impl->vattr, vk, &vdata); vinfo_xdecref(vdata, po); ! goto done; } impl = impl->parent; } ! /* The end of PyObject_GenericGetAttr() */ ! if (f != NULL) { ! vresult = Psyco_META3(po, f, CfReturnRef|CfPyErrIfNull, ! "lvl", descr, vk, tp); ! goto done; ! } ! ! if (descr != NULL) { ! source_known_t* sk = sk_new((long) descr, SkFlagPyObj); ! descr = NULL; ! vresult = vinfo_new(CompileTime_NewSk(sk)); ! goto done; ! } ! ! PycException_SetFormat(po, PyExc_AttributeError, ! "'%.50s' object has no attribute '%.400s'", ! tp->tp_name, PyString_AS_STRING(name)); ! done: ! Py_XDECREF(descr); Py_DECREF(name); return vresult; *************** *** 235,238 **** --- 282,288 ---- vinfo_t* source_vi) { + PyTypeObject* tp; + PyObject* descr; + descrsetfunc f; long l; vinfo_t* vimpl; *************** *** 249,252 **** --- 299,328 ---- int smin, smax, s, s1, s2; + /* we need the type of 'obj' at compile-time */ + tp = (PyTypeObject*) Psyco_NeedType(po, vk); + if (tp == NULL) + return false; + + if (tp->tp_dict == NULL) { + if (PyType_Ready(tp) < 0) { + psyco_virtualize_exception(po); + return false; + } + } + + /* XXX this is broken in the same way as PsycoObject_GenericGetAttr() */ + descr = _PyType_Lookup(tp, attr); + if (descr != NULL && + PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) { + f = descr->ob_type->tp_descr_set; + if (f != NULL && PyDescr_IsData(descr)) { + Py_INCREF(descr); /* XXX leaks */ + return Psyco_META3(po, f, + CfNoReturnValue|CfPyErrIfNonNull, + source_vi ? "lvv" : "lvl", + descr, vk, source_vi) != NULL; + } + } + if (source_vi != NULL) { /* force mutable virtual-time objects out of virtual-time, |