gh-131586: Avoid refcount contention in some "special" calls (#131588)

In the free threaded build, the `_PyObject_LookupSpecial()` call can lead to
reference count contention on the returned function object becuase it
doesn't use stackrefs. Refactor some of the callers to use
`_PyObject_MaybeCallSpecialNoArgs`, which uses stackrefs internally.

This fixes the scaling bottleneck in the "lookup_special" microbenchmark
in `ftscalingbench.py`. However, the are still some uses of
`_PyObject_LookupSpecial()` that need to be addressed in future PRs.
This commit is contained in:
Sam Gross 2025-03-26 14:38:47 -04:00 committed by GitHub
parent 3d4ac1a2c2
commit 67fbfb42bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 450 additions and 374 deletions

View file

@ -598,6 +598,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bytes__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bytes__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__call__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__call__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__cantrace__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__cantrace__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__ceil__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__class__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__class__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__class_getitem__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__class_getitem__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__classcell__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__classcell__));
@ -622,6 +623,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__file__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__file__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__firstlineno__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__firstlineno__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__float__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__float__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__floor__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__floordiv__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__floordiv__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__format__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__format__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__fspath__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__fspath__));
@ -727,6 +729,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasscheck__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasscheck__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasshook__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasshook__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__truediv__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__truediv__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__trunc__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__type_params__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__type_params__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_is_unpacked_typevartuple__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_is_unpacked_typevartuple__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_prepare_subst__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_prepare_subst__));

View file

@ -89,6 +89,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__bytes__) STRUCT_FOR_ID(__bytes__)
STRUCT_FOR_ID(__call__) STRUCT_FOR_ID(__call__)
STRUCT_FOR_ID(__cantrace__) STRUCT_FOR_ID(__cantrace__)
STRUCT_FOR_ID(__ceil__)
STRUCT_FOR_ID(__class__) STRUCT_FOR_ID(__class__)
STRUCT_FOR_ID(__class_getitem__) STRUCT_FOR_ID(__class_getitem__)
STRUCT_FOR_ID(__classcell__) STRUCT_FOR_ID(__classcell__)
@ -113,6 +114,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__file__) STRUCT_FOR_ID(__file__)
STRUCT_FOR_ID(__firstlineno__) STRUCT_FOR_ID(__firstlineno__)
STRUCT_FOR_ID(__float__) STRUCT_FOR_ID(__float__)
STRUCT_FOR_ID(__floor__)
STRUCT_FOR_ID(__floordiv__) STRUCT_FOR_ID(__floordiv__)
STRUCT_FOR_ID(__format__) STRUCT_FOR_ID(__format__)
STRUCT_FOR_ID(__fspath__) STRUCT_FOR_ID(__fspath__)
@ -218,6 +220,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__subclasscheck__) STRUCT_FOR_ID(__subclasscheck__)
STRUCT_FOR_ID(__subclasshook__) STRUCT_FOR_ID(__subclasshook__)
STRUCT_FOR_ID(__truediv__) STRUCT_FOR_ID(__truediv__)
STRUCT_FOR_ID(__trunc__)
STRUCT_FOR_ID(__type_params__) STRUCT_FOR_ID(__type_params__)
STRUCT_FOR_ID(__typing_is_unpacked_typevartuple__) STRUCT_FOR_ID(__typing_is_unpacked_typevartuple__)
STRUCT_FOR_ID(__typing_prepare_subst__) STRUCT_FOR_ID(__typing_prepare_subst__)

View file

@ -891,6 +891,12 @@ extern bool _PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name,
extern PyObject *_PyType_LookupRefAndVersion(PyTypeObject *, PyObject *, extern PyObject *_PyType_LookupRefAndVersion(PyTypeObject *, PyObject *,
unsigned int *); 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 // Cache the provided init method in the specialization cache of type if the
// provided type version matches the current version of the type. // 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_LookupSpecial(PyObject *, PyObject *);
PyAPI_FUNC(PyObject*) _PyObject_LookupSpecialMethod(PyObject *self, PyObject *attr, PyObject **self_or_null); 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 *); extern int _PyObject_IsAbstract(PyObject *);
PyAPI_FUNC(int) _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method); PyAPI_FUNC(int) _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method);

View file

@ -596,6 +596,7 @@ extern "C" {
INIT_ID(__bytes__), \ INIT_ID(__bytes__), \
INIT_ID(__call__), \ INIT_ID(__call__), \
INIT_ID(__cantrace__), \ INIT_ID(__cantrace__), \
INIT_ID(__ceil__), \
INIT_ID(__class__), \ INIT_ID(__class__), \
INIT_ID(__class_getitem__), \ INIT_ID(__class_getitem__), \
INIT_ID(__classcell__), \ INIT_ID(__classcell__), \
@ -620,6 +621,7 @@ extern "C" {
INIT_ID(__file__), \ INIT_ID(__file__), \
INIT_ID(__firstlineno__), \ INIT_ID(__firstlineno__), \
INIT_ID(__float__), \ INIT_ID(__float__), \
INIT_ID(__floor__), \
INIT_ID(__floordiv__), \ INIT_ID(__floordiv__), \
INIT_ID(__format__), \ INIT_ID(__format__), \
INIT_ID(__fspath__), \ INIT_ID(__fspath__), \
@ -725,6 +727,7 @@ extern "C" {
INIT_ID(__subclasscheck__), \ INIT_ID(__subclasscheck__), \
INIT_ID(__subclasshook__), \ INIT_ID(__subclasshook__), \
INIT_ID(__truediv__), \ INIT_ID(__truediv__), \
INIT_ID(__trunc__), \
INIT_ID(__type_params__), \ INIT_ID(__type_params__), \
INIT_ID(__typing_is_unpacked_typevartuple__), \ INIT_ID(__typing_is_unpacked_typevartuple__), \
INIT_ID(__typing_prepare_subst__), \ INIT_ID(__typing_prepare_subst__), \

View file

@ -592,7 +592,7 @@ PyStackRef_XCLOSE(_PyStackRef ref)
// Note: this is a macro because MSVC (Windows) has trouble inlining it. // 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) #endif // !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
@ -640,6 +640,28 @@ PyStackRef_FunctionCheck(_PyStackRef stackref)
return PyFunction_Check(PyStackRef_AsPyObjectBorrow(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 #ifdef Py_GIL_DISABLED
static inline int static inline int
@ -656,6 +678,17 @@ _Py_TryIncrefCompareStackRef(PyObject **src, PyObject *op, _PyStackRef *out)
return 0; 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 #endif
// Like Py_VISIT but for _PyStackRef fields // Like Py_VISIT but for _PyStackRef fields

View file

@ -65,6 +65,16 @@ typedef union _PyStackRef {
#endif #endif
} _PyStackRef; } _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 #ifdef __cplusplus
} }

View file

@ -47,8 +47,9 @@ typedef struct _PyThreadStateImpl {
struct _qsbr_thread_state *qsbr; // only used by free-threaded build struct _qsbr_thread_state *qsbr; // only used by free-threaded build
struct llist_node mem_free_queue; // delayed free queue struct llist_node mem_free_queue; // delayed free queue
#ifdef Py_GIL_DISABLED #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 _gc_thread_state gc;
struct _mimalloc_thread_state mimalloc; struct _mimalloc_thread_state mimalloc;
struct _Py_freelists freelists; struct _Py_freelists freelists;

View file

@ -144,6 +144,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string); _PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1)); assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(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__); string = &_Py_ID(__class__);
_PyUnicode_InternStatic(interp, &string); _PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1)); assert(_PyUnicode_CheckConsistency(string, 1));
@ -240,6 +244,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string); _PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1)); assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(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__); string = &_Py_ID(__floordiv__);
_PyUnicode_InternStatic(interp, &string); _PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1)); assert(_PyUnicode_CheckConsistency(string, 1));
@ -660,6 +668,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string); _PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1)); assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(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__); string = &_Py_ID(__type_params__);
_PyUnicode_InternStatic(interp, &string); _PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1)); assert(_PyUnicode_CheckConsistency(string, 1));

View file

@ -383,6 +383,10 @@ class BoolTest(unittest.TestCase):
__bool__ = None __bool__ = None
self.assertRaises(TypeError, bool, B()) self.assertRaises(TypeError, bool, B())
class C:
__len__ = None
self.assertRaises(TypeError, bool, C())
def test_real_and_imag(self): def test_real_and_imag(self):
self.assertEqual(True.real, 1) self.assertEqual(True.real, 1)
self.assertEqual(True.imag, 0) self.assertEqual(True.imag, 0)

View file

@ -1746,6 +1746,11 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
a[0] = a a[0] = a
self.assertEqual(repr(a), '{0: {...}}') self.assertEqual(repr(a), '{0: {...}}')
def test_repr_blocked(self):
class C:
__repr__ = None
self.assertRaises(TypeError, repr, C())
def test_round(self): def test_round(self):
self.assertEqual(round(0.0), 0.0) self.assertEqual(round(0.0), 0.0)
self.assertEqual(type(round(0.0)), int) self.assertEqual(type(round(0.0)), int)

View file

@ -573,6 +573,8 @@ class MathTests(unittest.TestCase):
#self.assertEqual(math.ceil(NINF), NINF) #self.assertEqual(math.ceil(NINF), NINF)
#self.assertTrue(math.isnan(math.floor(NAN))) #self.assertTrue(math.isnan(math.floor(NAN)))
class TestFloorIsNone(float):
__floor__ = None
class TestFloor: class TestFloor:
def __floor__(self): def __floor__(self):
return 42 return 42
@ -588,6 +590,7 @@ class MathTests(unittest.TestCase):
self.assertEqual(math.floor(FloatLike(41.9)), 41) self.assertEqual(math.floor(FloatLike(41.9)), 41)
self.assertRaises(TypeError, math.floor, TestNoFloor()) self.assertRaises(TypeError, math.floor, TestNoFloor())
self.assertRaises(ValueError, math.floor, TestBadFloor()) self.assertRaises(ValueError, math.floor, TestBadFloor())
self.assertRaises(TypeError, math.floor, TestFloorIsNone(3.5))
t = TestNoFloor() t = TestNoFloor()
t.__floor__ = lambda *args: args t.__floor__ = lambda *args: args

View file

@ -78,19 +78,6 @@ module math
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=76bc7002685dd942]*/ /*[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: Double and triple length extended precision algorithms from:
@ -1140,18 +1127,17 @@ math_ceil(PyObject *module, PyObject *number)
x = PyFloat_AS_DOUBLE(number); x = PyFloat_AS_DOUBLE(number);
} }
else { else {
math_module_state *state = get_math_module_state(module); PyObject *result = _PyObject_MaybeCallSpecialNoArgs(number, &_Py_ID(__ceil__));
PyObject *method = _PyObject_LookupSpecial(number, state->str___ceil__); if (result != NULL) {
if (method != NULL) {
PyObject *result = _PyObject_CallNoArgs(method);
Py_DECREF(method);
return result; return result;
} }
if (PyErr_Occurred()) else if (PyErr_Occurred()) {
return NULL; return NULL;
}
x = PyFloat_AsDouble(number); x = PyFloat_AsDouble(number);
if (x == -1.0 && PyErr_Occurred()) if (x == -1.0 && PyErr_Occurred()) {
return NULL; return NULL;
}
} }
return PyLong_FromDouble(ceil(x)); return PyLong_FromDouble(ceil(x));
} }
@ -1209,18 +1195,17 @@ math_floor(PyObject *module, PyObject *number)
x = PyFloat_AS_DOUBLE(number); x = PyFloat_AS_DOUBLE(number);
} }
else { else {
math_module_state *state = get_math_module_state(module); PyObject *result = _PyObject_MaybeCallSpecialNoArgs(number, &_Py_ID(__floor__));
PyObject *method = _PyObject_LookupSpecial(number, state->str___floor__); if (result != NULL) {
if (method != NULL) {
PyObject *result = _PyObject_CallNoArgs(method);
Py_DECREF(method);
return result; return result;
} }
if (PyErr_Occurred()) else if (PyErr_Occurred()) {
return NULL; return NULL;
}
x = PyFloat_AsDouble(number); x = PyFloat_AsDouble(number);
if (x == -1.0 && PyErr_Occurred()) if (x == -1.0 && PyErr_Occurred()) {
return NULL; return NULL;
}
} }
return PyLong_FromDouble(floor(x)); return PyLong_FromDouble(floor(x));
} }
@ -2074,24 +2059,20 @@ static PyObject *
math_trunc(PyObject *module, PyObject *x) math_trunc(PyObject *module, PyObject *x)
/*[clinic end generated code: output=34b9697b707e1031 input=2168b34e0a09134d]*/ /*[clinic end generated code: output=34b9697b707e1031 input=2168b34e0a09134d]*/
{ {
PyObject *trunc, *result;
if (PyFloat_CheckExact(x)) { if (PyFloat_CheckExact(x)) {
return PyFloat_Type.tp_as_number->nb_int(x); return PyFloat_Type.tp_as_number->nb_int(x);
} }
math_module_state *state = get_math_module_state(module); PyObject *result = _PyObject_MaybeCallSpecialNoArgs(x, &_Py_ID(__trunc__));
trunc = _PyObject_LookupSpecial(x, state->str___trunc__); if (result != NULL) {
if (trunc == NULL) { return result;
if (!PyErr_Occurred())
PyErr_Format(PyExc_TypeError,
"type %.100s doesn't define __trunc__ method",
Py_TYPE(x)->tp_name);
return NULL;
} }
result = _PyObject_CallNoArgs(trunc); else if (!PyErr_Occurred()) {
Py_DECREF(trunc); PyErr_Format(PyExc_TypeError,
return result; "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_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) { if (PyModule_Add(module, "pi", PyFloat_FromDouble(Py_MATH_PI)) < 0) {
return -1; return -1;
} }
@ -4116,22 +4084,6 @@ math_exec(PyObject *module)
return 0; 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[] = { static PyMethodDef math_methods[] = {
{"acos", math_acos, METH_O, math_acos_doc}, {"acos", math_acos, METH_O, math_acos_doc},
{"acosh", math_acosh, METH_O, math_acosh_doc}, {"acosh", math_acosh, METH_O, math_acosh_doc},
@ -4208,11 +4160,9 @@ static struct PyModuleDef mathmodule = {
PyModuleDef_HEAD_INIT, PyModuleDef_HEAD_INIT,
.m_name = "math", .m_name = "math",
.m_doc = module_doc, .m_doc = module_doc,
.m_size = sizeof(math_module_state), .m_size = 0,
.m_methods = math_methods, .m_methods = math_methods,
.m_slots = math_slots, .m_slots = math_slots,
.m_clear = math_clear,
.m_free = math_free,
}; };
PyMODINIT_FUNC PyMODINIT_FUNC

View file

@ -1680,14 +1680,20 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
Py_TYPE(name)->tp_name); Py_TYPE(name)->tp_name);
return NULL; return NULL;
} }
Py_INCREF(name);
if (!_PyType_IsReady(tp)) { if (!_PyType_IsReady(tp)) {
if (PyType_Ready(tp) < 0) 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; f = NULL;
if (descr != NULL) { if (descr != NULL) {
@ -1758,8 +1764,8 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
} }
if (descr != NULL) { if (descr != NULL) {
res = descr; res = PyStackRef_AsPyObjectSteal(cref.ref);
descr = NULL; cref.ref = PyStackRef_NULL;
goto done; goto done;
} }
@ -1771,7 +1777,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
_PyObject_SetAttributeErrorContext(obj, name); _PyObject_SetAttributeErrorContext(obj, name);
} }
done: done:
Py_XDECREF(descr); _PyThreadState_PopCStackRef(tstate, &cref);
Py_DECREF(name); Py_DECREF(name);
return res; return res;
} }
@ -1805,7 +1811,13 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
Py_INCREF(name); Py_INCREF(name);
Py_INCREF(tp); 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) { if (descr != NULL) {
f = Py_TYPE(descr)->tp_descr_set; f = Py_TYPE(descr)->tp_descr_set;
@ -1872,7 +1884,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
_PyObject_SetAttributeErrorContext(obj, name); _PyObject_SetAttributeErrorContext(obj, name);
} }
done: done:
Py_XDECREF(descr); _PyThreadState_PopCStackRef(tstate, &cref);
Py_DECREF(tp); Py_DECREF(tp);
Py_DECREF(name); Py_DECREF(name);
return res; return res;

View file

@ -103,13 +103,9 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer);
static PyObject * static PyObject *
slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds); slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
static PyObject *
lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound);
static int static int
slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value); slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value);
static inline PyTypeObject * static inline PyTypeObject *
type_from_ref(PyObject *ref) type_from_ref(PyObject *ref)
{ {
@ -1146,8 +1142,29 @@ PyType_Modified(PyTypeObject *type)
static int static int
is_subtype_with_mro(PyObject *a_mro, PyTypeObject *a, PyTypeObject *b); 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 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 Check that all base classes or elements of the MRO of type are
able to be cached. This function is called after the base able to be cached. This function is called after the base
@ -1161,29 +1178,10 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
each subclass when their mro is recursively updated. each subclass when their mro is recursively updated.
*/ */
Py_ssize_t i, n; Py_ssize_t i, n;
int custom = !Py_IS_TYPE(type, &PyType_Type);
int unbound;
ASSERT_TYPE_LOCK_HELD(); ASSERT_TYPE_LOCK_HELD();
if (custom) { if (!Py_IS_TYPE(type, &PyType_Type) && has_custom_mro(type)) {
PyObject *mro_meth, *type_mro_meth; goto clear;
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;
}
} }
n = PyTuple_GET_SIZE(bases); n = PyTuple_GET_SIZE(bases);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
@ -1224,7 +1222,6 @@ This is similar to func_version_cache.
void void
_PyType_SetVersion(PyTypeObject *tp, unsigned int version) _PyType_SetVersion(PyTypeObject *tp, unsigned int version)
{ {
BEGIN_TYPE_LOCK(); BEGIN_TYPE_LOCK();
set_version_unlocked(tp, version); set_version_unlocked(tp, version);
END_TYPE_LOCK(); END_TYPE_LOCK();
@ -2805,36 +2802,51 @@ _PyObject_LookupSpecialMethod(PyObject *self, PyObject *attr, PyObject **self_or
return res; return res;
} }
static PyObject * static int
lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound) lookup_method_ex(PyObject *self, PyObject *attr, _PyStackRef *out,
int raise_attribute_error)
{ {
PyObject *res = _PyType_LookupRef(Py_TYPE(self), attr); _PyType_LookupStackRefAndVersion(Py_TYPE(self), attr, out);
if (res == NULL) { if (PyStackRef_IsNull(*out)) {
return NULL; 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 */ /* Avoid temporary PyMethodObject */
*unbound = 1; return 1;
} }
else {
*unbound = 0; descrgetfunc f = Py_TYPE(value)->tp_descr_get;
descrgetfunc f = Py_TYPE(res)->tp_descr_get; if (f != NULL) {
if (f != NULL) { value = f(value, self, (PyObject *)(Py_TYPE(self)));
Py_SETREF(res, f(res, 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 * static int
lookup_method(PyObject *self, PyObject *attr, int *unbound) lookup_maybe_method(PyObject *self, PyObject *attr, _PyStackRef *out)
{ {
PyObject *res = lookup_maybe_method(self, attr, unbound); return lookup_method_ex(self, attr, out, 0);
if (res == NULL && !PyErr_Occurred()) { }
PyErr_SetObject(PyExc_AttributeError, attr);
} static int
return res; lookup_method(PyObject *self, PyObject *attr, _PyStackRef *out)
{
return lookup_method_ex(self, attr, out, 1);
} }
@ -2864,6 +2876,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() /* A variation of PyObject_CallMethod* that uses lookup_method()
instead of PyObject_GetAttrString(). instead of PyObject_GetAttrString().
@ -2875,14 +2926,16 @@ vectorcall_method(PyObject *name, PyObject *const *args, Py_ssize_t nargs)
assert(nargs >= 1); assert(nargs >= 1);
PyThreadState *tstate = _PyThreadState_GET(); PyThreadState *tstate = _PyThreadState_GET();
int unbound; PyObject *retval = NULL;
PyObject *self = args[0]; PyObject *self = args[0];
PyObject *func = lookup_method(self, name, &unbound); _PyCStackRef cref;
if (func == NULL) { _PyThreadState_PushCStackRef(tstate, &cref);
return NULL; 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);
} }
PyObject *retval = vectorcall_unbound(tstate, unbound, func, args, nargs); _PyThreadState_PopCStackRef(tstate, &cref);
Py_DECREF(func);
return retval; return retval;
} }
@ -2894,19 +2947,81 @@ vectorcall_maybe(PyThreadState *tstate, PyObject *name,
{ {
assert(nargs >= 1); assert(nargs >= 1);
int unbound;
PyObject *self = args[0]; 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 (func == NULL) {
if (!PyErr_Occurred()) _PyThreadState_PopCStackRef(tstate, &cref);
if (!PyErr_Occurred()) {
Py_RETURN_NOTIMPLEMENTED; Py_RETURN_NOTIMPLEMENTED;
}
return NULL; return NULL;
} }
PyObject *retval = vectorcall_unbound(tstate, unbound, func, args, nargs); PyObject *retval = vectorcall_unbound(tstate, unbound, func, args, nargs);
Py_DECREF(func); _PyThreadState_PopCStackRef(tstate, &cref);
return retval; 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 Method resolution order algorithm C3 described in
"A Monotonic Superclass Linearization for Dylan", "A Monotonic Superclass Linearization for Dylan",
@ -3288,13 +3403,7 @@ mro_invoke(PyTypeObject *type)
const int custom = !Py_IS_TYPE(type, &PyType_Type); const int custom = !Py_IS_TYPE(type, &PyType_Type);
if (custom) { if (custom) {
int unbound; mro_result = call_method_noarg((PyObject *)type, &_Py_ID(mro));
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);
} }
else { else {
mro_result = mro_implementation_unlocked(type); mro_result = mro_implementation_unlocked(type);
@ -5611,10 +5720,20 @@ _PyTypes_AfterFork(void)
PyObject * PyObject *
_PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *version) _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *version)
{ {
PyObject *res; _PyStackRef out;
int error; unsigned int ver = _PyType_LookupStackRefAndVersion(type, name, &out);
PyInterpreterState *interp = _PyInterpreterState_GET(); 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); unsigned int h = MCACHE_HASH_METHOD(type, name);
struct type_cache *cache = get_type_cache(); struct type_cache *cache = get_type_cache();
struct type_cache_entry *entry = &cache->hashtable[h]; struct type_cache_entry *entry = &cache->hashtable[h];
@ -5628,16 +5747,12 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve
_Py_atomic_load_ptr_relaxed(&entry->name) == name) { _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_hits, !is_dunder_name(name));
OBJECT_STAT_INC_COND(type_cache_dunder_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 (_Py_TryXGetStackRef(&entry->value, out)) {
// If the sequence is still valid then we're done // If the sequence is still valid then we're done
if (value == NULL || _Py_TryIncref(value)) {
if (_PySeqLock_EndRead(&entry->sequence, sequence)) { if (_PySeqLock_EndRead(&entry->sequence, sequence)) {
if (version != NULL) { return entry_version;
*version = entry_version;
}
return value;
} }
Py_XDECREF(value); PyStackRef_XCLOSE(*out);
} }
else { else {
// If we can't incref the object we need to fallback to locking // If we can't incref the object we need to fallback to locking
@ -5650,16 +5765,12 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve
} }
} }
#else #else
if (entry->version == type->tp_version_tag && if (entry->version == type->tp_version_tag && entry->name == name) {
entry->name == name) {
assert(type->tp_version_tag); assert(type->tp_version_tag);
OBJECT_STAT_INC_COND(type_cache_hits, !is_dunder_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)); OBJECT_STAT_INC_COND(type_cache_dunder_hits, is_dunder_name(name));
Py_XINCREF(entry->value); *out = entry->value ? PyStackRef_FromPyObjectNew(entry->value) : PyStackRef_NULL;
if (version != NULL) { return entry->version;
*version = entry->version;
}
return entry->value;
} }
#endif #endif
OBJECT_STAT_INC_COND(type_cache_misses, !is_dunder_name(name)); OBJECT_STAT_INC_COND(type_cache_misses, !is_dunder_name(name));
@ -5671,6 +5782,9 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve
// We need to atomically do the lookup and capture the version before // We need to atomically do the lookup and capture the version before
// anyone else can modify our mro or mutate the type. // anyone else can modify our mro or mutate the type.
PyObject *res;
int error;
PyInterpreterState *interp = _PyInterpreterState_GET();
int has_version = 0; int has_version = 0;
unsigned int assigned_version = 0; unsigned int assigned_version = 0;
BEGIN_TYPE_LOCK(); BEGIN_TYPE_LOCK();
@ -5694,11 +5808,8 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve
if (error == -1) { if (error == -1) {
PyErr_Clear(); PyErr_Clear();
} }
if (version != NULL) { *out = PyStackRef_NULL;
// 0 is not a valid version return 0;
*version = 0;
}
return NULL;
} }
if (has_version) { if (has_version) {
@ -5709,11 +5820,8 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve
Py_DECREF(old_value); Py_DECREF(old_value);
#endif #endif
} }
if (version != NULL) { *out = res ? PyStackRef_FromPyObjectSteal(res) : PyStackRef_NULL;
// 0 is not a valid version return has_version ? assigned_version : 0;
*version = has_version ? assigned_version : 0;
}
return res;
} }
/* Internal API to look for a name through the MRO. /* Internal API to look for a name through the MRO.
@ -9802,32 +9910,23 @@ slot_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value)
static int static int
slot_sq_contains(PyObject *self, PyObject *value) slot_sq_contains(PyObject *self, PyObject *value)
{ {
PyThreadState *tstate = _PyThreadState_GET(); int attr_is_none = 0;
PyObject *func, *res; PyObject *res = maybe_call_special_one_arg(self, &_Py_ID(__contains__), value,
int result = -1, unbound; &attr_is_none);
if (attr_is_none) {
func = lookup_maybe_method(self, &_Py_ID(__contains__), &unbound);
if (func == Py_None) {
Py_DECREF(func);
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"'%.200s' object is not a container", "'%.200s' object is not a container",
Py_TYPE(self)->tp_name); Py_TYPE(self)->tp_name);
return -1; return -1;
} }
if (func != NULL) { else if (res == NULL && PyErr_Occurred()) {
PyObject *args[2] = {self, value}; return -1;
res = vectorcall_unbound(tstate, unbound, func, args, 2);
Py_DECREF(func);
if (res != NULL) {
result = PyObject_IsTrue(res);
Py_DECREF(res);
}
} }
else if (! PyErr_Occurred()) { else if (res == NULL) {
/* Possible results: -1 and 1 */ return (int)_PySequence_IterSearch(self, value, PY_ITERSEARCH_CONTAINS);
result = (int)_PySequence_IterSearch(self, value,
PY_ITERSEARCH_CONTAINS);
} }
int result = PyObject_IsTrue(res);
Py_DECREF(res);
return result; return result;
} }
@ -9891,31 +9990,29 @@ SLOT0(slot_nb_absolute, __abs__)
static int static int
slot_nb_bool(PyObject *self) slot_nb_bool(PyObject *self)
{ {
PyObject *func, *value;
int result, unbound;
int using_len = 0; int using_len = 0;
int attr_is_none = 0;
func = lookup_maybe_method(self, &_Py_ID(__bool__), &unbound); PyObject *value = maybe_call_special_no_args(self, &_Py_ID(__bool__),
if (func == NULL) { &attr_is_none);
if (PyErr_Occurred()) { if (attr_is_none) {
return -1; PyErr_Format(PyExc_TypeError,
} "'%.200s' cannot be interpreted as a boolean",
Py_TYPE(self)->tp_name);
func = lookup_maybe_method(self, &_Py_ID(__len__), &unbound); return -1;
if (func == NULL) { }
if (PyErr_Occurred()) { else if (value == NULL && !PyErr_Occurred()) {
return -1; value = _PyObject_MaybeCallSpecialNoArgs(self, &_Py_ID(__len__));
} if (value == NULL && !PyErr_Occurred()) {
return 1; return 1;
} }
using_len = 1; using_len = 1;
} }
value = call_unbound_noarg(unbound, func, self);
if (value == NULL) { if (value == NULL) {
goto error; return -1;
} }
int result;
if (using_len) { if (using_len) {
/* bool type enforced by slot_nb_len */ /* bool type enforced by slot_nb_len */
result = PyObject_IsTrue(value); result = PyObject_IsTrue(value);
@ -9930,14 +10027,8 @@ slot_nb_bool(PyObject *self)
Py_TYPE(value)->tp_name); Py_TYPE(value)->tp_name);
result = -1; result = -1;
} }
Py_DECREF(value); Py_DECREF(value);
Py_DECREF(func);
return result; return result;
error:
Py_DECREF(func);
return -1;
} }
@ -9984,18 +10075,15 @@ SLOT1(slot_nb_inplace_true_divide, __itruediv__, PyObject *)
static PyObject * static PyObject *
slot_tp_repr(PyObject *self) slot_tp_repr(PyObject *self)
{ {
PyObject *func, *res; PyObject *res = _PyObject_MaybeCallSpecialNoArgs(self, &_Py_ID(__repr__));
int unbound; if (res != NULL) {
func = lookup_maybe_method(self, &_Py_ID(__repr__), &unbound);
if (func != NULL) {
res = call_unbound_noarg(unbound, func, self);
Py_DECREF(func);
return res; return res;
} }
PyErr_Clear(); else if (PyErr_Occurred()) {
return NULL;
}
return PyUnicode_FromFormat("<%s object at %p>", return PyUnicode_FromFormat("<%s object at %p>",
Py_TYPE(self)->tp_name, self); Py_TYPE(self)->tp_name, self);
} }
SLOT0(slot_tp_str, __str__) SLOT0(slot_tp_str, __str__)
@ -10003,25 +10091,15 @@ SLOT0(slot_tp_str, __str__)
static Py_hash_t static Py_hash_t
slot_tp_hash(PyObject *self) slot_tp_hash(PyObject *self)
{ {
PyObject *func, *res; PyObject *res;
Py_ssize_t h; int attr_is_none = 0;
int unbound; res = maybe_call_special_no_args(self, &_Py_ID(__hash__), &attr_is_none);
if (attr_is_none || res == NULL) {
func = lookup_maybe_method(self, &_Py_ID(__hash__), &unbound); if (PyErr_Occurred()) {
return -1;
if (func == Py_None) { }
Py_SETREF(func, NULL);
}
if (func == NULL) {
return PyObject_HashNotImplemented(self); return PyObject_HashNotImplemented(self);
} }
res = call_unbound_noarg(unbound, func, self);
Py_DECREF(func);
if (res == NULL)
return -1;
if (!PyLong_Check(res)) { if (!PyLong_Check(res)) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"__hash__ method should return an integer"); "__hash__ method should return an integer");
@ -10032,7 +10110,7 @@ slot_tp_hash(PyObject *self)
Py_hash_t. Therefore our transformation must preserve values that Py_hash_t. Therefore our transformation must preserve values that
already lie within this range, to ensure that if x.__hash__() returns already lie within this range, to ensure that if x.__hash__() returns
hash(y) then hash(x) == hash(y). */ 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()) { if (h == -1 && PyErr_Occurred()) {
/* res was not within the range of a Py_hash_t, so we're free to /* res was not within the range of a Py_hash_t, so we're free to
use any sufficiently bit-mixing transformation; use any sufficiently bit-mixing transformation;
@ -10050,24 +10128,7 @@ slot_tp_hash(PyObject *self)
static PyObject * static PyObject *
slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds) slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds)
{ {
PyThreadState *tstate = _PyThreadState_GET(); return call_method(self, &_Py_ID(__call__), args, kwds);
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;
} }
/* There are two slot dispatch functions for tp_getattro. /* There are two slot dispatch functions for tp_getattro.
@ -10194,51 +10255,46 @@ static PyObject *name_op[] = {
static PyObject * static PyObject *
slot_tp_richcompare(PyObject *self, PyObject *other, int op) slot_tp_richcompare(PyObject *self, PyObject *other, int op)
{ {
PyThreadState *tstate = _PyThreadState_GET(); PyObject *res = _PyObject_MaybeCallSpecialOneArg(self, name_op[op], other);
if (res == NULL) {
int unbound; if (PyErr_Occurred()) {
PyObject *func = lookup_maybe_method(self, name_op[op], &unbound); return NULL;
if (func == NULL) { }
PyErr_Clear();
Py_RETURN_NOTIMPLEMENTED; Py_RETURN_NOTIMPLEMENTED;
} }
PyObject *stack[2] = {self, other};
PyObject *res = vectorcall_unbound(tstate, unbound, func, stack, 2);
Py_DECREF(func);
return res; 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 * static PyObject *
slot_tp_iter(PyObject *self) slot_tp_iter(PyObject *self)
{ {
int unbound; int attr_is_none = 0;
PyObject *func, *res; PyObject *res = maybe_call_special_no_args(self, &_Py_ID(__iter__),
&attr_is_none);
func = lookup_maybe_method(self, &_Py_ID(__iter__), &unbound); if (res != NULL) {
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);
return res; return res;
} }
else if (PyErr_Occurred()) {
PyErr_Clear(); return NULL;
func = lookup_maybe_method(self, &_Py_ID(__getitem__), &unbound); }
if (func == NULL) { else if (attr_is_none || !has_dunder_getitem(self)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"'%.200s' object is not iterable", "'%.200s' object is not iterable",
Py_TYPE(self)->tp_name); Py_TYPE(self)->tp_name);
return NULL; return NULL;
} }
Py_DECREF(func);
return PySeqIter_New(self); return PySeqIter_New(self);
} }
@ -10296,22 +10352,7 @@ slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value)
static int static int
slot_tp_init(PyObject *self, PyObject *args, PyObject *kwds) slot_tp_init(PyObject *self, PyObject *args, PyObject *kwds)
{ {
PyThreadState *tstate = _PyThreadState_GET(); PyObject *res = call_method(self, &_Py_ID(__init__), args, kwds);
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);
if (res == NULL) if (res == NULL)
return -1; return -1;
if (res != Py_None) { if (res != Py_None) {
@ -10344,16 +10385,18 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static void static void
slot_tp_finalize(PyObject *self) slot_tp_finalize(PyObject *self)
{ {
int unbound;
PyObject *del, *res;
/* Save the current exception, if any. */ /* 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. */ /* Execute __del__ method, if any. */
del = lookup_maybe_method(self, &_Py_ID(__del__), &unbound); int unbound = lookup_maybe_method(self, &_Py_ID(__del__), &cref.ref);
if (del != NULL) { if (unbound >= 0) {
res = call_unbound_noarg(unbound, del, self); PyObject *del = PyStackRef_AsPyObjectBorrow(cref.ref);
PyObject *res = call_unbound_noarg(unbound, del, self);
if (res == NULL) { if (res == NULL) {
PyErr_FormatUnraisable("Exception ignored while " PyErr_FormatUnraisable("Exception ignored while "
"calling deallocator %R", del); "calling deallocator %R", del);
@ -10361,11 +10404,12 @@ slot_tp_finalize(PyObject *self)
else { else {
Py_DECREF(res); Py_DECREF(res);
} }
Py_DECREF(del);
} }
_PyThreadState_PopCStackRef(tstate, &cref);
/* Restore the saved exception. */ /* Restore the saved exception. */
PyErr_SetRaisedException(exc); _PyErr_SetRaisedException(tstate, exc);
} }
typedef struct _PyBufferWrapper { typedef struct _PyBufferWrapper {
@ -10613,58 +10657,34 @@ slot_bf_releasebuffer(PyObject *self, Py_buffer *buffer)
releasebuffer_maybe_call_super(self, buffer); releasebuffer_maybe_call_super(self, buffer);
} }
static PyObject *
slot_am_generic(PyObject *self, PyObject *name)
{
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);
}
return res;
}
static PyObject * static PyObject *
slot_am_await(PyObject *self) slot_am_await(PyObject *self)
{ {
int unbound; return slot_am_generic(self, &_Py_ID(__await__));
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;
}
PyErr_Format(PyExc_AttributeError,
"object %.50s does not have __await__ method",
Py_TYPE(self)->tp_name);
return NULL;
} }
static PyObject * static PyObject *
slot_am_aiter(PyObject *self) slot_am_aiter(PyObject *self)
{ {
int unbound; return slot_am_generic(self, &_Py_ID(__aiter__));
PyObject *func, *res;
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 * static PyObject *
slot_am_anext(PyObject *self) slot_am_anext(PyObject *self)
{ {
int unbound; return slot_am_generic(self, &_Py_ID(__anext__));
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;
} }
/* /*

View file

@ -2537,22 +2537,19 @@ static PyObject *
builtin_round_impl(PyObject *module, PyObject *number, PyObject *ndigits) builtin_round_impl(PyObject *module, PyObject *number, PyObject *ndigits)
/*[clinic end generated code: output=ff0d9dd176c02ede input=275678471d7aca15]*/ /*[clinic end generated code: output=ff0d9dd176c02ede input=275678471d7aca15]*/
{ {
PyObject *round, *result; PyObject *result;
if (ndigits == Py_None) {
round = _PyObject_LookupSpecial(number, &_Py_ID(__round__)); result = _PyObject_MaybeCallSpecialNoArgs(number, &_Py_ID(__round__));
if (round == NULL) { }
if (!PyErr_Occurred()) else {
PyErr_Format(PyExc_TypeError, result = _PyObject_MaybeCallSpecialOneArg(number, &_Py_ID(__round__),
"type %.100s doesn't define __round__ method", ndigits);
Py_TYPE(number)->tp_name); }
return NULL; 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; return result;
} }

View file

@ -433,6 +433,12 @@ static void
gc_visit_thread_stacks(PyInterpreterState *interp, struct collection_state *state) gc_visit_thread_stacks(PyInterpreterState *interp, struct collection_state *state)
{ {
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) { _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) { for (_PyInterpreterFrame *f = p->current_frame; f != NULL; f = f->previous) {
if (f->owner >= FRAME_OWNED_BY_INTERPRETER) { if (f->owner >= FRAME_OWNED_BY_INTERPRETER) {
continue; continue;