mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +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(__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__)); | ||||||
|  |  | ||||||
|  | @ -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__) | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  |  | ||||||
							
								
								
									
										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(__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__), \ | ||||||
|  |  | ||||||
|  | @ -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
 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
							
								
								
									
										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); |     _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)); | ||||||
|  |  | ||||||
|  | @ -383,6 +383,10 @@ def __len__(self): | ||||||
|             __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) | ||||||
|  |  | ||||||
|  | @ -1746,6 +1746,11 @@ def test_repr(self): | ||||||
|         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) | ||||||
|  |  | ||||||
|  | @ -573,6 +573,8 @@ def testFloor(self): | ||||||
|         #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 TestBadFloor: | ||||||
|         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 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sam Gross
						Sam Gross