mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	GH-95707: Fix uses of Py_TPFLAGS_MANAGED_DICT (GH-95854)
				
					
				
			* Make sure that tp_dictoffset is correct with Py_TPFLAGS_MANAGED_DICT is set. * Avoid traversing managed dict twice when subclassing class with Py_TPFLAGS_MANAGED_DICT set.
This commit is contained in:
		
							parent
							
								
									4a7f5a55dc
								
							
						
					
					
						commit
						3ef3c6306d
					
				
					 7 changed files with 137 additions and 25 deletions
				
			
		|  | @ -539,6 +539,30 @@ def test_heaptype_with_dict(self): | ||||||
|         inst = _testcapi.HeapCTypeWithDict() |         inst = _testcapi.HeapCTypeWithDict() | ||||||
|         self.assertEqual({}, inst.__dict__) |         self.assertEqual({}, inst.__dict__) | ||||||
| 
 | 
 | ||||||
|  |     def test_heaptype_with_managed_dict(self): | ||||||
|  |         inst = _testcapi.HeapCTypeWithManagedDict() | ||||||
|  |         inst.foo = 42 | ||||||
|  |         self.assertEqual(inst.foo, 42) | ||||||
|  |         self.assertEqual(inst.__dict__, {"foo": 42}) | ||||||
|  | 
 | ||||||
|  |         inst = _testcapi.HeapCTypeWithManagedDict() | ||||||
|  |         self.assertEqual({}, inst.__dict__) | ||||||
|  | 
 | ||||||
|  |         a = _testcapi.HeapCTypeWithManagedDict() | ||||||
|  |         b = _testcapi.HeapCTypeWithManagedDict() | ||||||
|  |         a.b = b | ||||||
|  |         b.a = a | ||||||
|  |         del a, b | ||||||
|  | 
 | ||||||
|  |     def test_sublclassing_managed_dict(self): | ||||||
|  | 
 | ||||||
|  |         class C(_testcapi.HeapCTypeWithManagedDict): | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  |         i = C() | ||||||
|  |         i.spam = i | ||||||
|  |         del i | ||||||
|  | 
 | ||||||
|     def test_heaptype_with_negative_dict(self): |     def test_heaptype_with_negative_dict(self): | ||||||
|         inst = _testcapi.HeapCTypeWithNegativeDict() |         inst = _testcapi.HeapCTypeWithNegativeDict() | ||||||
|         inst.foo = 42 |         inst.foo = 42 | ||||||
|  |  | ||||||
|  | @ -1287,7 +1287,7 @@ class OverflowSizeof(int): | ||||||
|             def __sizeof__(self): |             def __sizeof__(self): | ||||||
|                 return int(self) |                 return int(self) | ||||||
|         self.assertEqual(sys.getsizeof(OverflowSizeof(sys.maxsize)), |         self.assertEqual(sys.getsizeof(OverflowSizeof(sys.maxsize)), | ||||||
|                          sys.maxsize + self.gc_headsize) |                          sys.maxsize + self.gc_headsize*2) | ||||||
|         with self.assertRaises(OverflowError): |         with self.assertRaises(OverflowError): | ||||||
|             sys.getsizeof(OverflowSizeof(sys.maxsize + 1)) |             sys.getsizeof(OverflowSizeof(sys.maxsize + 1)) | ||||||
|         with self.assertRaises(ValueError): |         with self.assertRaises(ValueError): | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | Support C extensions using managed dictionaries by setting the | ||||||
|  | ``Py_TPFLAGS_MANAGED_DICT`` flag. | ||||||
|  | @ -761,6 +761,45 @@ static PyType_Spec HeapCTypeWithDict2_spec = { | ||||||
|     HeapCTypeWithDict_slots |     HeapCTypeWithDict_slots | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static int | ||||||
|  | heapmanaged_traverse(HeapCTypeObject *self, visitproc visit, void *arg) | ||||||
|  | { | ||||||
|  |     Py_VISIT(Py_TYPE(self)); | ||||||
|  |     return _PyObject_VisitManagedDict((PyObject *)self, visit, arg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | heapmanaged_clear(HeapCTypeObject *self) | ||||||
|  | { | ||||||
|  |     _PyObject_ClearManagedDict((PyObject *)self); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | heapmanaged_dealloc(HeapCTypeObject *self) | ||||||
|  | { | ||||||
|  |     PyTypeObject *tp = Py_TYPE(self); | ||||||
|  |     _PyObject_ClearManagedDict((PyObject *)self); | ||||||
|  |     PyObject_GC_UnTrack(self); | ||||||
|  |     PyObject_GC_Del(self); | ||||||
|  |     Py_DECREF(tp); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyType_Slot HeapCTypeWithManagedDict_slots[] = { | ||||||
|  |     {Py_tp_traverse, heapmanaged_traverse}, | ||||||
|  |     {Py_tp_getset, heapctypewithdict_getsetlist}, | ||||||
|  |     {Py_tp_clear, heapmanaged_clear}, | ||||||
|  |     {Py_tp_dealloc, heapmanaged_dealloc}, | ||||||
|  |     {0, 0}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static PyType_Spec  HeapCTypeWithManagedDict_spec = { | ||||||
|  |     "_testcapi.HeapCTypeWithManagedDict", | ||||||
|  |     sizeof(PyObject), | ||||||
|  |     0, | ||||||
|  |     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_MANAGED_DICT, | ||||||
|  |     HeapCTypeWithManagedDict_slots | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| static struct PyMemberDef heapctypewithnegativedict_members[] = { | static struct PyMemberDef heapctypewithnegativedict_members[] = { | ||||||
|     {"dictobj", T_OBJECT, offsetof(HeapCTypeWithDictObject, dict)}, |     {"dictobj", T_OBJECT, offsetof(HeapCTypeWithDictObject, dict)}, | ||||||
|     {"__dictoffset__", T_PYSSIZET, -(Py_ssize_t)sizeof(void*), READONLY}, |     {"__dictoffset__", T_PYSSIZET, -(Py_ssize_t)sizeof(void*), READONLY}, | ||||||
|  | @ -963,6 +1002,12 @@ _PyTestCapi_Init_Heaptype(PyObject *m) { | ||||||
|     } |     } | ||||||
|     PyModule_AddObject(m, "HeapCTypeWithNegativeDict", HeapCTypeWithNegativeDict); |     PyModule_AddObject(m, "HeapCTypeWithNegativeDict", HeapCTypeWithNegativeDict); | ||||||
| 
 | 
 | ||||||
|  |     PyObject *HeapCTypeWithManagedDict = PyType_FromSpec(&HeapCTypeWithManagedDict_spec); | ||||||
|  |     if (HeapCTypeWithManagedDict == NULL) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     PyModule_AddObject(m, "HeapCTypeWithManagedDict", HeapCTypeWithManagedDict); | ||||||
|  | 
 | ||||||
|     PyObject *HeapCTypeWithWeakref = PyType_FromSpec(&HeapCTypeWithWeakref_spec); |     PyObject *HeapCTypeWithWeakref = PyType_FromSpec(&HeapCTypeWithWeakref_spec); | ||||||
|     if (HeapCTypeWithWeakref == NULL) { |     if (HeapCTypeWithWeakref == NULL) { | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|  | @ -1064,6 +1064,7 @@ _PyObject_ComputedDictPointer(PyObject *obj) | ||||||
|     if (dictoffset == 0) |     if (dictoffset == 0) | ||||||
|         return NULL; |         return NULL; | ||||||
|     if (dictoffset < 0) { |     if (dictoffset < 0) { | ||||||
|  |         assert(dictoffset != -1); | ||||||
|         Py_ssize_t tsize = Py_SIZE(obj); |         Py_ssize_t tsize = Py_SIZE(obj); | ||||||
|         if (tsize < 0) { |         if (tsize < 0) { | ||||||
|             tsize = -tsize; |             tsize = -tsize; | ||||||
|  |  | ||||||
|  | @ -1312,16 +1312,21 @@ subtype_traverse(PyObject *self, visitproc visit, void *arg) | ||||||
|         assert(base); |         assert(base); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { |     if (type->tp_dictoffset != base->tp_dictoffset) { | ||||||
|         int err = _PyObject_VisitManagedDict(self, visit, arg); |         assert(base->tp_dictoffset == 0); | ||||||
|         if (err) { |         if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { | ||||||
|             return err; |             assert(type->tp_dictoffset == -1); | ||||||
|  |             int err = _PyObject_VisitManagedDict(self, visit, arg); | ||||||
|  |             if (err) { | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             PyObject **dictptr = _PyObject_ComputedDictPointer(self); | ||||||
|  |             if (dictptr && *dictptr) { | ||||||
|  |                 Py_VISIT(*dictptr); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |  | ||||||
|     else if (type->tp_dictoffset != base->tp_dictoffset) { |  | ||||||
|         PyObject **dictptr = _PyObject_ComputedDictPointer(self); |  | ||||||
|         if (dictptr && *dictptr) |  | ||||||
|             Py_VISIT(*dictptr); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (type->tp_flags & Py_TPFLAGS_HEAPTYPE |     if (type->tp_flags & Py_TPFLAGS_HEAPTYPE | ||||||
|  | @ -1380,7 +1385,9 @@ subtype_clear(PyObject *self) | ||||||
|     /* Clear the instance dict (if any), to break cycles involving only
 |     /* Clear the instance dict (if any), to break cycles involving only
 | ||||||
|        __dict__ slots (as in the case 'self.__dict__ is self'). */ |        __dict__ slots (as in the case 'self.__dict__ is self'). */ | ||||||
|     if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { |     if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { | ||||||
|         _PyObject_ClearManagedDict(self); |         if ((base->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { | ||||||
|  |             _PyObject_ClearManagedDict(self); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     else if (type->tp_dictoffset != base->tp_dictoffset) { |     else if (type->tp_dictoffset != base->tp_dictoffset) { | ||||||
|         PyObject **dictptr = _PyObject_ComputedDictPointer(self); |         PyObject **dictptr = _PyObject_ComputedDictPointer(self); | ||||||
|  | @ -3085,20 +3092,15 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (ctx->add_dict && ctx->base->tp_itemsize) { |  | ||||||
|         type->tp_dictoffset = -(long)sizeof(PyObject *); |  | ||||||
|         slotoffset += sizeof(PyObject *); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (ctx->add_weak) { |     if (ctx->add_weak) { | ||||||
|         assert(!ctx->base->tp_itemsize); |         assert(!ctx->base->tp_itemsize); | ||||||
|         type->tp_weaklistoffset = slotoffset; |         type->tp_weaklistoffset = slotoffset; | ||||||
|         slotoffset += sizeof(PyObject *); |         slotoffset += sizeof(PyObject *); | ||||||
|     } |     } | ||||||
|     if (ctx->add_dict && ctx->base->tp_itemsize == 0) { |     if (ctx->add_dict) { | ||||||
|         assert((type->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0); |         assert((type->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0); | ||||||
|         type->tp_flags |= Py_TPFLAGS_MANAGED_DICT; |         type->tp_flags |= Py_TPFLAGS_MANAGED_DICT; | ||||||
|         type->tp_dictoffset = -slotoffset - sizeof(PyObject *)*3; |         type->tp_dictoffset = -1; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     type->tp_basicsize = slotoffset; |     type->tp_basicsize = slotoffset; | ||||||
|  | @ -6161,6 +6163,7 @@ inherit_special(PyTypeObject *type, PyTypeObject *base) | ||||||
|     COPYVAL(tp_itemsize); |     COPYVAL(tp_itemsize); | ||||||
|     COPYVAL(tp_weaklistoffset); |     COPYVAL(tp_weaklistoffset); | ||||||
|     COPYVAL(tp_dictoffset); |     COPYVAL(tp_dictoffset); | ||||||
|  | 
 | ||||||
| #undef COPYVAL | #undef COPYVAL | ||||||
| 
 | 
 | ||||||
|     /* Setup fast subclass flags */ |     /* Setup fast subclass flags */ | ||||||
|  | @ -6567,6 +6570,21 @@ type_ready_fill_dict(PyTypeObject *type) | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int | ||||||
|  | type_ready_dict_offset(PyTypeObject *type) | ||||||
|  | { | ||||||
|  |     if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { | ||||||
|  |         if (type->tp_dictoffset > 0 || type->tp_dictoffset < -1) { | ||||||
|  |             PyErr_Format(PyExc_TypeError, | ||||||
|  |                         "type %s has the Py_TPFLAGS_MANAGED_DICT flag " | ||||||
|  |                         "but tp_dictoffset is set", | ||||||
|  |                         type->tp_name); | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |         type->tp_dictoffset = -1; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| static int | static int | ||||||
| type_ready_mro(PyTypeObject *type) | type_ready_mro(PyTypeObject *type) | ||||||
|  | @ -6775,6 +6793,21 @@ type_ready_post_checks(PyTypeObject *type) | ||||||
|                      type->tp_name); |                      type->tp_name); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |     if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { | ||||||
|  |         if (type->tp_dictoffset != -1) { | ||||||
|  |             PyErr_Format(PyExc_SystemError, | ||||||
|  |                         "type %s has the Py_TPFLAGS_MANAGED_DICT flag " | ||||||
|  |                         "but tp_dictoffset is set to incompatible value", | ||||||
|  |                         type->tp_name); | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else if (type->tp_dictoffset < sizeof(PyObject)) { | ||||||
|  |         if (type->tp_dictoffset + type->tp_basicsize <= 0) { | ||||||
|  |             PyErr_Format(PyExc_SystemError, | ||||||
|  |                          "type %s has a tp_dictoffset that is too small"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -6814,6 +6847,9 @@ type_ready(PyTypeObject *type) | ||||||
|     if (type_ready_inherit(type) < 0) { |     if (type_ready_inherit(type) < 0) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |     if (type_ready_dict_offset(type) < 0) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|     if (type_ready_set_hash(type) < 0) { |     if (type_ready_set_hash(type) < 0) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -478,13 +478,17 @@ def get_attr_dict(self): | ||||||
|             dictoffset = int_from_int(typeobj.field('tp_dictoffset')) |             dictoffset = int_from_int(typeobj.field('tp_dictoffset')) | ||||||
|             if dictoffset != 0: |             if dictoffset != 0: | ||||||
|                 if dictoffset < 0: |                 if dictoffset < 0: | ||||||
|                     type_PyVarObject_ptr = gdb.lookup_type('PyVarObject').pointer() |                     if int_from_int(typeobj.field('tp_flags')) & Py_TPFLAGS_MANAGED_DICT: | ||||||
|                     tsize = int_from_int(self._gdbval.cast(type_PyVarObject_ptr)['ob_size']) |                         assert dictoffset == -1 | ||||||
|                     if tsize < 0: |                         dictoffset = -3 * _sizeof_void_p() | ||||||
|                         tsize = -tsize |                     else: | ||||||
|                     size = _PyObject_VAR_SIZE(typeobj, tsize) |                         type_PyVarObject_ptr = gdb.lookup_type('PyVarObject').pointer() | ||||||
|                     dictoffset += size |                         tsize = int_from_int(self._gdbval.cast(type_PyVarObject_ptr)['ob_size']) | ||||||
|                     assert dictoffset % _sizeof_void_p() == 0 |                         if tsize < 0: | ||||||
|  |                             tsize = -tsize | ||||||
|  |                         size = _PyObject_VAR_SIZE(typeobj, tsize) | ||||||
|  |                         dictoffset += size | ||||||
|  |                         assert dictoffset % _sizeof_void_p() == 0 | ||||||
| 
 | 
 | ||||||
|                 dictptr = self._gdbval.cast(_type_char_ptr()) + dictoffset |                 dictptr = self._gdbval.cast(_type_char_ptr()) + dictoffset | ||||||
|                 PyObjectPtrPtr = PyObjectPtr.get_gdb_type().pointer() |                 PyObjectPtrPtr = PyObjectPtr.get_gdb_type().pointer() | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Mark Shannon
						Mark Shannon