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 {
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;
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;
PyObject* slot_tp_getattr_hook(PyObject* self, PyObject* name) noexcept;
PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* 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;
template <ExceptionStyle S, Rewritable rewritable>
......
......@@ -725,8 +725,8 @@ public:
void clearAttrsForDealloc();
void giveAttrDescriptor(const char* attr, Box* (*get)(Box*, void*),
void (*set)(Box*, Box*, void*));
void giveAttrDescriptor(const char* attr, Box* (*get)(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
// 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
Py_FatalError("unimplemented");
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");
}
auto old_cls = inst->inst_cls;
inst->inst_cls = static_cast<BoxedClassobj*>(value);
......
......@@ -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 {
// TODO do something with __doc__
return new (capi_getset_cls) BoxedGetsetDescriptor(autoDecref(internStringMortal(y->name)), y->get,
(void (*)(Box*, Box*, void*))y->set, y->closure);
return new (capi_getset_cls)
BoxedGetsetDescriptor(autoDecref(internStringMortal(y->name)), y->get, y->set, y->closure);
}
extern "C" PyObject* PyDescr_NewClassMethod(PyTypeObject* type, PyMethodDef* method) noexcept {
......
......@@ -2940,8 +2940,13 @@ bool dataDescriptorSetSpecialCases(Box* obj, STOLEN(Box*) val, Box* descr, Setat
}
AUTO_DECREF(val);
getset_descr->set(obj, val, getset_descr->closure);
checkAndThrowCAPIException();
if (descr->cls == pyston_getset_cls) {
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;
} else if (descr->cls == member_descriptor_cls) {
......
......@@ -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));
}
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() {
assert(cls->instancesHaveHCAttrs());
HCAttrs* attrs = getHCAttrsPtr();
......@@ -3376,47 +3381,37 @@ done:
return result;
}
static Box* objectClass(Box* obj, void* context) {
assert(obj->cls != instance_cls); // should override __class__ in classobj
return incref(obj->cls);
static PyObject* object_get_class(PyObject* self, void* closure) noexcept {
Py_INCREF(Py_TYPE(self));
return (PyObject*)(Py_TYPE(self));
}
static void objectSetClass(Box* obj, Box* val, void* context) {
RELEASE_ASSERT(0, "check refcounting");
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);
static int object_set_class(PyObject* self, PyObject* value, void* closure) noexcept {
PyTypeObject* oldto = Py_TYPE(self);
PyTypeObject* newto;
// Conservative Pyston checks: make sure that both classes are derived only from Pyston types,
// and that they don't define any extra C-level fields
RELEASE_ASSERT(val->cls == type_cls, "");
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, "");
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "can't delete __class__ attribute");
return -1;
}
for (auto b : *static_cast<BoxedTuple*>(new_cls->tp_mro)) {
BoxedClass* base = static_cast<BoxedClass*>(b);
RELEASE_ASSERT(base->is_pyston_class, "");
if (!PyType_Check(value)) {
PyErr_Format(PyExc_TypeError, "__class__ must be set to new-style class, not '%s' object",
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[] = {
......@@ -3729,12 +3724,13 @@ static Box* getsetSet(Box* self, Box* obj, Box* val) {
}
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);
} else {
RELEASE_ASSERT(isSubclass(self->cls, capi_getset_cls), "");
getset_descr->set(obj, val, getset_descr->closure);
checkAndThrowCAPIException();
int r = getset_descr->set_capi(obj, val, getset_descr->closure);
if (r)
throwCAPIException();
return incref(None);
}
}
......@@ -4495,7 +4491,7 @@ void setupRuntime() {
for (auto& md : object_methods) {
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;
add_operators(object_cls);
......
......@@ -1137,12 +1137,23 @@ public:
class BoxedGetsetDescriptor : public Box {
public:
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;
BoxedString* name;
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);
}
......
# expected: reffail
# 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
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