Commit 3f9490a3 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Copy in CPython's code for changing __class__

parent 369121e1
...@@ -3150,7 +3150,7 @@ static int same_slots_added(PyTypeObject* a, PyTypeObject* b) noexcept { ...@@ -3150,7 +3150,7 @@ static int same_slots_added(PyTypeObject* a, PyTypeObject* b) noexcept {
return size == a->tp_basicsize && size == b->tp_basicsize; return size == a->tp_basicsize && size == b->tp_basicsize;
} }
static int compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, const char* attr) noexcept { int compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, const char* attr) noexcept {
PyTypeObject* newbase, *oldbase; PyTypeObject* newbase, *oldbase;
if (newto->tp_dealloc != oldto->tp_dealloc || newto->tp_free != oldto->tp_free) { if (newto->tp_dealloc != oldto->tp_dealloc || newto->tp_free != oldto->tp_free) {
......
...@@ -53,6 +53,7 @@ Py_ssize_t slot_sq_length(PyObject* self) noexcept; ...@@ -53,6 +53,7 @@ Py_ssize_t slot_sq_length(PyObject* self) noexcept;
PyObject* slot_tp_getattr_hook(PyObject* self, PyObject* name) noexcept; PyObject* slot_tp_getattr_hook(PyObject* self, PyObject* name) noexcept;
PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds) noexcept; PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds) noexcept;
int slot_tp_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept; int slot_tp_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept;
int compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, const char* attr) noexcept;
class GetattrRewriteArgs; class GetattrRewriteArgs;
template <ExceptionStyle S, Rewritable rewritable> template <ExceptionStyle S, Rewritable rewritable>
......
...@@ -725,8 +725,8 @@ public: ...@@ -725,8 +725,8 @@ public:
void clearAttrsForDealloc(); void clearAttrsForDealloc();
void giveAttrDescriptor(const char* attr, Box* (*get)(Box*, void*), void giveAttrDescriptor(const char* attr, Box* (*get)(Box*, void*), void (*set)(Box*, Box*, void*));
void (*set)(Box*, Box*, void*)); void giveCapiAttrDescriptor(const char* attr, Box* (*get)(Box*, void*), int (*set)(Box*, Box*, void*));
// getattr() does the equivalent of PyDict_GetItem(obj->dict, attr): it looks up the attribute's value on the // getattr() does the equivalent of PyDict_GetItem(obj->dict, attr): it looks up the attribute's value on the
// object's attribute storage. it doesn't look at other objects or do any descriptor logic. // object's attribute storage. it doesn't look at other objects or do any descriptor logic.
......
...@@ -539,8 +539,10 @@ void instanceSetattroInternal(Box* _inst, Box* _attr, STOLEN(Box*) value, Setatt ...@@ -539,8 +539,10 @@ void instanceSetattroInternal(Box* _inst, Box* _attr, STOLEN(Box*) value, Setatt
Py_FatalError("unimplemented"); Py_FatalError("unimplemented");
if (attr->s() == "__class__") { if (attr->s() == "__class__") {
if (value->cls != classobj_cls) if (value->cls != classobj_cls) {
Py_DECREF(value);
raiseExcHelper(TypeError, "__class__ must be set to a class"); raiseExcHelper(TypeError, "__class__ must be set to a class");
}
auto old_cls = inst->inst_cls; auto old_cls = inst->inst_cls;
inst->inst_cls = static_cast<BoxedClassobj*>(value); inst->inst_cls = static_cast<BoxedClassobj*>(value);
......
...@@ -827,8 +827,8 @@ extern "C" PyObject* PyDescr_NewMember(PyTypeObject* x, struct PyMemberDef* y) n ...@@ -827,8 +827,8 @@ extern "C" PyObject* PyDescr_NewMember(PyTypeObject* x, struct PyMemberDef* y) n
extern "C" PyObject* PyDescr_NewGetSet(PyTypeObject* x, struct PyGetSetDef* y) noexcept { extern "C" PyObject* PyDescr_NewGetSet(PyTypeObject* x, struct PyGetSetDef* y) noexcept {
// TODO do something with __doc__ // TODO do something with __doc__
return new (capi_getset_cls) BoxedGetsetDescriptor(autoDecref(internStringMortal(y->name)), y->get, return new (capi_getset_cls)
(void (*)(Box*, Box*, void*))y->set, y->closure); BoxedGetsetDescriptor(autoDecref(internStringMortal(y->name)), y->get, y->set, y->closure);
} }
extern "C" PyObject* PyDescr_NewClassMethod(PyTypeObject* type, PyMethodDef* method) noexcept { extern "C" PyObject* PyDescr_NewClassMethod(PyTypeObject* type, PyMethodDef* method) noexcept {
......
...@@ -2940,8 +2940,13 @@ bool dataDescriptorSetSpecialCases(Box* obj, STOLEN(Box*) val, Box* descr, Setat ...@@ -2940,8 +2940,13 @@ bool dataDescriptorSetSpecialCases(Box* obj, STOLEN(Box*) val, Box* descr, Setat
} }
AUTO_DECREF(val); AUTO_DECREF(val);
getset_descr->set(obj, val, getset_descr->closure); if (descr->cls == pyston_getset_cls) {
checkAndThrowCAPIException(); getset_descr->set_pyston(obj, val, getset_descr->closure);
} else {
int r = getset_descr->set_capi(obj, val, getset_descr->closure);
if (r)
throwCAPIException();
}
return true; return true;
} else if (descr->cls == member_descriptor_cls) { } else if (descr->cls == member_descriptor_cls) {
......
...@@ -2815,6 +2815,11 @@ void Box::giveAttrDescriptor(const char* attr, Box* (*get)(Box*, void*), void (* ...@@ -2815,6 +2815,11 @@ void Box::giveAttrDescriptor(const char* attr, Box* (*get)(Box*, void*), void (*
giveAttr(bstr, new (pyston_getset_cls) BoxedGetsetDescriptor(bstr, get, set, NULL)); giveAttr(bstr, new (pyston_getset_cls) BoxedGetsetDescriptor(bstr, get, set, NULL));
} }
void Box::giveCapiAttrDescriptor(const char* attr, Box* (*get)(Box*, void*), int (*set)(Box*, Box*, void*)) {
BoxedString* bstr = internStringMortal(attr);
giveAttr(bstr, new (capi_getset_cls) BoxedGetsetDescriptor(bstr, get, set, NULL));
}
BORROWED(Box*) Box::getAttrWrapper() { BORROWED(Box*) Box::getAttrWrapper() {
assert(cls->instancesHaveHCAttrs()); assert(cls->instancesHaveHCAttrs());
HCAttrs* attrs = getHCAttrsPtr(); HCAttrs* attrs = getHCAttrsPtr();
...@@ -3376,47 +3381,37 @@ done: ...@@ -3376,47 +3381,37 @@ done:
return result; return result;
} }
static Box* objectClass(Box* obj, void* context) { static PyObject* object_get_class(PyObject* self, void* closure) noexcept {
assert(obj->cls != instance_cls); // should override __class__ in classobj Py_INCREF(Py_TYPE(self));
return incref(obj->cls); return (PyObject*)(Py_TYPE(self));
} }
static void objectSetClass(Box* obj, Box* val, void* context) { static int object_set_class(PyObject* self, PyObject* value, void* closure) noexcept {
RELEASE_ASSERT(0, "check refcounting"); PyTypeObject* oldto = Py_TYPE(self);
PyTypeObject* newto;
if (!PyType_Check(val))
raiseExcHelper(TypeError, "__class__ must be set to new-style class, not '%s' object", val->cls->tp_name);
auto new_cls = static_cast<BoxedClass*>(val);
// Conservative Pyston checks: make sure that both classes are derived only from Pyston types, if (value == NULL) {
// and that they don't define any extra C-level fields PyErr_SetString(PyExc_TypeError, "can't delete __class__ attribute");
RELEASE_ASSERT(val->cls == type_cls, ""); return -1;
RELEASE_ASSERT(obj->cls->cls == type_cls, "");
for (auto b : *static_cast<BoxedTuple*>(obj->cls->tp_mro)) {
BoxedClass* base = static_cast<BoxedClass*>(b);
RELEASE_ASSERT(base->is_pyston_class, "");
} }
for (auto b : *static_cast<BoxedTuple*>(new_cls->tp_mro)) { if (!PyType_Check(value)) {
BoxedClass* base = static_cast<BoxedClass*>(b); PyErr_Format(PyExc_TypeError, "__class__ must be set to new-style class, not '%s' object",
RELEASE_ASSERT(base->is_pyston_class, ""); Py_TYPE(value)->tp_name);
return -1;
}
newto = (PyTypeObject*)value;
if (!(newto->tp_flags & Py_TPFLAGS_HEAPTYPE) || !(oldto->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
PyErr_Format(PyExc_TypeError, "__class__ assignment: only for heap types");
return -1;
}
if (compatible_for_assignment(newto, oldto, "__class__")) {
Py_INCREF(newto);
Py_TYPE(self) = newto;
Py_DECREF(oldto);
return 0;
} else {
return -1;
} }
RELEASE_ASSERT(obj->cls->tp_basicsize == object_cls->tp_basicsize + sizeof(HCAttrs) + sizeof(Box**), "");
RELEASE_ASSERT(new_cls->tp_basicsize == object_cls->tp_basicsize + sizeof(HCAttrs) + sizeof(Box**), "");
RELEASE_ASSERT(obj->cls->attrs_offset != 0, "");
RELEASE_ASSERT(new_cls->attrs_offset != 0, "");
RELEASE_ASSERT(obj->cls->tp_weaklistoffset != 0, "");
RELEASE_ASSERT(new_cls->tp_weaklistoffset != 0, "");
// Normal Python checks.
// TODO there are more checks to add here, and they should throw errors not asserts
RELEASE_ASSERT(obj->cls->tp_basicsize == new_cls->tp_basicsize, "");
RELEASE_ASSERT(obj->cls->tp_dictoffset == new_cls->tp_dictoffset, "");
RELEASE_ASSERT(obj->cls->tp_weaklistoffset == new_cls->tp_weaklistoffset, "");
RELEASE_ASSERT(obj->cls->attrs_offset == new_cls->attrs_offset, "");
obj->cls = new_cls;
} }
static PyMethodDef object_methods[] = { static PyMethodDef object_methods[] = {
...@@ -3729,12 +3724,13 @@ static Box* getsetSet(Box* self, Box* obj, Box* val) { ...@@ -3729,12 +3724,13 @@ static Box* getsetSet(Box* self, Box* obj, Box* val) {
} }
if (isSubclass(self->cls, pyston_getset_cls)) { if (isSubclass(self->cls, pyston_getset_cls)) {
getset_descr->set(obj, val, getset_descr->closure); getset_descr->set_pyston(obj, val, getset_descr->closure);
return incref(None); return incref(None);
} else { } else {
RELEASE_ASSERT(isSubclass(self->cls, capi_getset_cls), ""); RELEASE_ASSERT(isSubclass(self->cls, capi_getset_cls), "");
getset_descr->set(obj, val, getset_descr->closure); int r = getset_descr->set_capi(obj, val, getset_descr->closure);
checkAndThrowCAPIException(); if (r)
throwCAPIException();
return incref(None); return incref(None);
} }
} }
...@@ -4495,7 +4491,7 @@ void setupRuntime() { ...@@ -4495,7 +4491,7 @@ void setupRuntime() {
for (auto& md : object_methods) { for (auto& md : object_methods) {
object_cls->giveAttr(md.ml_name, new BoxedMethodDescriptor(&md, object_cls)); object_cls->giveAttr(md.ml_name, new BoxedMethodDescriptor(&md, object_cls));
} }
object_cls->giveAttrDescriptor("__class__", objectClass, objectSetClass); object_cls->giveCapiAttrDescriptor("__class__", object_get_class, object_set_class);
object_cls->tp_str = object_str; object_cls->tp_str = object_str;
add_operators(object_cls); add_operators(object_cls);
......
...@@ -1137,12 +1137,23 @@ public: ...@@ -1137,12 +1137,23 @@ public:
class BoxedGetsetDescriptor : public Box { class BoxedGetsetDescriptor : public Box {
public: public:
Box* (*get)(Box*, void*); Box* (*get)(Box*, void*);
void (*set)(Box*, Box*, void*); union {
void* set;
void (*set_pyston)(Box*, Box*, void*);
int (*set_capi)(Box*, Box*, void*);
};
void* closure; void* closure;
BoxedString* name; BoxedString* name;
BoxedGetsetDescriptor(BoxedString* name, Box* (*get)(Box*, void*), void (*set)(Box*, Box*, void*), void* closure) BoxedGetsetDescriptor(BoxedString* name, Box* (*get)(Box*, void*), void (*set)(Box*, Box*, void*), void* closure)
: get(get), set(set), closure(closure), name(name) { : get(get), set_pyston(set), closure(closure), name(name) {
assert(this->cls == pyston_getset_cls);
Py_INCREF(name);
}
BoxedGetsetDescriptor(BoxedString* name, Box* (*get)(Box*, void*), int (*set)(Box*, Box*, void*), void* closure)
: get(get), set_capi(set), closure(closure), name(name) {
assert(this->cls == capi_getset_cls);
Py_INCREF(name); Py_INCREF(name);
} }
......
# expected: reffail
# Tests to make sure that setting __class__ changes the class, and that it's ok to disallow # Tests to make sure that setting __class__ changes the class, and that it's ok to disallow
# having anything other than a type as the class # having anything other than a type as the class
class C(object): class C(object):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment