mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									3d4ac1a2c2
								
							
						
					
					
						commit
						67fbfb42bd
					
				
					 16 changed files with 450 additions and 374 deletions
				
			
		|  | @ -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__)); | ||||
|  | @ -622,6 +623,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__)); | ||||
|  | @ -727,6 +729,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__)); | ||||
|  |  | |||
|  | @ -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__) | ||||
|  | @ -113,6 +114,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__) | ||||
|  | @ -218,6 +220,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__) | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
							
								
								
									
										3
									
								
								Include/internal/pycore_runtime_init_generated.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								Include/internal/pycore_runtime_init_generated.h
									
										
									
										generated
									
									
									
								
							|  | @ -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__), \ | ||||
|  | @ -620,6 +621,7 @@ extern "C" { | |||
|     INIT_ID(__file__), \ | ||||
|     INIT_ID(__firstlineno__), \ | ||||
|     INIT_ID(__float__), \ | ||||
|     INIT_ID(__floor__), \ | ||||
|     INIT_ID(__floordiv__), \ | ||||
|     INIT_ID(__format__), \ | ||||
|     INIT_ID(__fspath__), \ | ||||
|  | @ -725,6 +727,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__), \ | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
| // Like Py_VISIT but for _PyStackRef fields
 | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
							
								
								
									
										12
									
								
								Include/internal/pycore_unicodeobject_generated.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								Include/internal/pycore_unicodeobject_generated.h
									
										
									
										generated
									
									
									
								
							|  | @ -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)); | ||||
|  | @ -240,6 +244,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)); | ||||
|  | @ -660,6 +668,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)); | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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,19 +1127,18 @@ 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,19 +1195,18 @@ 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()) | ||||
|     PyObject *result = _PyObject_MaybeCallSpecialNoArgs(x, &_Py_ID(__trunc__)); | ||||
|     if (result != NULL) { | ||||
|         return result; | ||||
|     } | ||||
|     else 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); | ||||
|     Py_DECREF(trunc); | ||||
|     return result; | ||||
|     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 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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) | ||||
| { | ||||
|  | @ -1146,8 +1142,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 | ||||
|  | @ -1161,30 +1178,11 @@ 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) { | ||||
|     if (!Py_IS_TYPE(type, &PyType_Type) && has_custom_mro(type)) { | ||||
|         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); | ||||
|     for (i = 0; i < n; i++) { | ||||
|         PyObject *b = PyTuple_GET_ITEM(bases, i); | ||||
|  | @ -1224,7 +1222,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(); | ||||
|  | @ -2805,36 +2802,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; | ||||
|     } | ||||
| 
 | ||||
|     if (_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) { | ||||
|         /* Avoid temporary PyMethodObject */ | ||||
|         *unbound = 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)))); | ||||
|         } | ||||
|     } | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| static PyObject * | ||||
| lookup_method(PyObject *self, PyObject *attr, int *unbound) | ||||
| { | ||||
|     PyObject *res = lookup_maybe_method(self, attr, unbound); | ||||
|     if (res == NULL && !PyErr_Occurred()) { | ||||
|     _PyType_LookupStackRefAndVersion(Py_TYPE(self), attr, out); | ||||
|     if (PyStackRef_IsNull(*out)) { | ||||
|         if (raise_attribute_error) { | ||||
|             PyErr_SetObject(PyExc_AttributeError, attr); | ||||
|         } | ||||
|     return res; | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     PyObject *value = PyStackRef_AsPyObjectBorrow(*out); | ||||
|     if (_PyType_HasFeature(Py_TYPE(value), Py_TPFLAGS_METHOD_DESCRIPTOR)) { | ||||
|         /* Avoid temporary PyMethodObject */ | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     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 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| lookup_maybe_method(PyObject *self, PyObject *attr, _PyStackRef *out) | ||||
| { | ||||
|     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); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -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()
 | ||||
|    instead of PyObject_GetAttrString(). | ||||
| 
 | ||||
|  | @ -2875,14 +2926,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; | ||||
|     _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); | ||||
|     } | ||||
|     PyObject *retval = vectorcall_unbound(tstate, unbound, func, args, nargs); | ||||
|     Py_DECREF(func); | ||||
|     _PyThreadState_PopCStackRef(tstate, &cref); | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
|  | @ -2894,19 +2947,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", | ||||
|  | @ -3288,13 +3403,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); | ||||
|  | @ -5611,10 +5720,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]; | ||||
|  | @ -5628,16 +5747,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 (_Py_TryXGetStackRef(&entry->value, out)) { | ||||
|                 // If the sequence is still valid then we're done
 | ||||
|             if (value == NULL || _Py_TryIncref(value)) { | ||||
|                 if (_PySeqLock_EndRead(&entry->sequence, sequence)) { | ||||
|                     if (version != NULL) { | ||||
|                         *version = entry_version; | ||||
|                     return entry_version; | ||||
|                 } | ||||
|                     return value; | ||||
|                 } | ||||
|                 Py_XDECREF(value); | ||||
|                 PyStackRef_XCLOSE(*out); | ||||
|             } | ||||
|             else { | ||||
|                 // 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 | ||||
|     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)); | ||||
|  | @ -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
 | ||||
|     // 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(); | ||||
|  | @ -5694,11 +5808,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) { | ||||
|  | @ -5709,11 +5820,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.
 | ||||
|  | @ -9802,32 +9910,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); | ||||
|         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); | ||||
|     else if (res == NULL && PyErr_Occurred()) { | ||||
|         return -1; | ||||
|     } | ||||
|     else if (res == NULL) { | ||||
|         return (int)_PySequence_IterSearch(self, value, PY_ITERSEARCH_CONTAINS); | ||||
|     } | ||||
|     int result = PyObject_IsTrue(res); | ||||
|     Py_DECREF(res); | ||||
|         } | ||||
|     } | ||||
|     else if (! PyErr_Occurred()) { | ||||
|         /* Possible results: -1 and 1 */ | ||||
|         result = (int)_PySequence_IterSearch(self, value, | ||||
|                                          PY_ITERSEARCH_CONTAINS); | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
|  | @ -9891,31 +9990,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()) { | ||||
|     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); | ||||
|  | @ -9930,14 +10027,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; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -9984,16 +10075,13 @@ 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); | ||||
| } | ||||
|  | @ -10003,25 +10091,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); | ||||
|     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; | ||||
|         } | ||||
| 
 | ||||
|     if (func == NULL) { | ||||
|         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"); | ||||
|  | @ -10032,7 +10110,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; | ||||
|  | @ -10050,24 +10128,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.
 | ||||
|  | @ -10194,51 +10255,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); | ||||
|         return NULL; | ||||
|     } | ||||
|     Py_DECREF(func); | ||||
|     return PySeqIter_New(self); | ||||
| } | ||||
| 
 | ||||
|  | @ -10296,22 +10352,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) { | ||||
|  | @ -10344,16 +10385,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); | ||||
|  | @ -10361,11 +10404,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 { | ||||
|  | @ -10614,57 +10658,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); | ||||
|     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; | ||||
| } | ||||
|     PyErr_Format(PyExc_AttributeError, | ||||
|                  "object %.50s does not have __await__ method", | ||||
|                  Py_TYPE(self)->tp_name); | ||||
|     return NULL; | ||||
| 
 | ||||
| static PyObject * | ||||
| slot_am_await(PyObject *self) | ||||
| { | ||||
|     return slot_am_generic(self, &_Py_ID(__await__)); | ||||
| } | ||||
| 
 | ||||
| static PyObject * | ||||
| slot_am_aiter(PyObject *self) | ||||
| { | ||||
|     int unbound; | ||||
|     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; | ||||
|     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__)); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
|  | @ -2537,22 +2537,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()) | ||||
|     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); | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     if (ndigits == Py_None) | ||||
|         result = _PyObject_CallNoArgs(round); | ||||
|     else | ||||
|         result = PyObject_CallOneArg(round, ndigits); | ||||
|     Py_DECREF(round); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sam Gross
						Sam Gross