Commit 0ac14234 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Add class-level caching of whether __getattribute__ exists

This is not quite the same as checking whether tp_getattro
has been updated, since setting a __getattr__ will change
tp_getattro.

Usually this isn't that important since we will still need
to do some special getattr handling, but in a couple specific
cases we can optimize based on it.  In particular, this commit
optimizes calls to isinstance(inst, cls) on instances which have
a __getattr__ set: in the common case (no __getattribute__,
no custom __class__) we can know that the __getattr__ will not
get called and we can stay on the isinstance fastpath.
parent f6c4298d
......@@ -457,7 +457,7 @@ struct _typeobject {
char _ics[32];
void* _gcvisit_func;
int _attrs_offset;
bool _flags[6];
bool _flags[7];
void* _tpp_descr_get;
void* _tpp_hasnext;
void* _tpp_call;
......
......@@ -17,6 +17,7 @@
#include "Python.h"
#include "capi/typeobject.h"
#include "capi/types.h"
#include "core/ast.h"
#include "core/threading.h"
......@@ -405,12 +406,22 @@ static int recursive_isinstance(PyObject* inst, PyObject* cls) noexcept {
retval = PyObject_TypeCheck(inst, (PyTypeObject*)cls);
if (retval == 0) {
PyObject* c = NULL;
if (inst->cls->has___class__ || inst->cls->tp_getattr != object_cls->tp_getattr
|| inst->cls->tp_getattro != object_cls->tp_getattro) {
if (!inst->cls->has_getattribute) {
assert(inst->cls->tp_getattr == object_cls->tp_getattr);
assert(inst->cls->tp_getattro == object_cls->tp_getattro
|| inst->cls->tp_getattro == slot_tp_getattr_hook);
}
// We don't need to worry about __getattr__, since the default __class__ will always resolve.
bool has_custom_class = inst->cls->has___class__ || inst->cls->has_getattribute;
if (!has_custom_class) {
assert(PyObject_GetAttr(inst, __class__) == inst->cls);
} else {
c = PyObject_GetAttr(inst, __class__);
if (!c)
PyErr_Clear();
}
if (c) {
if (c != (PyObject*)(inst->cls) && PyType_Check(c))
retval = PyType_IsSubtype((PyTypeObject*)c, (PyTypeObject*)cls);
......
......@@ -1615,6 +1615,7 @@ static slotdef slotdefs[]
TPSLOT("__del__", tp_del, slot_tp_del, NULL, ""),
FLSLOT("__class__", has___class__, NULL, NULL, "", PyWrapperFlag_BOOL),
FLSLOT("__instancecheck__", has_instancecheck, NULL, NULL, "", PyWrapperFlag_BOOL),
FLSLOT("__getattribute__", has_getattribute, NULL, NULL, "", PyWrapperFlag_BOOL),
TPPSLOT("__hasnext__", tpp_hasnext, slotTppHasnext, wrapInquirypred, "hasnext"),
BINSLOT("__add__", nb_add, slot_nb_add, "+"), // [force clang-format to line break]
......@@ -1807,6 +1808,13 @@ static const slotdef* update_one_slot(BoxedClass* type, const slotdef* p) noexce
descr = NULL;
}
static BoxedString* getattribute_str = internStringImmortal("__getattribute__");
if (p->name_strobj == getattribute_str) {
if (descr && descr->cls == wrapperdescr_cls
&& ((BoxedWrapperDescriptor*)descr)->wrapped == PyObject_GenericGetAttr)
descr = NULL;
}
*(bool*)ptr = (bool)descr;
return p + 1;
}
......
......@@ -232,6 +232,7 @@ public:
bool has___class__; // Has a custom __class__ attribute (ie different from object's __class__ descriptor)
bool has_instancecheck;
bool has_getattribute;
typedef bool (*pyston_inquiry)(Box*);
......
......@@ -91,3 +91,35 @@ print "checking superclass instancecheck:"
print isinstance(1, C)
M.__instancecheck__ = m_instancecheck
print isinstance(1, C)
print
class C(object):
@property
def __class__(self):
print "C.__class__ descriptor"
raise AttributeError()
def __getattr__(self, attr):
print "C.__getattr__", attr
return int
print "Testing __class__ from __getattr__"
c = C()
print c.__class__
print isinstance(c, int)
print
class C(object):
def __getattribute__(self, attr):
print "C.__getattribute__", attr
raise AttributeError()
def __getattr__(self, attr):
print "C.__getattr__", attr
return int
print "Testing __class__ from __getattr__+__getattribute__"
c = C()
print c.__class__
print isinstance(c, int)
print
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