Commit debf2077 authored by Rudi Chen's avatar Rudi Chen

Add support for the __del__ slot and some refactoring.

Make sure __del__ methods get assigned to tp_del in new style classes.

Go through an attribute lookup in old style classes. Also, change the
function calling structure to match CPython's a bit better.

For exceptions thrown in tp_del methods, print a warning and ignore
them, like CPython does.
parent d098297f
...@@ -977,6 +977,26 @@ static PyObject* slot_tp_getattr_hook(PyObject* self, PyObject* name) noexcept { ...@@ -977,6 +977,26 @@ static PyObject* slot_tp_getattr_hook(PyObject* self, PyObject* name) noexcept {
} }
} }
static PyObject* slot_tp_del(PyObject* self) noexcept {
static BoxedString* del_str = internStringImmortal("__del__");
try {
// TODO: runtime ICs?
Box* del_attr = typeLookup(self->cls, del_str, NULL);
assert(del_attr);
CallattrFlags flags{.cls_only = false,
.null_on_nonexistent = true,
.argspec = ArgPassSpec(0, 0, false, false) };
return callattr(self, del_str, flags, NULL, NULL, NULL, NULL, NULL);
} catch (ExcInfo e) {
// Python does not support exceptions thrown inside finalizers. Instead, it just
// prints a warning that an exception was throw to stderr but ignores it.
setCAPIException(e);
PyErr_WriteUnraisable(self);
return NULL;
}
}
static int slot_tp_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept { static int slot_tp_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept {
STAT_TIMER(t0, "us_timer_slot_tpinit", SLOT_AVOIDABILITY(self)); STAT_TIMER(t0, "us_timer_slot_tpinit", SLOT_AVOIDABILITY(self));
...@@ -1467,6 +1487,7 @@ static slotdef slotdefs[] ...@@ -1467,6 +1487,7 @@ static slotdef slotdefs[]
"see help(type(x)) for signature", "see help(type(x)) for signature",
PyWrapperFlag_KEYWORDS), PyWrapperFlag_KEYWORDS),
TPSLOT("__new__", tp_new, slot_tp_new, NULL, ""), TPSLOT("__new__", tp_new, slot_tp_new, NULL, ""),
TPSLOT("__del__", tp_del, slot_tp_del, NULL, ""),
TPPSLOT("__hasnext__", tpp_hasnext, slotTppHasnext, wrapInquirypred, "hasnext"), TPPSLOT("__hasnext__", tpp_hasnext, slotTppHasnext, wrapInquirypred, "hasnext"),
BINSLOT("__add__", nb_add, slot_nb_add, "+"), // [force clang-format to line break] BINSLOT("__add__", nb_add, slot_nb_add, "+"), // [force clang-format to line break]
......
...@@ -251,52 +251,73 @@ Box* classobjStr(Box* _obj) { ...@@ -251,52 +251,73 @@ Box* classobjStr(Box* _obj) {
return boxStringTwine(llvm::Twine(static_cast<BoxedString*>(_mod)->s()) + "." + cls->name->s()); return boxStringTwine(llvm::Twine(static_cast<BoxedString*>(_mod)->s()) + "." + cls->name->s());
} }
static Box* _instanceGetattribute(Box* _inst, Box* _attr, bool raise_on_missing) {
STAT_TIMER(t0, "us_timer_instance_getattribute", 0);
RELEASE_ASSERT(_inst->cls == instance_cls, "");
BoxedInstance* inst = static_cast<BoxedInstance*>(_inst);
RELEASE_ASSERT(_attr->cls == str_cls, "");
BoxedString* attr = static_cast<BoxedString*>(_attr);
// These are special cases in CPython as well:
if (attr->s()[0] == '_' && attr->s()[1] == '_') {
if (attr->s() == "__dict__")
return inst->getAttrWrapper();
if (attr->s() == "__class__") // Analogous to CPython's instance_getattr2
return inst->inst_cls; static Box* instanceGetattributeSimple(BoxedInstance* inst, BoxedString* attr_str) {
} Box* r = inst->getattr(attr_str);
Box* r = inst->getattr(attr);
if (r) if (r)
return r; return r;
r = classLookup(inst->inst_cls, attr); r = classLookup(inst->inst_cls, attr_str);
if (r) { if (r) {
return processDescriptor(r, inst, inst->inst_cls); return processDescriptor(r, inst, inst->inst_cls);
} }
RELEASE_ASSERT(!r, "");
return NULL;
}
static Box* instanceGetattributeWithFallback(BoxedInstance* inst, BoxedString* attr_str) {
Box* attr_obj = instanceGetattributeSimple(inst, attr_str);
if (attr_obj) {
return attr_obj;
}
static BoxedString* getattr_str = internStringImmortal("__getattr__"); static BoxedString* getattr_str = internStringImmortal("__getattr__");
Box* getattr = classLookup(inst->inst_cls, getattr_str); Box* getattr = classLookup(inst->inst_cls, getattr_str);
if (getattr) { if (getattr) {
getattr = processDescriptor(getattr, inst, inst->inst_cls); getattr = processDescriptor(getattr, inst, inst->inst_cls);
return runtimeCall(getattr, ArgPassSpec(1), _attr, NULL, NULL, NULL, NULL); return runtimeCall(getattr, ArgPassSpec(1), attr_str, NULL, NULL, NULL, NULL);
} }
if (!raise_on_missing)
return NULL; return NULL;
}
raiseExcHelper(AttributeError, "%s instance has no attribute '%s'", inst->inst_cls->name->data(), attr->data()); static Box* _instanceGetattribute(Box* _inst, BoxedString* attr_str, bool raise_on_missing) {
RELEASE_ASSERT(_inst->cls == instance_cls, "");
BoxedInstance* inst = static_cast<BoxedInstance*>(_inst);
// These are special cases in CPython as well:
if (attr_str->s()[0] == '_' && attr_str->s()[1] == '_') {
if (attr_str->s() == "__dict__")
return inst->getAttrWrapper();
if (attr_str->s() == "__class__")
return inst->inst_cls;
}
Box* attr = instanceGetattributeWithFallback(inst, attr_str);
if (attr) {
return attr;
} else if (!raise_on_missing) {
return NULL;
} else {
raiseExcHelper(AttributeError, "%s instance has no attribute '%s'", inst->inst_cls->name->data(),
attr_str->data());
}
} }
Box* instanceGetattribute(Box* _inst, Box* _attr) { Box* instanceGetattribute(Box* _inst, Box* _attr) {
return _instanceGetattribute(_inst, _attr, true); STAT_TIMER(t0, "us_timer_instance_getattribute", 0);
RELEASE_ASSERT(_attr->cls == str_cls, "");
BoxedString* attr = static_cast<BoxedString*>(_attr);
return _instanceGetattribute(_inst, attr, true);
} }
// Analogous to CPython's instance_getattr
static Box* instance_getattro(Box* cls, Box* attr) noexcept { static Box* instance_getattro(Box* cls, Box* attr) noexcept {
try { try {
return instanceGetattribute(cls, attr); return instanceGetattribute(cls, attr);
...@@ -716,16 +737,9 @@ static Box* instanceNext(BoxedInstance* inst) { ...@@ -716,16 +737,9 @@ static Box* instanceNext(BoxedInstance* inst) {
static PyObject* instance_index(PyObject* self) noexcept { static PyObject* instance_index(PyObject* self) noexcept {
PyObject* func, *res; PyObject* func, *res;
/*
static PyObject* indexstr = NULL;
if (indexstr == NULL) { static BoxedString* index_str = internStringImmortal("__index__");
indexstr = PyString_InternFromString("__index__"); if ((func = instance_getattro(self, index_str)) == NULL) {
if (indexstr == NULL)
return NULL;
}
*/
if ((func = instance_getattro(self, boxString("__index__"))) == NULL) {
if (!PyErr_ExceptionMatches(PyExc_AttributeError)) if (!PyErr_ExceptionMatches(PyExc_AttributeError))
return NULL; return NULL;
PyErr_Clear(); PyErr_Clear();
...@@ -737,6 +751,18 @@ static PyObject* instance_index(PyObject* self) noexcept { ...@@ -737,6 +751,18 @@ static PyObject* instance_index(PyObject* self) noexcept {
return res; return res;
} }
static void instance_dealloc(Box* _inst) {
RELEASE_ASSERT(_inst->cls == instance_cls, "");
BoxedInstance* inst = static_cast<BoxedInstance*>(_inst);
// Note that trying to call __del__ as a finalizer does not fallback to
// __getattr__ unlike other attributes (like __index__). This is CPython's behavior.
static BoxedString* del_str = internStringImmortal("__del__");
Box* func = instanceGetattributeSimple(inst, del_str);
if (func)
runtimeCall(func, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
static Box* _instanceBinary(Box* _inst, Box* other, BoxedString* attr) { static Box* _instanceBinary(Box* _inst, Box* other, BoxedString* attr) {
RELEASE_ASSERT(_inst->cls == instance_cls, ""); RELEASE_ASSERT(_inst->cls == instance_cls, "");
BoxedInstance* inst = static_cast<BoxedInstance*>(_inst); BoxedInstance* inst = static_cast<BoxedInstance*>(_inst);
...@@ -893,5 +919,7 @@ void setupClassobj() { ...@@ -893,5 +919,7 @@ void setupClassobj() {
instance_cls->tp_getattro = instance_getattro; instance_cls->tp_getattro = instance_getattro;
instance_cls->tp_setattro = instance_setattro; instance_cls->tp_setattro = instance_setattro;
instance_cls->tp_as_number->nb_index = instance_index; instance_cls->tp_as_number->nb_index = instance_index;
instance_cls->tp_dealloc = instance_dealloc;
instance_cls->has_safe_tp_dealloc = false;
} }
} }
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