Commit 2d565c4c authored by Kevin Modzelewski's avatar Kevin Modzelewski

Allow attrwrappers with deallocd underlying objects

ie something like "print C().__dict__"

The attrwrapper objects don't (arnd probably shouldn't) keep their underlying
objects alive.  Previous to this commit they would just try to access their
freed underlying object and crash.

With this commit, object deallocation will check if there is an attrwrapper,
and if so convert it to be privately-backed.
parent 22aff9b5
...@@ -649,8 +649,11 @@ public: ...@@ -649,8 +649,11 @@ public:
HCAttrs(HiddenClass* hcls = NULL) : hcls(hcls), attr_list(nullptr) {} HCAttrs(HiddenClass* hcls = NULL) : hcls(hcls), attr_list(nullptr) {}
int traverse(visitproc visit, void* arg) noexcept; int traverse(visitproc visit, void* arg) noexcept;
void clear() noexcept;
void moduleClear() noexcept; // slightly different order of clearing attributes, meant for modules void _clearRaw() noexcept; // Raw clear -- clears out and decrefs all the attrs.
// Meant for implementing other clear-like functions
void clearForDealloc() noexcept; // meant for normal object deallocation. converts the attrwrapper
void moduleClear() noexcept; // Meant for _PyModule_Clear. doesn't clear all attributes.
}; };
static_assert(sizeof(HCAttrs) == sizeof(struct _hcattrs), ""); static_assert(sizeof(HCAttrs) == sizeof(struct _hcattrs), "");
...@@ -715,7 +718,7 @@ public: ...@@ -715,7 +718,7 @@ public:
} }
void giveAttr(STOLEN(BoxedString*) attr, STOLEN(Box*) val); void giveAttr(STOLEN(BoxedString*) attr, STOLEN(Box*) val);
void clearAttrs(); 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*));
......
...@@ -1751,7 +1751,7 @@ void BoxedInstance::dealloc(Box* b) noexcept { ...@@ -1751,7 +1751,7 @@ void BoxedInstance::dealloc(Box* b) noexcept {
} }
Py_DECREF(inst->inst_cls); Py_DECREF(inst->inst_cls);
inst->attrs.clear(); inst->attrs.clearForDealloc();
PyObject_GC_Del(inst); PyObject_GC_Del(inst);
} else { } else {
Py_ssize_t refcnt = inst->ob_refcnt; Py_ssize_t refcnt = inst->ob_refcnt;
...@@ -1801,7 +1801,7 @@ void BoxedClassobj::dealloc(Box* b) noexcept { ...@@ -1801,7 +1801,7 @@ void BoxedClassobj::dealloc(Box* b) noexcept {
if (cl->weakreflist) if (cl->weakreflist)
PyObject_ClearWeakRefs(cl); PyObject_ClearWeakRefs(cl);
cl->clearAttrs(); cl->clearAttrsForDealloc();
cl->cls->tp_free(cl); cl->cls->tp_free(cl);
} }
......
...@@ -490,7 +490,7 @@ static void subtype_dealloc(Box* self) noexcept { ...@@ -490,7 +490,7 @@ static void subtype_dealloc(Box* self) noexcept {
// Pyston addition: same for hcattrs // Pyston addition: same for hcattrs
if (type->attrs_offset && !base->attrs_offset) { if (type->attrs_offset && !base->attrs_offset) {
self->getHCAttrsPtr()->clear(); self->getHCAttrsPtr()->clearForDealloc();
} }
/* Extract the type again; tp_del may have changed it */ /* Extract the type again; tp_del may have changed it */
...@@ -866,7 +866,7 @@ static int subtype_clear(PyObject* self) noexcept { ...@@ -866,7 +866,7 @@ static int subtype_clear(PyObject* self) noexcept {
} }
if (type->attrs_offset != base->attrs_offset) { if (type->attrs_offset != base->attrs_offset) {
self->getHCAttrsPtr()->clear(); self->getHCAttrsPtr()->clearForDealloc();
} }
if (baseclear) if (baseclear)
...@@ -1233,25 +1233,56 @@ static HCAttrs::AttrList* reallocAttrs(HCAttrs::AttrList* attrs, int old_nattrs, ...@@ -1233,25 +1233,56 @@ static HCAttrs::AttrList* reallocAttrs(HCAttrs::AttrList* attrs, int old_nattrs,
return rtn; return rtn;
} }
void HCAttrs::clear() noexcept { void Box::setDictBacked(STOLEN(Box*) val) {
HiddenClass* hcls = this->hcls; // this checks for: v.__dict__ = v.__dict__
if (val->cls == attrwrapper_cls && unwrapAttrWrapper(val) == this) {
Py_DECREF(val);
return;
}
if (!hcls) assert(this->cls->instancesHaveHCAttrs());
HCAttrs* hcattrs = this->getHCAttrsPtr();
RELEASE_ASSERT(PyDict_Check(val) || val->cls == attrwrapper_cls, "");
if (hcattrs->hcls->type == HiddenClass::DICT_BACKED) {
auto old_dict = hcattrs->attr_list->attrs[0];
hcattrs->attr_list->attrs[0] = val;
Py_DECREF(old_dict);
return; return;
}
if (unlikely(hcls->type == HiddenClass::DICT_BACKED)) { // If there is an old attrwrapper it is not allowed to wrap the instance anymore instead it has to switch to a
Box* d = this->attr_list->attrs[0]; // private dictonary.
// e.g.:
// a = v.__dict__
// v.__dict__ = {} # 'a' must switch now from wrapping 'v' to a the private dict.
int offset = hcattrs->hcls->getAttrwrapperOffset();
if (offset != -1) {
Box* wrapper = hcattrs->attr_list->attrs[offset];
RELEASE_ASSERT(wrapper->cls == attrwrapper_cls, "");
convertAttrwrapperToPrivateDict(wrapper);
}
// Skips the attrlist freelist // assign the dict to the attribute list and switch to the dict backed strategy
PyObject_FREE(this->attr_list); // Skips the attrlist freelist
this->attr_list = NULL; auto new_attr_list = (HCAttrs::AttrList*)PyObject_MALLOC(sizeof(HCAttrs::AttrList) + sizeof(Box*));
new_attr_list->attrs[0] = val;
Py_DECREF(d); auto old_attr_list = hcattrs->attr_list;
int old_attr_list_size = hcattrs->hcls->attributeArraySize();
return; hcattrs->hcls = HiddenClass::dict_backed;
} hcattrs->attr_list = new_attr_list;
assert(hcls->type == HiddenClass::NORMAL || hcls->type == HiddenClass::SINGLETON); decrefArray(old_attr_list->attrs, old_attr_list_size);
freeAttrs(old_attr_list, old_attr_list_size);
}
void HCAttrs::_clearRaw() noexcept {
HiddenClass* hcls = this->hcls;
if (!hcls)
return;
auto old_attr_list = this->attr_list; auto old_attr_list = this->attr_list;
auto old_attr_list_size = hcls->attributeArraySize(); auto old_attr_list_size = hcls->attributeArraySize();
...@@ -1261,8 +1292,30 @@ void HCAttrs::clear() noexcept { ...@@ -1261,8 +1292,30 @@ void HCAttrs::clear() noexcept {
if (old_attr_list) { if (old_attr_list) {
decrefArray(old_attr_list->attrs, old_attr_list_size); decrefArray(old_attr_list->attrs, old_attr_list_size);
freeAttrs(old_attr_list, old_attr_list_size); // DICT_BACKED attrs don't use the freelist:
if (hcls->type == HiddenClass::DICT_BACKED)
PyObject_FREE(old_attr_list);
else
freeAttrs(old_attr_list, old_attr_list_size);
}
}
void HCAttrs::clearForDealloc() noexcept {
HiddenClass* hcls = this->hcls;
if (!hcls)
return;
if (hcls->type == HiddenClass::NORMAL || hcls->type == HiddenClass::SINGLETON) {
int offset = hcls->getAttrwrapperOffset();
if (offset != -1) {
Box* attrwrapper = this->attr_list->attrs[offset];
if (attrwrapper->ob_refcnt != 1)
convertAttrwrapperToPrivateDict(attrwrapper);
}
} }
_clearRaw();
} }
void HCAttrs::moduleClear() noexcept { void HCAttrs::moduleClear() noexcept {
......
...@@ -405,7 +405,7 @@ static void functionDtor(Box* b) { ...@@ -405,7 +405,7 @@ static void functionDtor(Box* b) {
self->dependent_ics.invalidateAll(); self->dependent_ics.invalidateAll();
self->dependent_ics.~ICInvalidator(); self->dependent_ics.~ICInvalidator();
self->clearAttrs(); self->clearAttrsForDealloc();
Py_DECREF(self->doc); Py_DECREF(self->doc);
Py_XDECREF(self->modname); Py_XDECREF(self->modname);
...@@ -2166,10 +2166,17 @@ private: ...@@ -2166,10 +2166,17 @@ private:
if (isDictBacked()) if (isDictBacked())
return; return;
// TODO: this means that future accesses to __dict__ will return something other than
// this attrwrapper. We should store the attrwrapper in the attributes array.
HCAttrs* attrs = this->b->getHCAttrsPtr(); HCAttrs* attrs = this->b->getHCAttrsPtr();
assert(attrs->hcls->type != HiddenClass::DICT_BACKED); assert(attrs->hcls->type != HiddenClass::DICT_BACKED);
BoxedDict* d = (BoxedDict*)AttrWrapper::copy(this); BoxedDict* d = (BoxedDict*)AttrWrapper::copy(this);
b->clearAttrs();
b->getHCAttrsPtr()->_clearRaw();
assert(this->b);
assert(!private_dict);
HCAttrs* hcattrs = b->getHCAttrsPtr(); HCAttrs* hcattrs = b->getHCAttrsPtr();
// Skips the attrlist freelist: // Skips the attrlist freelist:
auto new_attr_list = (HCAttrs::AttrList*)PyObject_MALLOC(sizeof(HCAttrs::AttrList) + sizeof(Box*)); auto new_attr_list = (HCAttrs::AttrList*)PyObject_MALLOC(sizeof(HCAttrs::AttrList) + sizeof(Box*));
...@@ -2211,7 +2218,6 @@ public: ...@@ -2211,7 +2218,6 @@ public:
RELEASE_ASSERT(b, ""); RELEASE_ASSERT(b, "");
private_dict = (BoxedDict*)AttrWrapper::copy(this); private_dict = (BoxedDict*)AttrWrapper::copy(this);
assert(PyDict_CheckExact(private_dict)); assert(PyDict_CheckExact(private_dict));
b->clearAttrs();
b = NULL; b = NULL;
} }
...@@ -2610,7 +2616,7 @@ public: ...@@ -2610,7 +2616,7 @@ public:
HCAttrs* attrs = self->b->getHCAttrsPtr(); HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL || attrs->hcls->type == HiddenClass::SINGLETON, ""); RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL || attrs->hcls->type == HiddenClass::SINGLETON, "");
attrs->clear(); attrs->_clearRaw();
// Add the existing attrwrapper object (ie self) back as the attrwrapper: // Add the existing attrwrapper object (ie self) back as the attrwrapper:
self->b->appendNewHCAttr(self, NULL); self->b->appendNewHCAttr(self, NULL);
...@@ -2730,6 +2736,11 @@ public: ...@@ -2730,6 +2736,11 @@ public:
friend class AttrWrapperIter; friend class AttrWrapperIter;
}; };
void convertAttrwrapperToPrivateDict(Box* b) {
assert(b->cls == attrwrapper_cls);
static_cast<AttrWrapper*>(b)->convertToPrivateDict();
};
AttrWrapperIter::AttrWrapperIter(AttrWrapper* aw) { AttrWrapperIter::AttrWrapperIter(AttrWrapper* aw) {
hcls = aw->b->getHCAttrsPtr()->hcls; hcls = aw->b->getHCAttrsPtr()->hcls;
assert(hcls); assert(hcls);
...@@ -2822,38 +2833,6 @@ void attrwrapperSet(Box* b, Box* k, Box* v) { ...@@ -2822,38 +2833,6 @@ void attrwrapperSet(Box* b, Box* k, Box* v) {
} }
void Box::setDictBacked(STOLEN(Box*) val) {
// this checks for: v.__dict__ = v.__dict__
if (val->cls == attrwrapper_cls && unwrapAttrWrapper(val) == this) {
Py_DECREF(val);
return;
}
assert(this->cls->instancesHaveHCAttrs());
HCAttrs* hcattrs = this->getHCAttrsPtr();
RELEASE_ASSERT(PyDict_Check(val) || val->cls == attrwrapper_cls, "");
// If there is an old attrwrapper it is not allowed to wrap the instance anymore instead it has to switch to a
// private dictonary.
// e.g.:
// a = v.__dict__
// v.__dict__ = {} # 'a' must switch now from wrapping 'v' to a the private dict.
int offset = hcattrs->hcls->type != HiddenClass::DICT_BACKED ? hcattrs->hcls->getAttrwrapperOffset() : -1;
if (offset != -1) {
AttrWrapper* wrapper = (AttrWrapper*)hcattrs->attr_list->attrs[offset];
RELEASE_ASSERT(wrapper->cls == attrwrapper_cls, "");
wrapper->convertToPrivateDict();
}
// assign the dict to the attribute list and switch to the dict backed strategy
// Skips the attrlist freelist
auto new_attr_list = (HCAttrs::AttrList*)PyObject_MALLOC(sizeof(HCAttrs::AttrList) + sizeof(Box*));
new_attr_list->attrs[0] = val;
hcattrs->hcls = HiddenClass::dict_backed;
hcattrs->attr_list = new_attr_list;
}
Box* attrwrapperKeys(Box* b) { Box* attrwrapperKeys(Box* b) {
return AttrWrapper::keys(b); return AttrWrapper::keys(b);
} }
...@@ -3509,7 +3488,7 @@ extern "C" int PyObject_DelHcAttrString(PyObject* obj, const char* attr) PYSTON_ ...@@ -3509,7 +3488,7 @@ extern "C" int PyObject_DelHcAttrString(PyObject* obj, const char* attr) PYSTON_
} }
extern "C" int PyObject_ClearHcAttrs(HCAttrs* attrs) noexcept { extern "C" int PyObject_ClearHcAttrs(HCAttrs* attrs) noexcept {
attrs->clear(); attrs->clearForDealloc();
return 0; return 0;
} }
...@@ -3729,16 +3708,16 @@ static Box* getsetDelete(Box* self, Box* obj) { ...@@ -3729,16 +3708,16 @@ static Box* getsetDelete(Box* self, Box* obj) {
return getsetSet(self, obj, NULL); return getsetSet(self, obj, NULL);
} }
void Box::clearAttrs() { void Box::clearAttrsForDealloc() {
if (cls->instancesHaveHCAttrs()) { if (cls->instancesHaveHCAttrs()) {
HCAttrs* attrs = getHCAttrsPtr(); HCAttrs* attrs = getHCAttrsPtr();
attrs->clear(); attrs->clearForDealloc();
return; return;
} }
if (cls->instancesHaveDictAttrs()) { if (cls->instancesHaveDictAttrs()) {
BoxedDict* d = getDict(); BoxedDict** d = getDictPtr();
PyDict_Clear(d); Py_CLEAR(*d);
return; return;
} }
} }
...@@ -3839,7 +3818,7 @@ extern "C" void _PyModule_Clear(PyObject* b) noexcept { ...@@ -3839,7 +3818,7 @@ extern "C" void _PyModule_Clear(PyObject* b) noexcept {
int BoxedModule::clear(Box* b) noexcept { int BoxedModule::clear(Box* b) noexcept {
_PyModule_Clear(b); _PyModule_Clear(b);
b->clearAttrs(); b->clearAttrsForDealloc();
return 0; return 0;
} }
...@@ -3857,7 +3836,7 @@ void BoxedSlice::dealloc(Box* b) noexcept { ...@@ -3857,7 +3836,7 @@ void BoxedSlice::dealloc(Box* b) noexcept {
void BoxedInstanceMethod::dealloc(Box* b) noexcept { void BoxedInstanceMethod::dealloc(Box* b) noexcept {
BoxedInstanceMethod* im = static_cast<BoxedInstanceMethod*>(b); BoxedInstanceMethod* im = static_cast<BoxedInstanceMethod*>(b);
im->clearAttrs(); im->clearAttrsForDealloc();
_PyObject_GC_UNTRACK(im); _PyObject_GC_UNTRACK(im);
if (im->im_weakreflist != NULL) if (im->im_weakreflist != NULL)
...@@ -3896,7 +3875,7 @@ void BoxedClass::dealloc(Box* b) noexcept { ...@@ -3896,7 +3875,7 @@ void BoxedClass::dealloc(Box* b) noexcept {
if (PyObject_IS_GC(type)) if (PyObject_IS_GC(type))
_PyObject_GC_UNTRACK(type); _PyObject_GC_UNTRACK(type);
type->clearAttrs(); type->clearAttrsForDealloc();
Py_XDECREF(type->tp_dict); Py_XDECREF(type->tp_dict);
Py_XDECREF(type->tp_bases); Py_XDECREF(type->tp_bases);
...@@ -4003,7 +3982,7 @@ static int type_clear(PyTypeObject* type) { ...@@ -4003,7 +3982,7 @@ static int type_clear(PyTypeObject* type) {
PyType_Modified(type); PyType_Modified(type);
if (type->tp_dict) if (type->tp_dict)
PyDict_Clear(type->tp_dict); PyDict_Clear(type->tp_dict);
type->attrs.clear(); type->attrs.clearForDealloc();
Py_CLEAR(type->tp_dict); Py_CLEAR(type->tp_dict);
Py_CLEAR(type->tp_mro); Py_CLEAR(type->tp_mro);
...@@ -4816,7 +4795,7 @@ extern "C" void Py_Finalize() noexcept { ...@@ -4816,7 +4795,7 @@ extern "C" void Py_Finalize() noexcept {
for (auto b : classes) { for (auto b : classes) {
if (!PyObject_IS_GC(b)) { if (!PyObject_IS_GC(b)) {
b->clearAttrs(); b->getHCAttrsPtr()->_clearRaw();
Py_CLEAR(b->tp_mro); Py_CLEAR(b->tp_mro);
} }
Py_DECREF(b); Py_DECREF(b);
......
...@@ -1333,6 +1333,7 @@ public: ...@@ -1333,6 +1333,7 @@ public:
Box* objectSetattr(Box* obj, Box* attr, Box* value); Box* objectSetattr(Box* obj, Box* attr, Box* value);
BORROWED(Box*) unwrapAttrWrapper(Box* b); BORROWED(Box*) unwrapAttrWrapper(Box* b);
void convertAttrwrapperToPrivateDict(Box* b);
Box* attrwrapperKeys(Box* b); Box* attrwrapperKeys(Box* b);
void attrwrapperDel(Box* b, llvm::StringRef attr); void attrwrapperDel(Box* b, llvm::StringRef attr);
void attrwrapperClear(Box* b); void attrwrapperClear(Box* b);
......
# expected: reffail
try: try:
object().__dict__ = 1 object().__dict__ = 1
except AttributeError as e: except AttributeError as e:
......
# expected: reffail
def show(obj): def show(obj):
print obj.__class__ print obj.__class__
for b in obj.__class__.__mro__: for b in obj.__class__.__mro__:
......
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