Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Specialize LOAD_ATTR for custom __getattr__
  • Loading branch information
Fidget-Spinner committed Jun 18, 2022
commit 2f483f56a36b70c6dab60579841325fccad14939
2 changes: 2 additions & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ struct _typeobject {
* by code other than the specializer and interpreter. */
struct _specialization_cache {
PyObject *getitem;
PyObject *getattribute;
PyObject *getattr;
};

/* The *real* layout of a type object when allocated on the heap */
Expand Down
44 changes: 23 additions & 21 deletions Include/internal/pycore_opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Include/internal/pycore_typeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ extern PyStatus _PyTypes_InitSlotDefs(void);
extern void _PyStaticType_Dealloc(PyTypeObject *type);


extern PyObject *_Py_slot_tp_getattro(PyObject *self, PyObject *name);
extern PyObject *_Py_slot_tp_getattr_hook(PyObject *self, PyObject *name);
extern PyObject *_Py_call_attribute(PyObject *self, PyObject *attr, PyObject *name);
Comment thread
Fidget-Spinner marked this conversation as resolved.
Outdated
#ifdef __cplusplus
}
#endif
Expand Down
60 changes: 31 additions & 29 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@ def jabs_op(name, op):
"LOAD_ATTR_ADAPTIVE",
# These potentially push [NULL, bound method] onto the stack.
"LOAD_ATTR_CLASS",
"LOAD_ATTR_GETATTRIBUTE_GETATTR_OVERRIDDEN",
"LOAD_ATTR_GETATTRIBUTE_GETATTR_PY_OVERRIDDEN",
"LOAD_ATTR_INSTANCE_VALUE",
"LOAD_ATTR_MODULE",
"LOAD_ATTR_PROPERTY",
Expand Down
38 changes: 19 additions & 19 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -7766,24 +7766,24 @@ slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds)

/* There are two slot dispatch functions for tp_getattro.

- slot_tp_getattro() is used when __getattribute__ is overridden
- _Py_slot_tp_getattro() is used when __getattribute__ is overridden
but no __getattr__ hook is present;

- slot_tp_getattr_hook() is used when a __getattr__ hook is present.
- _Py_slot_tp_getattr_hook() is used when a __getattr__ hook is present.

The code in update_one_slot() always installs slot_tp_getattr_hook(); this
detects the absence of __getattr__ and then installs the simpler slot if
necessary. */
The code in update_one_slot() always installs _Py_slot_tp_getattr_hook();
this detects the absence of __getattr__ and then installs the simpler
slot if necessary. */

static PyObject *
slot_tp_getattro(PyObject *self, PyObject *name)
PyObject *
_Py_slot_tp_getattro(PyObject *self, PyObject *name)
{
PyObject *stack[2] = {self, name};
return vectorcall_method(&_Py_ID(__getattribute__), stack, 2);
}

static inline PyObject *
call_attribute(PyObject *self, PyObject *attr, PyObject *name)
PyObject *
_Py_call_attribute(PyObject *self, PyObject *attr, PyObject *name)
{
PyObject *res, *descr = NULL;

Expand All @@ -7807,8 +7807,8 @@ call_attribute(PyObject *self, PyObject *attr, PyObject *name)
return res;
}

static PyObject *
slot_tp_getattr_hook(PyObject *self, PyObject *name)
PyObject *
_Py_slot_tp_getattr_hook(PyObject *self, PyObject *name)
{
PyTypeObject *tp = Py_TYPE(self);
PyObject *getattr, *getattribute, *res;
Expand All @@ -7817,19 +7817,19 @@ slot_tp_getattr_hook(PyObject *self, PyObject *name)
method fully for each attribute lookup for classes with
__getattr__, even when the attribute is present. So we use
_PyType_Lookup and create the method only when needed, with
call_attribute. */
_Py_call_attribute. */
getattr = _PyType_Lookup(tp, &_Py_ID(__getattr__));
if (getattr == NULL) {
/* No __getattr__ hook: use a simpler dispatcher */
tp->tp_getattro = slot_tp_getattro;
return slot_tp_getattro(self, name);
tp->tp_getattro = _Py_slot_tp_getattro;
return _Py_slot_tp_getattro(self, name);
}
Py_INCREF(getattr);
/* speed hack: we could use lookup_maybe, but that would resolve the
method fully for each attribute lookup for classes with
__getattr__, even when self has the default __getattribute__
method. So we use _PyType_Lookup and create the method only when
needed, with call_attribute. */
needed, with _Py_call_attribute. */
getattribute = _PyType_Lookup(tp, &_Py_ID(__getattribute__));
if (getattribute == NULL ||
(Py_IS_TYPE(getattribute, &PyWrapperDescr_Type) &&
Expand All @@ -7838,12 +7838,12 @@ slot_tp_getattr_hook(PyObject *self, PyObject *name)
res = PyObject_GenericGetAttr(self, name);
else {
Py_INCREF(getattribute);
res = call_attribute(self, getattribute, name);
res = _Py_call_attribute(self, getattribute, name);
Py_DECREF(getattribute);
}
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
res = call_attribute(self, getattr, name);
res = _Py_call_attribute(self, getattr, name);
}
Py_DECREF(getattr);
return res;
Expand Down Expand Up @@ -8182,10 +8182,10 @@ static slotdef slotdefs[] = {
PyWrapperFlag_KEYWORDS),
TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc,
"__str__($self, /)\n--\n\nReturn str(self)."),
TPSLOT("__getattribute__", tp_getattro, slot_tp_getattr_hook,
TPSLOT("__getattribute__", tp_getattro, _Py_slot_tp_getattr_hook,
wrap_binaryfunc,
"__getattribute__($self, name, /)\n--\n\nReturn getattr(self, name)."),
TPSLOT("__getattr__", tp_getattro, slot_tp_getattr_hook, NULL, ""),
TPSLOT("__getattr__", tp_getattro, _Py_slot_tp_getattr_hook, NULL, ""),
TPSLOT("__setattr__", tp_setattro, slot_tp_setattro, wrap_setattr,
"__setattr__($self, name, value, /)\n--\n\nImplement setattr(self, name, value)."),
TPSLOT("__delattr__", tp_setattro, slot_tp_setattro, wrap_delattr,
Expand Down
Loading