static PyObject *
slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
-static PyObject *
-lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound);
-
static int
slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value);
-
static inline PyTypeObject *
type_from_ref(PyObject *ref)
{
static int
is_subtype_with_mro(PyObject *a_mro, PyTypeObject *a, PyTypeObject *b);
+// Check if the `mro` method on `type` is overridden, i.e.,
+// `type(tp).mro is not type.mro`.
+static int
+has_custom_mro(PyTypeObject *tp)
+{
+ _PyCStackRef c_ref1, c_ref2;
+ PyThreadState *tstate = _PyThreadState_GET();
+ _PyThreadState_PushCStackRef(tstate, &c_ref1);
+ _PyThreadState_PushCStackRef(tstate, &c_ref2);
+
+ _PyType_LookupStackRefAndVersion(Py_TYPE(tp), &_Py_ID(mro), &c_ref1.ref);
+ _PyType_LookupStackRefAndVersion(&PyType_Type, &_Py_ID(mro), &c_ref2.ref);
+
+ int custom = !PyStackRef_Is(c_ref1.ref, c_ref2.ref);
+
+ _PyThreadState_PopCStackRef(tstate, &c_ref2);
+ _PyThreadState_PopCStackRef(tstate, &c_ref1);
+ return custom;
+}
+
static void
-type_mro_modified(PyTypeObject *type, PyObject *bases) {
+type_mro_modified(PyTypeObject *type, PyObject *bases)
+{
/*
Check that all base classes or elements of the MRO of type are
able to be cached. This function is called after the base
each subclass when their mro is recursively updated.
*/
Py_ssize_t i, n;
- int custom = !Py_IS_TYPE(type, &PyType_Type);
- int unbound;
ASSERT_TYPE_LOCK_HELD();
- if (custom) {
- PyObject *mro_meth, *type_mro_meth;
- mro_meth = lookup_maybe_method(
- (PyObject *)type, &_Py_ID(mro), &unbound);
- if (mro_meth == NULL) {
- goto clear;
- }
- type_mro_meth = lookup_maybe_method(
- (PyObject *)&PyType_Type, &_Py_ID(mro), &unbound);
- if (type_mro_meth == NULL) {
- Py_DECREF(mro_meth);
- goto clear;
- }
- int custom_mro = (mro_meth != type_mro_meth);
- Py_DECREF(mro_meth);
- Py_DECREF(type_mro_meth);
- if (custom_mro) {
- goto clear;
- }
+ if (!Py_IS_TYPE(type, &PyType_Type) && has_custom_mro(type)) {
+ goto clear;
}
n = PyTuple_GET_SIZE(bases);
for (i = 0; i < n; i++) {
void
_PyType_SetVersion(PyTypeObject *tp, unsigned int version)
{
-
BEGIN_TYPE_LOCK();
set_version_unlocked(tp, version);
END_TYPE_LOCK();
return res;
}
-static PyObject *
-lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound)
+static int
+lookup_method_ex(PyObject *self, PyObject *attr, _PyStackRef *out,
+ int raise_attribute_error)
{
- PyObject *res = _PyType_LookupRef(Py_TYPE(self), attr);
- if (res == NULL) {
- return NULL;
+ _PyType_LookupStackRefAndVersion(Py_TYPE(self), attr, out);
+ if (PyStackRef_IsNull(*out)) {
+ if (raise_attribute_error) {
+ PyErr_SetObject(PyExc_AttributeError, attr);
+ }
+ return -1;
}
- if (_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
+ PyObject *value = PyStackRef_AsPyObjectBorrow(*out);
+ if (_PyType_HasFeature(Py_TYPE(value), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
/* Avoid temporary PyMethodObject */
- *unbound = 1;
+ return 1;
}
- else {
- *unbound = 0;
- descrgetfunc f = Py_TYPE(res)->tp_descr_get;
- if (f != NULL) {
- Py_SETREF(res, f(res, self, (PyObject *)(Py_TYPE(self))));
+
+ descrgetfunc f = Py_TYPE(value)->tp_descr_get;
+ if (f != NULL) {
+ value = f(value, self, (PyObject *)(Py_TYPE(self)));
+ PyStackRef_CLEAR(*out);
+ if (value == NULL) {
+ if (!raise_attribute_error &&
+ PyErr_ExceptionMatches(PyExc_AttributeError))
+ {
+ PyErr_Clear();
+ }
+ return -1;
}
+ *out = PyStackRef_FromPyObjectSteal(value);
}
- return res;
+ return 0;
}
-static PyObject *
-lookup_method(PyObject *self, PyObject *attr, int *unbound)
+static int
+lookup_maybe_method(PyObject *self, PyObject *attr, _PyStackRef *out)
{
- PyObject *res = lookup_maybe_method(self, attr, unbound);
- if (res == NULL && !PyErr_Occurred()) {
- PyErr_SetObject(PyExc_AttributeError, attr);
- }
- return res;
+ return lookup_method_ex(self, attr, out, 0);
+}
+
+static int
+lookup_method(PyObject *self, PyObject *attr, _PyStackRef *out)
+{
+ return lookup_method_ex(self, attr, out, 1);
}
}
}
+// Call the method with the name `attr` on `self`. Returns NULL with an
+// exception set if the method is missing or an error occurs.
+static PyObject *
+call_method_noarg(PyObject *self, PyObject *attr)
+{
+ PyThreadState *tstate = _PyThreadState_GET();
+ _PyCStackRef cref;
+ _PyThreadState_PushCStackRef(tstate, &cref);
+ PyObject *res = NULL;
+ int unbound = lookup_method(self, attr, &cref.ref);
+ if (unbound >= 0) {
+ PyObject *func = PyStackRef_AsPyObjectBorrow(cref.ref);
+ res = call_unbound_noarg(unbound, func, self);
+ }
+ _PyThreadState_PopCStackRef(tstate, &cref);
+ return res;
+}
+
+static PyObject *
+call_method(PyObject *self, PyObject *attr, PyObject *args, PyObject *kwds)
+{
+ PyThreadState *tstate = _PyThreadState_GET();
+ _PyCStackRef cref;
+ _PyThreadState_PushCStackRef(tstate, &cref);
+ PyObject *res = NULL;
+ int unbound = lookup_method(self, attr, &cref.ref);
+ if (unbound >= 0) {
+ PyObject *meth = PyStackRef_AsPyObjectBorrow(cref.ref);
+ if (unbound) {
+ res = _PyObject_Call_Prepend(tstate, meth, self, args, kwds);
+ }
+ else {
+ res = _PyObject_Call(tstate, meth, args, kwds);
+ }
+ }
+ _PyThreadState_PopCStackRef(tstate, &cref);
+ return res;
+}
+
/* A variation of PyObject_CallMethod* that uses lookup_method()
instead of PyObject_GetAttrString().
assert(nargs >= 1);
PyThreadState *tstate = _PyThreadState_GET();
- int unbound;
+ PyObject *retval = NULL;
PyObject *self = args[0];
- PyObject *func = lookup_method(self, name, &unbound);
- if (func == NULL) {
- return NULL;
- }
- PyObject *retval = vectorcall_unbound(tstate, unbound, func, args, nargs);
- Py_DECREF(func);
+ _PyCStackRef cref;
+ _PyThreadState_PushCStackRef(tstate, &cref);
+ int unbound = lookup_method(self, name, &cref.ref);
+ if (unbound >= 0) {
+ PyObject *func = PyStackRef_AsPyObjectBorrow(cref.ref);
+ retval = vectorcall_unbound(tstate, unbound, func, args, nargs);
+ }
+ _PyThreadState_PopCStackRef(tstate, &cref);
return retval;
}
{
assert(nargs >= 1);
- int unbound;
PyObject *self = args[0];
- PyObject *func = lookup_maybe_method(self, name, &unbound);
+ _PyCStackRef cref;
+ _PyThreadState_PushCStackRef(tstate, &cref);
+ int unbound = lookup_maybe_method(self, name, &cref.ref);
+ PyObject *func = PyStackRef_AsPyObjectBorrow(cref.ref);
if (func == NULL) {
- if (!PyErr_Occurred())
+ _PyThreadState_PopCStackRef(tstate, &cref);
+ if (!PyErr_Occurred()) {
Py_RETURN_NOTIMPLEMENTED;
+ }
return NULL;
}
PyObject *retval = vectorcall_unbound(tstate, unbound, func, args, nargs);
- Py_DECREF(func);
+ _PyThreadState_PopCStackRef(tstate, &cref);
return retval;
}
+/* Call the method with the name `attr` on `self`. Returns NULL if the
+ method is missing or an error occurs. No exception is set if
+ the method is missing. If attr_is_none is not NULL, it is set to 1 if
+ the attribute was found and was None, or 0 if it was not found. */
+static PyObject *
+maybe_call_special_no_args(PyObject *self, PyObject *attr, int *attr_is_none)
+{
+ PyThreadState *tstate = _PyThreadState_GET();
+ _PyCStackRef cref;
+ _PyThreadState_PushCStackRef(tstate, &cref);
+
+ PyObject *res = NULL;
+ int unbound = lookup_maybe_method(self, attr, &cref.ref);
+ PyObject *func = PyStackRef_AsPyObjectBorrow(cref.ref);
+ if (attr_is_none != NULL) {
+ *attr_is_none = (func == Py_None);
+ }
+ if (func != NULL && (func != Py_None || attr_is_none == NULL)) {
+ res = call_unbound_noarg(unbound, func, self);
+ }
+ _PyThreadState_PopCStackRef(tstate, &cref);
+ return res;
+}
+
+static PyObject *
+maybe_call_special_one_arg(PyObject *self, PyObject *attr, PyObject *arg,
+ int *attr_is_none)
+{
+ PyThreadState *tstate = _PyThreadState_GET();
+ _PyCStackRef cref;
+ _PyThreadState_PushCStackRef(tstate, &cref);
+
+ PyObject *res = NULL;
+ int unbound = lookup_maybe_method(self, attr, &cref.ref);
+ PyObject *func = PyStackRef_AsPyObjectBorrow(cref.ref);
+ if (attr_is_none != NULL) {
+ *attr_is_none = (func == Py_None);
+ }
+ if (func != NULL && (func != Py_None || attr_is_none == NULL)) {
+ PyObject *args[] = { self, arg };
+ res = vectorcall_unbound(tstate, unbound, func, args, 2);
+ }
+ _PyThreadState_PopCStackRef(tstate, &cref);
+ return res;
+}
+
+PyObject *
+_PyObject_MaybeCallSpecialNoArgs(PyObject *self, PyObject *attr)
+{
+ return maybe_call_special_no_args(self, attr, NULL);
+}
+
+PyObject *
+_PyObject_MaybeCallSpecialOneArg(PyObject *self, PyObject *attr, PyObject *arg)
+{
+ return maybe_call_special_one_arg(self, attr, arg, NULL);
+}
+
/*
Method resolution order algorithm C3 described in
"A Monotonic Superclass Linearization for Dylan",
const int custom = !Py_IS_TYPE(type, &PyType_Type);
if (custom) {
- int unbound;
- PyObject *mro_meth = lookup_method(
- (PyObject *)type, &_Py_ID(mro), &unbound);
- if (mro_meth == NULL)
- return NULL;
- mro_result = call_unbound_noarg(unbound, mro_meth, (PyObject *)type);
- Py_DECREF(mro_meth);
+ mro_result = call_method_noarg((PyObject *)type, &_Py_ID(mro));
}
else {
mro_result = mro_implementation_unlocked(type);
PyObject *
_PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *version)
{
- PyObject *res;
- int error;
- PyInterpreterState *interp = _PyInterpreterState_GET();
+ _PyStackRef out;
+ unsigned int ver = _PyType_LookupStackRefAndVersion(type, name, &out);
+ if (version) {
+ *version = ver;
+ }
+ if (PyStackRef_IsNull(out)) {
+ return NULL;
+ }
+ return PyStackRef_AsPyObjectSteal(out);
+}
+unsigned int
+_PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef *out)
+{
unsigned int h = MCACHE_HASH_METHOD(type, name);
struct type_cache *cache = get_type_cache();
struct type_cache_entry *entry = &cache->hashtable[h];
_Py_atomic_load_ptr_relaxed(&entry->name) == name) {
OBJECT_STAT_INC_COND(type_cache_hits, !is_dunder_name(name));
OBJECT_STAT_INC_COND(type_cache_dunder_hits, is_dunder_name(name));
- PyObject *value = _Py_atomic_load_ptr_relaxed(&entry->value);
- // If the sequence is still valid then we're done
- if (value == NULL || _Py_TryIncref(value)) {
+ if (_Py_TryXGetStackRef(&entry->value, out)) {
+ // If the sequence is still valid then we're done
if (_PySeqLock_EndRead(&entry->sequence, sequence)) {
- if (version != NULL) {
- *version = entry_version;
- }
- return value;
+ return entry_version;
}
- Py_XDECREF(value);
+ PyStackRef_XCLOSE(*out);
}
else {
// If we can't incref the object we need to fallback to locking
}
}
#else
- if (entry->version == type->tp_version_tag &&
- entry->name == name) {
+ if (entry->version == type->tp_version_tag && entry->name == name) {
assert(type->tp_version_tag);
OBJECT_STAT_INC_COND(type_cache_hits, !is_dunder_name(name));
OBJECT_STAT_INC_COND(type_cache_dunder_hits, is_dunder_name(name));
- Py_XINCREF(entry->value);
- if (version != NULL) {
- *version = entry->version;
- }
- return entry->value;
+ *out = entry->value ? PyStackRef_FromPyObjectNew(entry->value) : PyStackRef_NULL;
+ return entry->version;
}
#endif
OBJECT_STAT_INC_COND(type_cache_misses, !is_dunder_name(name));
// We need to atomically do the lookup and capture the version before
// anyone else can modify our mro or mutate the type.
+ PyObject *res;
+ int error;
+ PyInterpreterState *interp = _PyInterpreterState_GET();
int has_version = 0;
unsigned int assigned_version = 0;
BEGIN_TYPE_LOCK();
if (error == -1) {
PyErr_Clear();
}
- if (version != NULL) {
- // 0 is not a valid version
- *version = 0;
- }
- return NULL;
+ *out = PyStackRef_NULL;
+ return 0;
}
if (has_version) {
Py_DECREF(old_value);
#endif
}
- if (version != NULL) {
- // 0 is not a valid version
- *version = has_version ? assigned_version : 0;
- }
- return res;
+ *out = res ? PyStackRef_FromPyObjectSteal(res) : PyStackRef_NULL;
+ return has_version ? assigned_version : 0;
}
/* Internal API to look for a name through the MRO.
static int
slot_sq_contains(PyObject *self, PyObject *value)
{
- PyThreadState *tstate = _PyThreadState_GET();
- PyObject *func, *res;
- int result = -1, unbound;
-
- func = lookup_maybe_method(self, &_Py_ID(__contains__), &unbound);
- if (func == Py_None) {
- Py_DECREF(func);
+ int attr_is_none = 0;
+ PyObject *res = maybe_call_special_one_arg(self, &_Py_ID(__contains__), value,
+ &attr_is_none);
+ if (attr_is_none) {
PyErr_Format(PyExc_TypeError,
- "'%.200s' object is not a container",
- Py_TYPE(self)->tp_name);
+ "'%.200s' object is not a container",
+ Py_TYPE(self)->tp_name);
return -1;
}
- if (func != NULL) {
- PyObject *args[2] = {self, value};
- res = vectorcall_unbound(tstate, unbound, func, args, 2);
- Py_DECREF(func);
- if (res != NULL) {
- result = PyObject_IsTrue(res);
- Py_DECREF(res);
- }
+ else if (res == NULL && PyErr_Occurred()) {
+ return -1;
}
- else if (! PyErr_Occurred()) {
- /* Possible results: -1 and 1 */
- result = (int)_PySequence_IterSearch(self, value,
- PY_ITERSEARCH_CONTAINS);
+ else if (res == NULL) {
+ return (int)_PySequence_IterSearch(self, value, PY_ITERSEARCH_CONTAINS);
}
+ int result = PyObject_IsTrue(res);
+ Py_DECREF(res);
return result;
}
static int
slot_nb_bool(PyObject *self)
{
- PyObject *func, *value;
- int result, unbound;
int using_len = 0;
-
- func = lookup_maybe_method(self, &_Py_ID(__bool__), &unbound);
- if (func == NULL) {
- if (PyErr_Occurred()) {
- return -1;
- }
-
- func = lookup_maybe_method(self, &_Py_ID(__len__), &unbound);
- if (func == NULL) {
- if (PyErr_Occurred()) {
- return -1;
- }
+ int attr_is_none = 0;
+ PyObject *value = maybe_call_special_no_args(self, &_Py_ID(__bool__),
+ &attr_is_none);
+ if (attr_is_none) {
+ PyErr_Format(PyExc_TypeError,
+ "'%.200s' cannot be interpreted as a boolean",
+ Py_TYPE(self)->tp_name);
+ return -1;
+ }
+ else if (value == NULL && !PyErr_Occurred()) {
+ value = _PyObject_MaybeCallSpecialNoArgs(self, &_Py_ID(__len__));
+ if (value == NULL && !PyErr_Occurred()) {
return 1;
}
using_len = 1;
}
- value = call_unbound_noarg(unbound, func, self);
if (value == NULL) {
- goto error;
+ return -1;
}
+ int result;
if (using_len) {
/* bool type enforced by slot_nb_len */
result = PyObject_IsTrue(value);
Py_TYPE(value)->tp_name);
result = -1;
}
-
Py_DECREF(value);
- Py_DECREF(func);
return result;
-
-error:
- Py_DECREF(func);
- return -1;
}
static PyObject *
slot_tp_repr(PyObject *self)
{
- PyObject *func, *res;
- int unbound;
-
- func = lookup_maybe_method(self, &_Py_ID(__repr__), &unbound);
- if (func != NULL) {
- res = call_unbound_noarg(unbound, func, self);
- Py_DECREF(func);
+ PyObject *res = _PyObject_MaybeCallSpecialNoArgs(self, &_Py_ID(__repr__));
+ if (res != NULL) {
return res;
}
- PyErr_Clear();
+ else if (PyErr_Occurred()) {
+ return NULL;
+ }
return PyUnicode_FromFormat("<%s object at %p>",
- Py_TYPE(self)->tp_name, self);
+ Py_TYPE(self)->tp_name, self);
}
SLOT0(slot_tp_str, __str__)
static Py_hash_t
slot_tp_hash(PyObject *self)
{
- PyObject *func, *res;
- Py_ssize_t h;
- int unbound;
-
- func = lookup_maybe_method(self, &_Py_ID(__hash__), &unbound);
-
- if (func == Py_None) {
- Py_SETREF(func, NULL);
- }
-
- if (func == NULL) {
+ PyObject *res;
+ int attr_is_none = 0;
+ res = maybe_call_special_no_args(self, &_Py_ID(__hash__), &attr_is_none);
+ if (attr_is_none || res == NULL) {
+ if (PyErr_Occurred()) {
+ return -1;
+ }
return PyObject_HashNotImplemented(self);
}
-
- res = call_unbound_noarg(unbound, func, self);
- Py_DECREF(func);
- if (res == NULL)
- return -1;
-
if (!PyLong_Check(res)) {
PyErr_SetString(PyExc_TypeError,
"__hash__ method should return an integer");
Py_hash_t. Therefore our transformation must preserve values that
already lie within this range, to ensure that if x.__hash__() returns
hash(y) then hash(x) == hash(y). */
- h = PyLong_AsSsize_t(res);
+ Py_ssize_t h = PyLong_AsSsize_t(res);
if (h == -1 && PyErr_Occurred()) {
/* res was not within the range of a Py_hash_t, so we're free to
use any sufficiently bit-mixing transformation;
static PyObject *
slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds)
{
- PyThreadState *tstate = _PyThreadState_GET();
- int unbound;
-
- PyObject *meth = lookup_method(self, &_Py_ID(__call__), &unbound);
- if (meth == NULL) {
- return NULL;
- }
-
- PyObject *res;
- if (unbound) {
- res = _PyObject_Call_Prepend(tstate, meth, self, args, kwds);
- }
- else {
- res = _PyObject_Call(tstate, meth, args, kwds);
- }
-
- Py_DECREF(meth);
- return res;
+ return call_method(self, &_Py_ID(__call__), args, kwds);
}
/* There are two slot dispatch functions for tp_getattro.
static PyObject *
slot_tp_richcompare(PyObject *self, PyObject *other, int op)
{
- PyThreadState *tstate = _PyThreadState_GET();
-
- int unbound;
- PyObject *func = lookup_maybe_method(self, name_op[op], &unbound);
- if (func == NULL) {
- PyErr_Clear();
+ PyObject *res = _PyObject_MaybeCallSpecialOneArg(self, name_op[op], other);
+ if (res == NULL) {
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
Py_RETURN_NOTIMPLEMENTED;
}
-
- PyObject *stack[2] = {self, other};
- PyObject *res = vectorcall_unbound(tstate, unbound, func, stack, 2);
- Py_DECREF(func);
return res;
}
+static int
+has_dunder_getitem(PyObject *self)
+{
+ PyThreadState *tstate = _PyThreadState_GET();
+ _PyCStackRef c_ref;
+ _PyThreadState_PushCStackRef(tstate, &c_ref);
+ lookup_maybe_method(self, &_Py_ID(__getitem__), &c_ref.ref);
+ int has_dunder_getitem = !PyStackRef_IsNull(c_ref.ref);
+ _PyThreadState_PopCStackRef(tstate, &c_ref);
+ return has_dunder_getitem;
+}
+
static PyObject *
slot_tp_iter(PyObject *self)
{
- int unbound;
- PyObject *func, *res;
-
- func = lookup_maybe_method(self, &_Py_ID(__iter__), &unbound);
- if (func == Py_None) {
- Py_DECREF(func);
- PyErr_Format(PyExc_TypeError,
- "'%.200s' object is not iterable",
- Py_TYPE(self)->tp_name);
- return NULL;
- }
-
- if (func != NULL) {
- res = call_unbound_noarg(unbound, func, self);
- Py_DECREF(func);
+ int attr_is_none = 0;
+ PyObject *res = maybe_call_special_no_args(self, &_Py_ID(__iter__),
+ &attr_is_none);
+ if (res != NULL) {
return res;
}
-
- PyErr_Clear();
- func = lookup_maybe_method(self, &_Py_ID(__getitem__), &unbound);
- if (func == NULL) {
+ else if (PyErr_Occurred()) {
+ return NULL;
+ }
+ else if (attr_is_none || !has_dunder_getitem(self)) {
PyErr_Format(PyExc_TypeError,
- "'%.200s' object is not iterable",
- Py_TYPE(self)->tp_name);
+ "'%.200s' object is not iterable",
+ Py_TYPE(self)->tp_name);
return NULL;
}
- Py_DECREF(func);
return PySeqIter_New(self);
}
static int
slot_tp_init(PyObject *self, PyObject *args, PyObject *kwds)
{
- PyThreadState *tstate = _PyThreadState_GET();
-
- int unbound;
- PyObject *meth = lookup_method(self, &_Py_ID(__init__), &unbound);
- if (meth == NULL) {
- return -1;
- }
-
- PyObject *res;
- if (unbound) {
- res = _PyObject_Call_Prepend(tstate, meth, self, args, kwds);
- }
- else {
- res = _PyObject_Call(tstate, meth, args, kwds);
- }
- Py_DECREF(meth);
+ PyObject *res = call_method(self, &_Py_ID(__init__), args, kwds);
if (res == NULL)
return -1;
if (res != Py_None) {
static void
slot_tp_finalize(PyObject *self)
{
- int unbound;
- PyObject *del, *res;
-
/* Save the current exception, if any. */
- PyObject *exc = PyErr_GetRaisedException();
+ PyThreadState *tstate = _PyThreadState_GET();
+ PyObject *exc = _PyErr_GetRaisedException(tstate);
+
+ _PyCStackRef cref;
+ _PyThreadState_PushCStackRef(tstate, &cref);
/* Execute __del__ method, if any. */
- del = lookup_maybe_method(self, &_Py_ID(__del__), &unbound);
- if (del != NULL) {
- res = call_unbound_noarg(unbound, del, self);
+ int unbound = lookup_maybe_method(self, &_Py_ID(__del__), &cref.ref);
+ if (unbound >= 0) {
+ PyObject *del = PyStackRef_AsPyObjectBorrow(cref.ref);
+ PyObject *res = call_unbound_noarg(unbound, del, self);
if (res == NULL) {
PyErr_FormatUnraisable("Exception ignored while "
"calling deallocator %R", del);
else {
Py_DECREF(res);
}
- Py_DECREF(del);
}
+ _PyThreadState_PopCStackRef(tstate, &cref);
+
/* Restore the saved exception. */
- PyErr_SetRaisedException(exc);
+ _PyErr_SetRaisedException(tstate, exc);
}
typedef struct _PyBufferWrapper {
}
static PyObject *
-slot_am_await(PyObject *self)
+slot_am_generic(PyObject *self, PyObject *name)
{
- int unbound;
- PyObject *func, *res;
-
- func = lookup_maybe_method(self, &_Py_ID(__await__), &unbound);
- if (func != NULL) {
- res = call_unbound_noarg(unbound, func, self);
- Py_DECREF(func);
- return res;
+ PyObject *res = _PyObject_MaybeCallSpecialNoArgs(self, name);
+ if (res == NULL && !PyErr_Occurred()) {
+ PyErr_Format(PyExc_AttributeError,
+ "object %.50s does not have %U method",
+ Py_TYPE(self)->tp_name, name);
}
- PyErr_Format(PyExc_AttributeError,
- "object %.50s does not have __await__ method",
- Py_TYPE(self)->tp_name);
- return NULL;
+ return res;
}
static PyObject *
-slot_am_aiter(PyObject *self)
+slot_am_await(PyObject *self)
{
- int unbound;
- PyObject *func, *res;
+ return slot_am_generic(self, &_Py_ID(__await__));
+}
- func = lookup_maybe_method(self, &_Py_ID(__aiter__), &unbound);
- if (func != NULL) {
- res = call_unbound_noarg(unbound, func, self);
- Py_DECREF(func);
- return res;
- }
- PyErr_Format(PyExc_AttributeError,
- "object %.50s does not have __aiter__ method",
- Py_TYPE(self)->tp_name);
- return NULL;
+static PyObject *
+slot_am_aiter(PyObject *self)
+{
+ return slot_am_generic(self, &_Py_ID(__aiter__));
}
static PyObject *
slot_am_anext(PyObject *self)
{
- int unbound;
- PyObject *func, *res;
-
- func = lookup_maybe_method(self, &_Py_ID(__anext__), &unbound);
- if (func != NULL) {
- res = call_unbound_noarg(unbound, func, self);
- Py_DECREF(func);
- return res;
- }
- PyErr_Format(PyExc_AttributeError,
- "object %.50s does not have __anext__ method",
- Py_TYPE(self)->tp_name);
- return NULL;
+ return slot_am_generic(self, &_Py_ID(__anext__));
}
/*