diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 90214a314031d1..c1f46eb00b528d 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -598,6 +598,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bytes__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__call__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__cantrace__)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__ceil__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__class__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__class_getitem__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__classcell__)); @@ -621,6 +622,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__file__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__firstlineno__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__float__)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__floor__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__floordiv__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__format__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__fspath__)); @@ -726,6 +728,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasscheck__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasshook__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__truediv__)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__trunc__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__type_params__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_is_unpacked_typevartuple__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_prepare_subst__)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 5056128dc97ca0..d0acf7821253d2 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -89,6 +89,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(__bytes__) STRUCT_FOR_ID(__call__) STRUCT_FOR_ID(__cantrace__) + STRUCT_FOR_ID(__ceil__) STRUCT_FOR_ID(__class__) STRUCT_FOR_ID(__class_getitem__) STRUCT_FOR_ID(__classcell__) @@ -112,6 +113,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(__file__) STRUCT_FOR_ID(__firstlineno__) STRUCT_FOR_ID(__float__) + STRUCT_FOR_ID(__floor__) STRUCT_FOR_ID(__floordiv__) STRUCT_FOR_ID(__format__) STRUCT_FOR_ID(__fspath__) @@ -217,6 +219,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(__subclasscheck__) STRUCT_FOR_ID(__subclasshook__) STRUCT_FOR_ID(__truediv__) + STRUCT_FOR_ID(__trunc__) STRUCT_FOR_ID(__type_params__) STRUCT_FOR_ID(__typing_is_unpacked_typevartuple__) STRUCT_FOR_ID(__typing_prepare_subst__) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 5e900f5bb91c99..e5034ff4dcc42b 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -891,6 +891,12 @@ extern bool _PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name, extern PyObject *_PyType_LookupRefAndVersion(PyTypeObject *, PyObject *, unsigned int *); +// Internal API to look for a name through the MRO. +// This stores a stack reference in out and returns the value of +// type->tp_version or zero if name is missing. It doesn't set an exception! +extern unsigned int +_PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef *out); + // Cache the provided init method in the specialization cache of type if the // provided type version matches the current version of the type. // @@ -946,6 +952,14 @@ extern int _PyObject_IsInstanceDictEmpty(PyObject *); PyAPI_FUNC(PyObject*) _PyObject_LookupSpecial(PyObject *, PyObject *); PyAPI_FUNC(PyObject*) _PyObject_LookupSpecialMethod(PyObject *self, PyObject *attr, PyObject **self_or_null); +// Calls the method named `attr` on `self`, but does not set an exception if +// the attribute does not exist. +PyAPI_FUNC(PyObject *) +_PyObject_MaybeCallSpecialNoArgs(PyObject *self, PyObject *attr); + +PyAPI_FUNC(PyObject *) +_PyObject_MaybeCallSpecialOneArg(PyObject *self, PyObject *attr, PyObject *arg); + extern int _PyObject_IsAbstract(PyObject *); PyAPI_FUNC(int) _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method); diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 4f928cc050bf8e..d32dfa53763f33 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -596,6 +596,7 @@ extern "C" { INIT_ID(__bytes__), \ INIT_ID(__call__), \ INIT_ID(__cantrace__), \ + INIT_ID(__ceil__), \ INIT_ID(__class__), \ INIT_ID(__class_getitem__), \ INIT_ID(__classcell__), \ @@ -619,6 +620,7 @@ extern "C" { INIT_ID(__file__), \ INIT_ID(__firstlineno__), \ INIT_ID(__float__), \ + INIT_ID(__floor__), \ INIT_ID(__floordiv__), \ INIT_ID(__format__), \ INIT_ID(__fspath__), \ @@ -724,6 +726,7 @@ extern "C" { INIT_ID(__subclasscheck__), \ INIT_ID(__subclasshook__), \ INIT_ID(__truediv__), \ + INIT_ID(__trunc__), \ INIT_ID(__type_params__), \ INIT_ID(__typing_is_unpacked_typevartuple__), \ INIT_ID(__typing_prepare_subst__), \ diff --git a/Include/internal/pycore_stackref.h b/Include/internal/pycore_stackref.h index cf7688416b170b..ab17c3cfc726ee 100644 --- a/Include/internal/pycore_stackref.h +++ b/Include/internal/pycore_stackref.h @@ -592,7 +592,7 @@ PyStackRef_XCLOSE(_PyStackRef ref) // Note: this is a macro because MSVC (Windows) has trouble inlining it. -#define PyStackRef_Is(a, b) (((a).bits & (~Py_TAG_REFCNT)) == ((b).bits & (~Py_TAG_REFCNT))) +#define PyStackRef_Is(a, b) (((a).bits & (~Py_TAG_BITS)) == ((b).bits & (~Py_TAG_BITS))) #endif // !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG) @@ -640,6 +640,28 @@ PyStackRef_FunctionCheck(_PyStackRef stackref) return PyFunction_Check(PyStackRef_AsPyObjectBorrow(stackref)); } +static inline void +_PyThreadState_PushCStackRef(PyThreadState *tstate, _PyCStackRef *ref) +{ +#ifdef Py_GIL_DISABLED + _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate; + ref->next = tstate_impl->c_stack_refs; + tstate_impl->c_stack_refs = ref; +#endif + ref->ref = PyStackRef_NULL; +} + +static inline void +_PyThreadState_PopCStackRef(PyThreadState *tstate, _PyCStackRef *ref) +{ +#ifdef Py_GIL_DISABLED + _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate; + assert(tstate_impl->c_stack_refs == ref); + tstate_impl->c_stack_refs = ref->next; +#endif + PyStackRef_XCLOSE(ref->ref); +} + #ifdef Py_GIL_DISABLED static inline int @@ -656,6 +678,17 @@ _Py_TryIncrefCompareStackRef(PyObject **src, PyObject *op, _PyStackRef *out) return 0; } +static inline int +_Py_TryXGetStackRef(PyObject **src, _PyStackRef *out) +{ + PyObject *op = _Py_atomic_load_ptr_relaxed(src); + if (op == NULL) { + *out = PyStackRef_NULL; + return 1; + } + return _Py_TryIncrefCompareStackRef(src, op, out); +} + #endif #ifdef __cplusplus diff --git a/Include/internal/pycore_structs.h b/Include/internal/pycore_structs.h index 90963f0f8b0dfb..b54d61197ad789 100644 --- a/Include/internal/pycore_structs.h +++ b/Include/internal/pycore_structs.h @@ -65,6 +65,16 @@ typedef union _PyStackRef { #endif } _PyStackRef; +// A stackref that can be stored in a regular C local variable and be visible +// to the GC in the free threading build. +// Used in combination with _PyThreadState_PushCStackRef(). +typedef struct _PyCStackRef { + _PyStackRef ref; +#ifdef Py_GIL_DISABLED + struct _PyCStackRef *next; +#endif +} _PyCStackRef; + #ifdef __cplusplus } diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index b73cd6828b5199..bad968428c73a1 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -47,8 +47,9 @@ typedef struct _PyThreadStateImpl { struct _qsbr_thread_state *qsbr; // only used by free-threaded build struct llist_node mem_free_queue; // delayed free queue - #ifdef Py_GIL_DISABLED + // Stack references for the current thread that exist on the C stack + struct _PyCStackRef *c_stack_refs; struct _gc_thread_state gc; struct _mimalloc_thread_state mimalloc; struct _Py_freelists freelists; diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 5b78d038fc1192..b74dbbec9c2c96 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -144,6 +144,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(__ceil__); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(__class__); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -236,6 +240,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(__floor__); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(__floordiv__); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -656,6 +664,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(__trunc__); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(__type_params__); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index 34ecb45f161dfe..dcdf7bdce03b80 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -383,6 +383,10 @@ def __len__(self): __bool__ = None self.assertRaises(TypeError, bool, B()) + class C: + __len__ = None + self.assertRaises(TypeError, bool, C()) + def test_real_and_imag(self): self.assertEqual(True.real, 1) self.assertEqual(True.imag, 0) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 0cc172c8b77237..5546e6345e9208 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1746,6 +1746,11 @@ def test_repr(self): a[0] = a self.assertEqual(repr(a), '{0: {...}}') + def test_repr_blocked(self): + class C: + __repr__ = None + self.assertRaises(TypeError, repr, C()) + def test_round(self): self.assertEqual(round(0.0), 0.0) self.assertEqual(type(round(0.0)), int) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index b4f5dd80f55f86..bfc55e7bbaba0a 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -573,6 +573,8 @@ def testFloor(self): #self.assertEqual(math.ceil(NINF), NINF) #self.assertTrue(math.isnan(math.floor(NAN))) + class TestFloorIsNone(float): + __floor__ = None class TestFloor: def __floor__(self): return 42 @@ -588,6 +590,7 @@ class TestBadFloor: self.assertEqual(math.floor(FloatLike(41.9)), 41) self.assertRaises(TypeError, math.floor, TestNoFloor()) self.assertRaises(ValueError, math.floor, TestBadFloor()) + self.assertRaises(TypeError, math.floor, TestFloorIsNone(3.5)) t = TestNoFloor() t.__floor__ = lambda *args: args diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index b4c15a143f9838..20d3c3192ba2e4 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -78,19 +78,6 @@ module math /*[clinic end generated code: output=da39a3ee5e6b4b0d input=76bc7002685dd942]*/ -typedef struct { - PyObject *str___ceil__; - PyObject *str___floor__; - PyObject *str___trunc__; -} math_module_state; - -static inline math_module_state* -get_math_module_state(PyObject *module) -{ - void *state = _PyModule_GetState(module); - assert(state != NULL); - return (math_module_state *)state; -} /* Double and triple length extended precision algorithms from: @@ -1140,18 +1127,17 @@ math_ceil(PyObject *module, PyObject *number) x = PyFloat_AS_DOUBLE(number); } else { - math_module_state *state = get_math_module_state(module); - PyObject *method = _PyObject_LookupSpecial(number, state->str___ceil__); - if (method != NULL) { - PyObject *result = _PyObject_CallNoArgs(method); - Py_DECREF(method); + PyObject *result = _PyObject_MaybeCallSpecialNoArgs(number, &_Py_ID(__ceil__)); + if (result != NULL) { return result; } - if (PyErr_Occurred()) + else if (PyErr_Occurred()) { return NULL; + } x = PyFloat_AsDouble(number); - if (x == -1.0 && PyErr_Occurred()) + if (x == -1.0 && PyErr_Occurred()) { return NULL; + } } return PyLong_FromDouble(ceil(x)); } @@ -1209,18 +1195,17 @@ math_floor(PyObject *module, PyObject *number) x = PyFloat_AS_DOUBLE(number); } else { - math_module_state *state = get_math_module_state(module); - PyObject *method = _PyObject_LookupSpecial(number, state->str___floor__); - if (method != NULL) { - PyObject *result = _PyObject_CallNoArgs(method); - Py_DECREF(method); + PyObject *result = _PyObject_MaybeCallSpecialNoArgs(number, &_Py_ID(__floor__)); + if (result != NULL) { return result; } - if (PyErr_Occurred()) + else if (PyErr_Occurred()) { return NULL; + } x = PyFloat_AsDouble(number); - if (x == -1.0 && PyErr_Occurred()) + if (x == -1.0 && PyErr_Occurred()) { return NULL; + } } return PyLong_FromDouble(floor(x)); } @@ -2074,24 +2059,20 @@ static PyObject * math_trunc(PyObject *module, PyObject *x) /*[clinic end generated code: output=34b9697b707e1031 input=2168b34e0a09134d]*/ { - PyObject *trunc, *result; - if (PyFloat_CheckExact(x)) { return PyFloat_Type.tp_as_number->nb_int(x); } - math_module_state *state = get_math_module_state(module); - trunc = _PyObject_LookupSpecial(x, state->str___trunc__); - if (trunc == NULL) { - if (!PyErr_Occurred()) - PyErr_Format(PyExc_TypeError, - "type %.100s doesn't define __trunc__ method", - Py_TYPE(x)->tp_name); - return NULL; + PyObject *result = _PyObject_MaybeCallSpecialNoArgs(x, &_Py_ID(__trunc__)); + if (result != NULL) { + return result; } - result = _PyObject_CallNoArgs(trunc); - Py_DECREF(trunc); - return result; + else if (!PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, + "type %.100s doesn't define __trunc__ method", + Py_TYPE(x)->tp_name); + } + return NULL; } @@ -4084,19 +4065,6 @@ static int math_exec(PyObject *module) { - math_module_state *state = get_math_module_state(module); - state->str___ceil__ = PyUnicode_InternFromString("__ceil__"); - if (state->str___ceil__ == NULL) { - return -1; - } - state->str___floor__ = PyUnicode_InternFromString("__floor__"); - if (state->str___floor__ == NULL) { - return -1; - } - state->str___trunc__ = PyUnicode_InternFromString("__trunc__"); - if (state->str___trunc__ == NULL) { - return -1; - } if (PyModule_Add(module, "pi", PyFloat_FromDouble(Py_MATH_PI)) < 0) { return -1; } @@ -4116,22 +4084,6 @@ math_exec(PyObject *module) return 0; } -static int -math_clear(PyObject *module) -{ - math_module_state *state = get_math_module_state(module); - Py_CLEAR(state->str___ceil__); - Py_CLEAR(state->str___floor__); - Py_CLEAR(state->str___trunc__); - return 0; -} - -static void -math_free(void *module) -{ - math_clear((PyObject *)module); -} - static PyMethodDef math_methods[] = { {"acos", math_acos, METH_O, math_acos_doc}, {"acosh", math_acosh, METH_O, math_acosh_doc}, @@ -4208,11 +4160,9 @@ static struct PyModuleDef mathmodule = { PyModuleDef_HEAD_INIT, .m_name = "math", .m_doc = module_doc, - .m_size = sizeof(math_module_state), + .m_size = 0, .m_methods = math_methods, .m_slots = math_slots, - .m_clear = math_clear, - .m_free = math_free, }; PyMODINIT_FUNC diff --git a/Objects/object.c b/Objects/object.c index ecc5a86901a347..974cf609cfccb4 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1680,14 +1680,20 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, Py_TYPE(name)->tp_name); return NULL; } - Py_INCREF(name); if (!_PyType_IsReady(tp)) { if (PyType_Ready(tp) < 0) - goto done; + return NULL; } - descr = _PyType_LookupRef(tp, name); + Py_INCREF(name); + + PyThreadState *tstate = _PyThreadState_GET(); + _PyCStackRef cref; + _PyThreadState_PushCStackRef(tstate, &cref); + + _PyType_LookupStackRefAndVersion(tp, name, &cref.ref); + descr = PyStackRef_AsPyObjectBorrow(cref.ref); f = NULL; if (descr != NULL) { @@ -1758,8 +1764,8 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, } if (descr != NULL) { - res = descr; - descr = NULL; + res = PyStackRef_AsPyObjectSteal(cref.ref); + cref.ref = PyStackRef_NULL; goto done; } @@ -1771,7 +1777,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, _PyObject_SetAttributeErrorContext(obj, name); } done: - Py_XDECREF(descr); + _PyThreadState_PopCStackRef(tstate, &cref); Py_DECREF(name); return res; } @@ -1805,7 +1811,13 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, Py_INCREF(name); Py_INCREF(tp); - descr = _PyType_LookupRef(tp, name); + + PyThreadState *tstate = _PyThreadState_GET(); + _PyCStackRef cref; + _PyThreadState_PushCStackRef(tstate, &cref); + + _PyType_LookupStackRefAndVersion(tp, name, &cref.ref); + descr = PyStackRef_AsPyObjectBorrow(cref.ref); if (descr != NULL) { f = Py_TYPE(descr)->tp_descr_set; @@ -1872,7 +1884,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, _PyObject_SetAttributeErrorContext(obj, name); } done: - Py_XDECREF(descr); + _PyThreadState_PopCStackRef(tstate, &cref); Py_DECREF(tp); Py_DECREF(name); return res; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 25153182f29487..fd5619ddd6c32f 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -103,13 +103,9 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer); 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) { @@ -1144,8 +1140,29 @@ PyType_Modified(PyTypeObject *type) 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 @@ -1159,29 +1176,10 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) { 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++) { @@ -1222,7 +1220,6 @@ This is similar to func_version_cache. void _PyType_SetVersion(PyTypeObject *tp, unsigned int version) { - BEGIN_TYPE_LOCK(); set_version_unlocked(tp, version); END_TYPE_LOCK(); @@ -2803,36 +2800,51 @@ _PyObject_LookupSpecialMethod(PyObject *self, PyObject *attr, PyObject **self_or 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); } @@ -2862,6 +2874,45 @@ call_unbound_noarg(int unbound, PyObject *func, PyObject *self) } } +// 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(). @@ -2873,14 +2924,16 @@ vectorcall_method(PyObject *name, PyObject *const *args, Py_ssize_t nargs) 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; } @@ -2892,19 +2945,81 @@ vectorcall_maybe(PyThreadState *tstate, PyObject *name, { 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", @@ -3286,13 +3401,7 @@ mro_invoke(PyTypeObject *type) 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); @@ -5609,10 +5718,20 @@ _PyTypes_AfterFork(void) 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]; @@ -5626,16 +5745,12 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve _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 @@ -5648,16 +5763,12 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve } } #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)); @@ -5669,6 +5780,9 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve // 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(); @@ -5692,11 +5806,8 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve 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) { @@ -5707,11 +5818,8 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve 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. @@ -9800,32 +9908,23 @@ slot_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value) 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; } @@ -9889,31 +9988,29 @@ SLOT0(slot_nb_absolute, __abs__) 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); @@ -9928,14 +10025,8 @@ slot_nb_bool(PyObject *self) Py_TYPE(value)->tp_name); result = -1; } - Py_DECREF(value); - Py_DECREF(func); return result; - -error: - Py_DECREF(func); - return -1; } @@ -9982,18 +10073,15 @@ SLOT1(slot_nb_inplace_true_divide, __itruediv__, PyObject *) 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__) @@ -10001,25 +10089,15 @@ 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"); @@ -10030,7 +10108,7 @@ slot_tp_hash(PyObject *self) 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; @@ -10048,24 +10126,7 @@ slot_tp_hash(PyObject *self) 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. @@ -10192,51 +10253,46 @@ static PyObject *name_op[] = { 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); } @@ -10294,22 +10350,7 @@ slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value) 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) { @@ -10342,16 +10383,18 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 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); @@ -10359,11 +10402,12 @@ slot_tp_finalize(PyObject *self) 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 { @@ -10609,57 +10653,33 @@ slot_bf_releasebuffer(PyObject *self, Py_buffer *buffer) } 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__)); } /* diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 6709f306bb024d..3bf31d7253e79a 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2534,22 +2534,19 @@ static PyObject * builtin_round_impl(PyObject *module, PyObject *number, PyObject *ndigits) /*[clinic end generated code: output=ff0d9dd176c02ede input=275678471d7aca15]*/ { - PyObject *round, *result; - - round = _PyObject_LookupSpecial(number, &_Py_ID(__round__)); - if (round == NULL) { - if (!PyErr_Occurred()) - PyErr_Format(PyExc_TypeError, - "type %.100s doesn't define __round__ method", - Py_TYPE(number)->tp_name); - return NULL; + PyObject *result; + if (ndigits == Py_None) { + result = _PyObject_MaybeCallSpecialNoArgs(number, &_Py_ID(__round__)); + } + else { + result = _PyObject_MaybeCallSpecialOneArg(number, &_Py_ID(__round__), + ndigits); + } + if (result == NULL && !PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, + "type %.100s doesn't define __round__ method", + Py_TYPE(number)->tp_name); } - - if (ndigits == Py_None) - result = _PyObject_CallNoArgs(round); - else - result = PyObject_CallOneArg(round, ndigits); - Py_DECREF(round); return result; } diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 70dace9fe9eff2..4c459b02ce2b8f 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -433,6 +433,12 @@ static void gc_visit_thread_stacks(PyInterpreterState *interp, struct collection_state *state) { _Py_FOR_EACH_TSTATE_BEGIN(interp, p) { + _PyCStackRef *c_ref = ((_PyThreadStateImpl *)p)->c_stack_refs; + while (c_ref != NULL) { + gc_visit_stackref(c_ref->ref); + c_ref = c_ref->next; + } + for (_PyInterpreterFrame *f = p->current_frame; f != NULL; f = f->previous) { if (f->owner >= FRAME_OWNED_BY_INTERPRETER) { continue;