mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	gh-103509: PEP 697 -- Limited C API for Extending Opaque Types (GH-103511)
Co-authored-by: Oleg Iarygin <oleg@arhadthedev.net> Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
This commit is contained in:
		
							parent
							
								
									35d273825a
								
							
						
					
					
						commit
						cd9a56c2b0
					
				
					 30 changed files with 970 additions and 19 deletions
				
			
		|  | @ -395,3 +395,42 @@ Object Protocol | ||||||
|    returns ``NULL`` if the object cannot be iterated. |    returns ``NULL`` if the object cannot be iterated. | ||||||
| 
 | 
 | ||||||
|    .. versionadded:: 3.10 |    .. versionadded:: 3.10 | ||||||
|  | 
 | ||||||
|  | .. c:function:: void *PyObject_GetTypeData(PyObject *o, PyTypeObject *cls) | ||||||
|  | 
 | ||||||
|  |    Get a pointer to subclass-specific data reserved for *cls*. | ||||||
|  | 
 | ||||||
|  |    The object *o* must be an instance of *cls*, and *cls* must have been | ||||||
|  |    created using negative :c:member:`PyType_Spec.basicsize`. | ||||||
|  |    Python does not check this. | ||||||
|  | 
 | ||||||
|  |    On error, set an exception and return ``NULL``. | ||||||
|  | 
 | ||||||
|  |    .. versionadded:: 3.12 | ||||||
|  | 
 | ||||||
|  | .. c:function:: Py_ssize_t PyType_GetTypeDataSize(PyTypeObject *cls) | ||||||
|  | 
 | ||||||
|  |    Return the size of the instance memory space reserved for *cls*, i.e. the size of the | ||||||
|  |    memory :c:func:`PyObject_GetTypeData` returns. | ||||||
|  | 
 | ||||||
|  |    This may be larger than requested using :c:member:`-PyType_Spec.basicsize <PyType_Spec.basicsize>`; | ||||||
|  |    it is safe to use this larger size (e.g. with :c:func:`!memset`). | ||||||
|  | 
 | ||||||
|  |    The type *cls* **must** have been created using | ||||||
|  |    negative :c:member:`PyType_Spec.basicsize`. | ||||||
|  |    Python does not check this. | ||||||
|  | 
 | ||||||
|  |    On error, set an exception and return a negative value. | ||||||
|  | 
 | ||||||
|  |    .. versionadded:: 3.12 | ||||||
|  | 
 | ||||||
|  | .. c:function:: void *PyObject_GetItemData(PyObject *o) | ||||||
|  | 
 | ||||||
|  |    Get a pointer to per-item data for a class with | ||||||
|  |    :const:`Py_TPFLAGS_ITEMS_AT_END`. | ||||||
|  | 
 | ||||||
|  |    On error, set an exception and return ``NULL``. | ||||||
|  |    :py:exc:`TypeError` is raised if *o* does not have | ||||||
|  |    :const:`Py_TPFLAGS_ITEMS_AT_END` set. | ||||||
|  | 
 | ||||||
|  |    .. versionadded:: 3.12 | ||||||
|  |  | ||||||
|  | @ -486,6 +486,22 @@ The following flags can be used with :c:member:`PyMemberDef.flags`: | ||||||
|    Emit an ``object.__getattr__`` :ref:`audit event <audit-events>` |    Emit an ``object.__getattr__`` :ref:`audit event <audit-events>` | ||||||
|    before reading. |    before reading. | ||||||
| 
 | 
 | ||||||
|  | .. c:macro:: Py_RELATIVE_OFFSET | ||||||
|  | 
 | ||||||
|  |    Indicates that the :c:member:`~PyMemberDef.offset` of this ``PyMemberDef`` | ||||||
|  |    entry indicates an offset from the subclass-specific data, rather than | ||||||
|  |    from ``PyObject``. | ||||||
|  | 
 | ||||||
|  |    Can only be used as part of :c:member:`Py_tp_members <PyTypeObject.tp_members>` | ||||||
|  |    :c:type:`slot <PyTypeSlot>` when creating a class using negative | ||||||
|  |    :c:member:`~PyTypeDef.basicsize`. | ||||||
|  |    It is mandatory in that case. | ||||||
|  | 
 | ||||||
|  |    This flag is only used in :c:type:`PyTypeSlot`. | ||||||
|  |    When setting :c:member:`~PyTypeObject.tp_members` during | ||||||
|  |    class creation, Python clears it and sets | ||||||
|  |    :c:member:`PyMemberDef.offset` to the offset from the ``PyObject`` struct. | ||||||
|  | 
 | ||||||
| .. index:: | .. index:: | ||||||
|    single: READ_RESTRICTED |    single: READ_RESTRICTED | ||||||
|    single: WRITE_RESTRICTED |    single: WRITE_RESTRICTED | ||||||
|  |  | ||||||
|  | @ -353,25 +353,57 @@ The following functions and structs are used to create | ||||||
| 
 | 
 | ||||||
|    Structure defining a type's behavior. |    Structure defining a type's behavior. | ||||||
| 
 | 
 | ||||||
|    .. c:member:: const char* PyType_Spec.name |    .. c:member:: const char* name | ||||||
| 
 | 
 | ||||||
|       Name of the type, used to set :c:member:`PyTypeObject.tp_name`. |       Name of the type, used to set :c:member:`PyTypeObject.tp_name`. | ||||||
| 
 | 
 | ||||||
|    .. c:member:: int PyType_Spec.basicsize |    .. c:member:: int basicsize | ||||||
|    .. c:member:: int PyType_Spec.itemsize |  | ||||||
| 
 | 
 | ||||||
|       Size of the instance in bytes, used to set |       If positive, specifies the size of the instance in bytes. | ||||||
|       :c:member:`PyTypeObject.tp_basicsize` and |       It is used to set :c:member:`PyTypeObject.tp_basicsize`. | ||||||
|       :c:member:`PyTypeObject.tp_itemsize`. |  | ||||||
| 
 | 
 | ||||||
|    .. c:member:: int PyType_Spec.flags |       If zero, specifies that :c:member:`~PyTypeObject.tp_basicsize` | ||||||
|  |       should be inherited. | ||||||
|  | 
 | ||||||
|  |       If negative, the absolute value specifies how much space instances of the | ||||||
|  |       class need *in addition* to the superclass. | ||||||
|  |       Use :c:func:`PyObject_GetTypeData` to get a pointer to subclass-specific | ||||||
|  |       memory reserved this way. | ||||||
|  | 
 | ||||||
|  |       .. versionchanged:: 3.12 | ||||||
|  | 
 | ||||||
|  |          Previously, this field could not be negative. | ||||||
|  | 
 | ||||||
|  |    .. c:member:: int itemsize | ||||||
|  | 
 | ||||||
|  |       Size of one element of a variable-size type, in bytes. | ||||||
|  |       Used to set :c:member:`PyTypeObject.tp_itemsize`. | ||||||
|  |       See ``tp_itemsize`` documentation for caveats. | ||||||
|  | 
 | ||||||
|  |       If zero, :c:member:`~PyTypeObject.tp_itemsize` is inherited. | ||||||
|  |       Extending arbitrary variable-sized classes is dangerous, | ||||||
|  |       since some types use a fixed offset for variable-sized memory, | ||||||
|  |       which can then overlap fixed-sized memory used by a subclass. | ||||||
|  |       To help prevent mistakes, inheriting ``itemsize`` is only possible | ||||||
|  |       in the following situations: | ||||||
|  | 
 | ||||||
|  |       - The base is not variable-sized (its | ||||||
|  |         :c:member:`~PyTypeObject.tp_itemsize`). | ||||||
|  |       - The requested :c:member:`PyType_Spec.basicsize` is positive, | ||||||
|  |         suggesting that the memory layout of the base class is known. | ||||||
|  |       - The requested :c:member:`PyType_Spec.basicsize` is zero, | ||||||
|  |         suggesting that the subclass does not access the instance's memory | ||||||
|  |         directly. | ||||||
|  |       - With the :const:`Py_TPFLAGS_ITEMS_AT_END` flag. | ||||||
|  | 
 | ||||||
|  |    .. c:member:: unsigned int flags | ||||||
| 
 | 
 | ||||||
|       Type flags, used to set :c:member:`PyTypeObject.tp_flags`. |       Type flags, used to set :c:member:`PyTypeObject.tp_flags`. | ||||||
| 
 | 
 | ||||||
|       If the ``Py_TPFLAGS_HEAPTYPE`` flag is not set, |       If the ``Py_TPFLAGS_HEAPTYPE`` flag is not set, | ||||||
|       :c:func:`PyType_FromSpecWithBases` sets it automatically. |       :c:func:`PyType_FromSpecWithBases` sets it automatically. | ||||||
| 
 | 
 | ||||||
|    .. c:member:: PyType_Slot *PyType_Spec.slots |    .. c:member:: PyType_Slot *slots | ||||||
| 
 | 
 | ||||||
|       Array of :c:type:`PyType_Slot` structures. |       Array of :c:type:`PyType_Slot` structures. | ||||||
|       Terminated by the special slot value ``{0, NULL}``. |       Terminated by the special slot value ``{0, NULL}``. | ||||||
|  |  | ||||||
|  | @ -1171,6 +1171,26 @@ and :c:type:`PyType_Type` effectively act as defaults.) | ||||||
|       :c:member:`~PyTypeObject.tp_weaklistoffset` field is set in a superclass. |       :c:member:`~PyTypeObject.tp_weaklistoffset` field is set in a superclass. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |    .. data:: Py_TPFLAGS_ITEMS_AT_END | ||||||
|  | 
 | ||||||
|  |       Only usable with variable-size types, i.e. ones with non-zero | ||||||
|  |       :c:member:`~PyObject.tp_itemsize`. | ||||||
|  | 
 | ||||||
|  |       Indicates that the variable-sized portion of an instance of this type is | ||||||
|  |       at the end of the instance's memory area, at an offset of | ||||||
|  |       :c:expr:`Py_TYPE(obj)->tp_basicsize` (which may be different in each | ||||||
|  |       subclass). | ||||||
|  | 
 | ||||||
|  |       When setting this flag, be sure that all superclasses either | ||||||
|  |       use this memory layout, or are not variable-sized. | ||||||
|  |       Python does not check this. | ||||||
|  | 
 | ||||||
|  |       .. versionadded:: 3.12 | ||||||
|  | 
 | ||||||
|  |       **Inheritance:** | ||||||
|  | 
 | ||||||
|  |       This flag is inherited. | ||||||
|  | 
 | ||||||
|    .. XXX Document more flags here? |    .. XXX Document more flags here? | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								Doc/data/stable_abi.dat
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								Doc/data/stable_abi.dat
									
										
									
										generated
									
									
									
								
							|  | @ -521,6 +521,7 @@ function,PyObject_GetAttrString,3.2,, | ||||||
| function,PyObject_GetBuffer,3.11,, | function,PyObject_GetBuffer,3.11,, | ||||||
| function,PyObject_GetItem,3.2,, | function,PyObject_GetItem,3.2,, | ||||||
| function,PyObject_GetIter,3.2,, | function,PyObject_GetIter,3.2,, | ||||||
|  | function,PyObject_GetTypeData,3.12,, | ||||||
| function,PyObject_HasAttr,3.2,, | function,PyObject_HasAttr,3.2,, | ||||||
| function,PyObject_HasAttrString,3.2,, | function,PyObject_HasAttrString,3.2,, | ||||||
| function,PyObject_Hash,3.2,, | function,PyObject_Hash,3.2,, | ||||||
|  | @ -675,6 +676,7 @@ function,PyType_GetModuleState,3.10,, | ||||||
| function,PyType_GetName,3.11,, | function,PyType_GetName,3.11,, | ||||||
| function,PyType_GetQualName,3.11,, | function,PyType_GetQualName,3.11,, | ||||||
| function,PyType_GetSlot,3.4,, | function,PyType_GetSlot,3.4,, | ||||||
|  | function,PyType_GetTypeDataSize,3.12,, | ||||||
| function,PyType_IsSubtype,3.2,, | function,PyType_IsSubtype,3.2,, | ||||||
| function,PyType_Modified,3.2,, | function,PyType_Modified,3.2,, | ||||||
| function,PyType_Ready,3.2,, | function,PyType_Ready,3.2,, | ||||||
|  |  | ||||||
|  | @ -1159,6 +1159,21 @@ New Features | ||||||
| 
 | 
 | ||||||
|   (Contributed by Petr Viktorin in :gh:`101101`.) |   (Contributed by Petr Viktorin in :gh:`101101`.) | ||||||
| 
 | 
 | ||||||
|  | * :pep:`697`: Added API for extending types whose instance memory layout is | ||||||
|  |   opaque: | ||||||
|  | 
 | ||||||
|  |   - :c:member:`PyType_Spec.basicsize` can be zero or negative to specify | ||||||
|  |     inheriting or extending the base class size. | ||||||
|  |   - :c:func:`PyObject_GetTypeData` and :c:func:`PyType_GetTypeDataSize` | ||||||
|  |     added to allow access to subclass-specific instance data. | ||||||
|  |   - :const:`Py_TPFLAGS_ITEMS_AT_END` and :c:func:`PyObject_GetItemData` | ||||||
|  |     added to allow safely extending certain variable-sized types, including | ||||||
|  |     :c:var:`PyType_Type`. | ||||||
|  |   - :c:macro:`Py_RELATIVE_OFFSET` added to allow defining | ||||||
|  |     :c:type:`members <PyMemberDef>` in terms of a subclass-specific struct. | ||||||
|  | 
 | ||||||
|  |   (Contributed by Petr Viktorin in :gh:`103509`.) | ||||||
|  | 
 | ||||||
| * Added the new limited C API function :c:func:`PyType_FromMetaclass`, | * Added the new limited C API function :c:func:`PyType_FromMetaclass`, | ||||||
|   which generalizes the existing :c:func:`PyType_FromModuleAndSpec` using |   which generalizes the existing :c:func:`PyType_FromModuleAndSpec` using | ||||||
|   an additional metaclass argument. |   an additional metaclass argument. | ||||||
|  |  | ||||||
|  | @ -553,6 +553,7 @@ Py_DEPRECATED(3.11) typedef int UsingDeprecatedTrashcanMacro; | ||||||
|         Py_TRASHCAN_END; \ |         Py_TRASHCAN_END; \ | ||||||
|     } while(0); |     } while(0); | ||||||
| 
 | 
 | ||||||
|  | PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj); | ||||||
| 
 | 
 | ||||||
| PyAPI_FUNC(int) _PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg); | PyAPI_FUNC(int) _PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg); | ||||||
| PyAPI_FUNC(void) _PyObject_ClearManagedDict(PyObject *obj); | PyAPI_FUNC(void) _PyObject_ClearManagedDict(PyObject *obj); | ||||||
|  |  | ||||||
|  | @ -83,6 +83,7 @@ struct PyMemberDef { | ||||||
| #define Py_READONLY            1 | #define Py_READONLY            1 | ||||||
| #define Py_AUDIT_READ          2 // Added in 3.10, harmless no-op before that
 | #define Py_AUDIT_READ          2 // Added in 3.10, harmless no-op before that
 | ||||||
| #define _Py_WRITE_RESTRICTED   4 // Deprecated, no-op. Do not reuse the value.
 | #define _Py_WRITE_RESTRICTED   4 // Deprecated, no-op. Do not reuse the value.
 | ||||||
|  | #define Py_RELATIVE_OFFSET     8 | ||||||
| 
 | 
 | ||||||
| PyAPI_FUNC(PyObject *) PyMember_GetOne(const char *, PyMemberDef *); | PyAPI_FUNC(PyObject *) PyMember_GetOne(const char *, PyMemberDef *); | ||||||
| PyAPI_FUNC(int) PyMember_SetOne(char *, PyMemberDef *, PyObject *); | PyAPI_FUNC(int) PyMember_SetOne(char *, PyMemberDef *, PyObject *); | ||||||
|  |  | ||||||
|  | @ -389,11 +389,6 @@ extern PyObject ** _PyObject_ComputedDictPointer(PyObject *); | ||||||
| extern void _PyObject_FreeInstanceAttributes(PyObject *obj); | extern void _PyObject_FreeInstanceAttributes(PyObject *obj); | ||||||
| extern int _PyObject_IsInstanceDictEmpty(PyObject *); | extern int _PyObject_IsInstanceDictEmpty(PyObject *); | ||||||
| 
 | 
 | ||||||
| // Access macro to the members which are floating "behind" the object
 |  | ||||||
| static inline PyMemberDef* _PyHeapType_GET_MEMBERS(PyHeapTypeObject *etype) { |  | ||||||
|     return (PyMemberDef*)((char*)etype + Py_TYPE(etype)->tp_basicsize); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, PyObject *); | PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, PyObject *); | ||||||
| 
 | 
 | ||||||
| /* C function call trampolines to mitigate bad function pointer casts.
 | /* C function call trampolines to mitigate bad function pointer casts.
 | ||||||
|  |  | ||||||
|  | @ -355,6 +355,8 @@ PyAPI_FUNC(PyObject *) PyType_GetQualName(PyTypeObject *); | ||||||
| #endif | #endif | ||||||
| #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030C0000 | #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030C0000 | ||||||
| PyAPI_FUNC(PyObject *) PyType_FromMetaclass(PyTypeObject*, PyObject*, PyType_Spec*, PyObject*); | PyAPI_FUNC(PyObject *) PyType_FromMetaclass(PyTypeObject*, PyObject*, PyType_Spec*, PyObject*); | ||||||
|  | PyAPI_FUNC(void *) PyObject_GetTypeData(PyObject *obj, PyTypeObject *cls); | ||||||
|  | PyAPI_FUNC(Py_ssize_t) PyType_GetTypeDataSize(PyTypeObject *cls); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* Generic type check */ | /* Generic type check */ | ||||||
|  | @ -521,6 +523,9 @@ given type object has a specified feature. | ||||||
| // subject itself (rather than a mapped attribute on it):
 | // subject itself (rather than a mapped attribute on it):
 | ||||||
| #define _Py_TPFLAGS_MATCH_SELF (1UL << 22) | #define _Py_TPFLAGS_MATCH_SELF (1UL << 22) | ||||||
| 
 | 
 | ||||||
|  | /* Items (ob_size*tp_itemsize) are found at the end of an instance's memory */ | ||||||
|  | #define Py_TPFLAGS_ITEMS_AT_END (1UL << 23) | ||||||
|  | 
 | ||||||
| /* These flags are used to determine if a type is a subclass. */ | /* These flags are used to determine if a type is a subclass. */ | ||||||
| #define Py_TPFLAGS_LONG_SUBCLASS        (1UL << 24) | #define Py_TPFLAGS_LONG_SUBCLASS        (1UL << 24) | ||||||
| #define Py_TPFLAGS_LIST_SUBCLASS        (1UL << 25) | #define Py_TPFLAGS_LIST_SUBCLASS        (1UL << 25) | ||||||
|  |  | ||||||
|  | @ -765,4 +765,15 @@ extern char * _getpty(int *, int, mode_t, int); | ||||||
| #undef __bool__ | #undef __bool__ | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | // Make sure we have maximum alignment, even if the current compiler
 | ||||||
|  | // does not support max_align_t. Note that:
 | ||||||
|  | // - Autoconf reports alignment of unknown types to 0.
 | ||||||
|  | // - 'long double' has maximum alignment on *most* platforms,
 | ||||||
|  | //   looks like the best we can do for pre-C11 compilers.
 | ||||||
|  | // - The value is tested, see test_alignof_max_align_t
 | ||||||
|  | #if !defined(ALIGNOF_MAX_ALIGN_T) || ALIGNOF_MAX_ALIGN_T == 0 | ||||||
|  | #   undef ALIGNOF_MAX_ALIGN_T | ||||||
|  | #   define ALIGNOF_MAX_ALIGN_T _Alignof(long double) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #endif /* Py_PYPORT_H */ | #endif /* Py_PYPORT_H */ | ||||||
|  |  | ||||||
|  | @ -16,11 +16,13 @@ | ||||||
| import unittest | import unittest | ||||||
| import warnings | import warnings | ||||||
| import weakref | import weakref | ||||||
|  | import operator | ||||||
| from test import support | from test import support | ||||||
| from test.support import MISSING_C_DOCSTRINGS | from test.support import MISSING_C_DOCSTRINGS | ||||||
| from test.support import import_helper | from test.support import import_helper | ||||||
| from test.support import threading_helper | from test.support import threading_helper | ||||||
| from test.support import warnings_helper | from test.support import warnings_helper | ||||||
|  | from test.support import requires_limited_api | ||||||
| from test.support.script_helper import assert_python_failure, assert_python_ok, run_python_until_end | from test.support.script_helper import assert_python_failure, assert_python_ok, run_python_until_end | ||||||
| try: | try: | ||||||
|     import _posixsubprocess |     import _posixsubprocess | ||||||
|  | @ -770,7 +772,6 @@ def meth(self): | ||||||
|         MutableBase.meth = lambda self: 'changed' |         MutableBase.meth = lambda self: 'changed' | ||||||
|         self.assertEqual(instance.meth(), 'changed') |         self.assertEqual(instance.meth(), 'changed') | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def test_pynumber_tobase(self): |     def test_pynumber_tobase(self): | ||||||
|         from _testcapi import pynumber_tobase |         from _testcapi import pynumber_tobase | ||||||
|         small_number = 123 |         small_number = 123 | ||||||
|  | @ -1072,6 +1073,161 @@ class Data(_testcapi.ObjExtraData): | ||||||
|         self.assertIsNone(d.extra) |         self.assertIsNone(d.extra) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @requires_limited_api | ||||||
|  | class TestHeapTypeRelative(unittest.TestCase): | ||||||
|  |     """Test API for extending opaque types (PEP 697)""" | ||||||
|  | 
 | ||||||
|  |     @requires_limited_api | ||||||
|  |     def test_heaptype_relative_sizes(self): | ||||||
|  |         # Test subclassing using "relative" basicsize, see PEP 697 | ||||||
|  |         def check(extra_base_size, extra_size): | ||||||
|  |             Base, Sub, instance, data_ptr, data_offset, data_size = ( | ||||||
|  |                 _testcapi.make_sized_heaptypes( | ||||||
|  |                     extra_base_size, -extra_size)) | ||||||
|  | 
 | ||||||
|  |             # no alignment shenanigans when inheriting directly | ||||||
|  |             if extra_size == 0: | ||||||
|  |                 self.assertEqual(Base.__basicsize__, Sub.__basicsize__) | ||||||
|  |                 self.assertEqual(data_size, 0) | ||||||
|  | 
 | ||||||
|  |             else: | ||||||
|  |                 # The following offsets should be in increasing order: | ||||||
|  |                 offsets = [ | ||||||
|  |                     (0, 'start of object'), | ||||||
|  |                     (Base.__basicsize__, 'end of base data'), | ||||||
|  |                     (data_offset, 'subclass data'), | ||||||
|  |                     (data_offset + extra_size, 'end of requested subcls data'), | ||||||
|  |                     (data_offset + data_size, 'end of reserved subcls data'), | ||||||
|  |                     (Sub.__basicsize__, 'end of object'), | ||||||
|  |                 ] | ||||||
|  |                 ordered_offsets = sorted(offsets, key=operator.itemgetter(0)) | ||||||
|  |                 self.assertEqual( | ||||||
|  |                     offsets, ordered_offsets, | ||||||
|  |                     msg=f'Offsets not in expected order, got: {ordered_offsets}') | ||||||
|  | 
 | ||||||
|  |                 # end of reserved subcls data == end of object | ||||||
|  |                 self.assertEqual(Sub.__basicsize__, data_offset + data_size) | ||||||
|  | 
 | ||||||
|  |                 # we don't reserve (requested + alignment) or more data | ||||||
|  |                 self.assertLess(data_size - extra_size, | ||||||
|  |                                 _testcapi.ALIGNOF_MAX_ALIGN_T) | ||||||
|  | 
 | ||||||
|  |             # The offsets/sizes we calculated should be aligned. | ||||||
|  |             self.assertEqual(data_offset % _testcapi.ALIGNOF_MAX_ALIGN_T, 0) | ||||||
|  |             self.assertEqual(data_size % _testcapi.ALIGNOF_MAX_ALIGN_T, 0) | ||||||
|  | 
 | ||||||
|  |         sizes = sorted({0, 1, 2, 3, 4, 7, 8, 123, | ||||||
|  |                         object.__basicsize__, | ||||||
|  |                         object.__basicsize__-1, | ||||||
|  |                         object.__basicsize__+1}) | ||||||
|  |         for extra_base_size in sizes: | ||||||
|  |             for extra_size in sizes: | ||||||
|  |                 args = dict(extra_base_size=extra_base_size, | ||||||
|  |                             extra_size=extra_size) | ||||||
|  |                 with self.subTest(**args): | ||||||
|  |                     check(**args) | ||||||
|  | 
 | ||||||
|  |     def test_HeapCCollection(self): | ||||||
|  |         """Make sure HeapCCollection works properly by itself""" | ||||||
|  |         collection = _testcapi.HeapCCollection(1, 2, 3) | ||||||
|  |         self.assertEqual(list(collection), [1, 2, 3]) | ||||||
|  | 
 | ||||||
|  |     def test_heaptype_inherit_itemsize(self): | ||||||
|  |         """Test HeapCCollection subclasses work properly""" | ||||||
|  |         sizes = sorted({0, 1, 2, 3, 4, 7, 8, 123, | ||||||
|  |                         object.__basicsize__, | ||||||
|  |                         object.__basicsize__-1, | ||||||
|  |                         object.__basicsize__+1}) | ||||||
|  |         for extra_size in sizes: | ||||||
|  |             with self.subTest(extra_size=extra_size): | ||||||
|  |                 Sub = _testcapi.subclass_var_heaptype( | ||||||
|  |                     _testcapi.HeapCCollection, -extra_size, 0, 0) | ||||||
|  |                 collection = Sub(1, 2, 3) | ||||||
|  |                 collection.set_data_to_3s() | ||||||
|  | 
 | ||||||
|  |                 self.assertEqual(list(collection), [1, 2, 3]) | ||||||
|  |                 mem = collection.get_data() | ||||||
|  |                 self.assertGreaterEqual(len(mem), extra_size) | ||||||
|  |                 self.assertTrue(set(mem) <= {3}, f'got {mem!r}') | ||||||
|  | 
 | ||||||
|  |     def test_heaptype_invalid_inheritance(self): | ||||||
|  |         with self.assertRaises(SystemError, | ||||||
|  |                                msg="Cannot extend variable-size class without " | ||||||
|  |                                + "Py_TPFLAGS_ITEMS_AT_END"): | ||||||
|  |             _testcapi.subclass_heaptype(int, -8, 0) | ||||||
|  | 
 | ||||||
|  |     def test_heaptype_relative_members(self): | ||||||
|  |         """Test HeapCCollection subclasses work properly""" | ||||||
|  |         sizes = sorted({0, 1, 2, 3, 4, 7, 8, 123, | ||||||
|  |                         object.__basicsize__, | ||||||
|  |                         object.__basicsize__-1, | ||||||
|  |                         object.__basicsize__+1}) | ||||||
|  |         for extra_base_size in sizes: | ||||||
|  |             for extra_size in sizes: | ||||||
|  |                 for offset in sizes: | ||||||
|  |                     with self.subTest(extra_base_size=extra_base_size, extra_size=extra_size, offset=offset): | ||||||
|  |                         if offset < extra_size: | ||||||
|  |                             Sub = _testcapi.make_heaptype_with_member( | ||||||
|  |                                 extra_base_size, -extra_size, offset, True) | ||||||
|  |                             Base = Sub.mro()[1] | ||||||
|  |                             instance = Sub() | ||||||
|  |                             self.assertEqual(instance.memb, instance.get_memb()) | ||||||
|  |                             instance.set_memb(13) | ||||||
|  |                             self.assertEqual(instance.memb, instance.get_memb()) | ||||||
|  |                             self.assertEqual(instance.get_memb(), 13) | ||||||
|  |                             instance.memb = 14 | ||||||
|  |                             self.assertEqual(instance.memb, instance.get_memb()) | ||||||
|  |                             self.assertEqual(instance.get_memb(), 14) | ||||||
|  |                             self.assertGreaterEqual(instance.get_memb_offset(), Base.__basicsize__) | ||||||
|  |                             self.assertLess(instance.get_memb_offset(), Sub.__basicsize__) | ||||||
|  |                             with self.assertRaises(SystemError): | ||||||
|  |                                 instance.get_memb_relative() | ||||||
|  |                             with self.assertRaises(SystemError): | ||||||
|  |                                 instance.set_memb_relative(0) | ||||||
|  |                         else: | ||||||
|  |                             with self.assertRaises(SystemError): | ||||||
|  |                                 Sub = _testcapi.make_heaptype_with_member( | ||||||
|  |                                     extra_base_size, -extra_size, offset, True) | ||||||
|  |                         with self.assertRaises(SystemError): | ||||||
|  |                             Sub = _testcapi.make_heaptype_with_member( | ||||||
|  |                                 extra_base_size, extra_size, offset, True) | ||||||
|  |                 with self.subTest(extra_base_size=extra_base_size, extra_size=extra_size): | ||||||
|  |                     with self.assertRaises(SystemError): | ||||||
|  |                         Sub = _testcapi.make_heaptype_with_member( | ||||||
|  |                             extra_base_size, -extra_size, -1, True) | ||||||
|  | 
 | ||||||
|  |     def test_heaptype_relative_members_errors(self): | ||||||
|  |         with self.assertRaisesRegex( | ||||||
|  |                 SystemError, | ||||||
|  |                 r"With Py_RELATIVE_OFFSET, basicsize must be negative"): | ||||||
|  |             _testcapi.make_heaptype_with_member(0, 1234, 0, True) | ||||||
|  |         with self.assertRaisesRegex( | ||||||
|  |                 SystemError, r"Member offset out of range \(0\.\.-basicsize\)"): | ||||||
|  |             _testcapi.make_heaptype_with_member(0, -8, 1234, True) | ||||||
|  |         with self.assertRaisesRegex( | ||||||
|  |                 SystemError, r"Member offset out of range \(0\.\.-basicsize\)"): | ||||||
|  |             _testcapi.make_heaptype_with_member(0, -8, -1, True) | ||||||
|  | 
 | ||||||
|  |         Sub = _testcapi.make_heaptype_with_member(0, -8, 0, True) | ||||||
|  |         instance = Sub() | ||||||
|  |         with self.assertRaisesRegex( | ||||||
|  |                 SystemError, r"PyMember_GetOne used with Py_RELATIVE_OFFSET"): | ||||||
|  |             instance.get_memb_relative() | ||||||
|  |         with self.assertRaisesRegex( | ||||||
|  |                 SystemError, r"PyMember_SetOne used with Py_RELATIVE_OFFSET"): | ||||||
|  |             instance.set_memb_relative(0) | ||||||
|  | 
 | ||||||
|  |     def test_pyobject_getitemdata_error(self): | ||||||
|  |         """Test PyObject_GetItemData fails on unsupported types""" | ||||||
|  |         with self.assertRaises(TypeError): | ||||||
|  |             # None is not variable-length | ||||||
|  |             _testcapi.pyobject_getitemdata(None) | ||||||
|  |         with self.assertRaises(TypeError): | ||||||
|  |             # int is variable-length, but doesn't have the | ||||||
|  |             # Py_TPFLAGS_ITEMS_AT_END layout (and flag) | ||||||
|  |             _testcapi.pyobject_getitemdata(0) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class TestPendingCalls(unittest.TestCase): | class TestPendingCalls(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|     def pendingcalls_submit(self, l, n): |     def pendingcalls_submit(self, l, n): | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								Lib/test/test_stable_abi_ctypes.py
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								Lib/test/test_stable_abi_ctypes.py
									
										
									
										generated
									
									
									
								
							|  | @ -529,6 +529,7 @@ SYMBOL_NAMES = ( | ||||||
|     "PyObject_GetBuffer", |     "PyObject_GetBuffer", | ||||||
|     "PyObject_GetItem", |     "PyObject_GetItem", | ||||||
|     "PyObject_GetIter", |     "PyObject_GetIter", | ||||||
|  |     "PyObject_GetTypeData", | ||||||
|     "PyObject_HasAttr", |     "PyObject_HasAttr", | ||||||
|     "PyObject_HasAttrString", |     "PyObject_HasAttrString", | ||||||
|     "PyObject_Hash", |     "PyObject_Hash", | ||||||
|  | @ -679,6 +680,7 @@ SYMBOL_NAMES = ( | ||||||
|     "PyType_GetName", |     "PyType_GetName", | ||||||
|     "PyType_GetQualName", |     "PyType_GetQualName", | ||||||
|     "PyType_GetSlot", |     "PyType_GetSlot", | ||||||
|  |     "PyType_GetTypeDataSize", | ||||||
|     "PyType_IsSubtype", |     "PyType_IsSubtype", | ||||||
|     "PyType_Modified", |     "PyType_Modified", | ||||||
|     "PyType_Ready", |     "PyType_Ready", | ||||||
|  |  | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | Added C API for extending types whose instance memory layout is opaque: | ||||||
|  | :c:member:`PyType_Spec.basicsize` can now be zero or negative, | ||||||
|  | :c:func:`PyObject_GetTypeData` can be used to get subclass-specific data, | ||||||
|  | and :c:macro:`Py_TPFLAGS_ITEMS_AT_END` can be used to safely extend | ||||||
|  | variable-size objects. See :pep:`697` for details. | ||||||
|  | @ -2397,3 +2397,12 @@ | ||||||
|     added = '3.12'  # Before 3.12, available in "structmember.h" w/o Py_ prefix |     added = '3.12'  # Before 3.12, available in "structmember.h" w/o Py_ prefix | ||||||
| [const.Py_AUDIT_READ] | [const.Py_AUDIT_READ] | ||||||
|     added = '3.12'  # Before 3.12, available in "structmember.h" |     added = '3.12'  # Before 3.12, available in "structmember.h" | ||||||
|  | 
 | ||||||
|  | [function.PyObject_GetTypeData] | ||||||
|  |     added = '3.12' | ||||||
|  | [function.PyType_GetTypeDataSize] | ||||||
|  |     added = '3.12' | ||||||
|  | [const.Py_RELATIVE_OFFSET] | ||||||
|  |     added = '3.12' | ||||||
|  | [const.Py_TPFLAGS_ITEMS_AT_END] | ||||||
|  |     added = '3.12' | ||||||
|  |  | ||||||
|  | @ -169,7 +169,7 @@ | ||||||
| @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c | @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c | ||||||
| @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c | @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c | ||||||
| @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c | @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c | ||||||
| @MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/pyos.c _testcapi/immortal.c | @MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c | ||||||
| @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c | @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c | ||||||
| 
 | 
 | ||||||
| # Some testing modules MUST be built as shared libraries. | # Some testing modules MUST be built as shared libraries. | ||||||
|  |  | ||||||
|  | @ -371,7 +371,6 @@ create_type_from_repeated_slots(PyObject *self, PyObject *variant_obj) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| static PyObject * | static PyObject * | ||||||
| make_immutable_type_with_base(PyObject *self, PyObject *base) | make_immutable_type_with_base(PyObject *self, PyObject *base) | ||||||
| { | { | ||||||
|  | @ -399,6 +398,17 @@ make_type_with_base(PyObject *self, PyObject *base) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | static PyObject * | ||||||
|  | pyobject_getitemdata(PyObject *self, PyObject *o) | ||||||
|  | { | ||||||
|  |     void *pointer = PyObject_GetItemData(o); | ||||||
|  |     if (pointer == NULL) { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     return PyLong_FromVoidPtr(pointer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| static PyMethodDef TestMethods[] = { | static PyMethodDef TestMethods[] = { | ||||||
|     {"pytype_fromspec_meta",    pytype_fromspec_meta,            METH_O}, |     {"pytype_fromspec_meta",    pytype_fromspec_meta,            METH_O}, | ||||||
|     {"test_type_from_ephemeral_spec", test_type_from_ephemeral_spec, METH_NOARGS}, |     {"test_type_from_ephemeral_spec", test_type_from_ephemeral_spec, METH_NOARGS}, | ||||||
|  | @ -411,6 +421,7 @@ static PyMethodDef TestMethods[] = { | ||||||
|      METH_NOARGS}, |      METH_NOARGS}, | ||||||
|     {"make_immutable_type_with_base", make_immutable_type_with_base, METH_O}, |     {"make_immutable_type_with_base", make_immutable_type_with_base, METH_O}, | ||||||
|     {"make_type_with_base", make_type_with_base, METH_O}, |     {"make_type_with_base", make_type_with_base, METH_O}, | ||||||
|  |     {"pyobject_getitemdata", pyobject_getitemdata, METH_O}, | ||||||
|     {NULL}, |     {NULL}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -987,6 +998,113 @@ static PyType_Spec HeapCTypeSetattr_spec = { | ||||||
|     HeapCTypeSetattr_slots |     HeapCTypeSetattr_slots | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | PyDoc_STRVAR(HeapCCollection_doc, | ||||||
|  | "Tuple-like heap type that uses PyObject_GetItemData for items."); | ||||||
|  | 
 | ||||||
|  | static PyObject* | ||||||
|  | HeapCCollection_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) | ||||||
|  | { | ||||||
|  |     PyObject *self = NULL; | ||||||
|  |     PyObject *result = NULL; | ||||||
|  | 
 | ||||||
|  |     Py_ssize_t size = PyTuple_GET_SIZE(args); | ||||||
|  |     self = subtype->tp_alloc(subtype, size); | ||||||
|  |     if (!self) { | ||||||
|  |         goto finally; | ||||||
|  |     } | ||||||
|  |     PyObject **data = PyObject_GetItemData(self); | ||||||
|  |     if (!data) { | ||||||
|  |         goto finally; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (Py_ssize_t i = 0; i < size; i++) { | ||||||
|  |         data[i] = Py_NewRef(PyTuple_GET_ITEM(args, i)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     result = self; | ||||||
|  |     self = NULL; | ||||||
|  |   finally: | ||||||
|  |     Py_XDECREF(self); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static Py_ssize_t | ||||||
|  | HeapCCollection_length(PyVarObject *self) | ||||||
|  | { | ||||||
|  |     return Py_SIZE(self); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyObject* | ||||||
|  | HeapCCollection_item(PyObject *self, Py_ssize_t i) | ||||||
|  | { | ||||||
|  |     if (i < 0 || i >= Py_SIZE(self)) { | ||||||
|  |         return PyErr_Format(PyExc_IndexError, "index %zd out of range", i); | ||||||
|  |     } | ||||||
|  |     PyObject **data = PyObject_GetItemData(self); | ||||||
|  |     if (!data) { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     return Py_NewRef(data[i]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | HeapCCollection_traverse(PyObject *self, visitproc visit, void *arg) | ||||||
|  | { | ||||||
|  |     PyObject **data = PyObject_GetItemData(self); | ||||||
|  |     if (!data) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     for (Py_ssize_t i = 0; i < Py_SIZE(self); i++) { | ||||||
|  |         Py_VISIT(data[i]); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | HeapCCollection_clear(PyObject *self) | ||||||
|  | { | ||||||
|  |     PyObject **data = PyObject_GetItemData(self); | ||||||
|  |     if (!data) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     Py_ssize_t size = Py_SIZE(self); | ||||||
|  |     Py_SET_SIZE(self, 0); | ||||||
|  |     for (Py_ssize_t i = 0; i < size; i++) { | ||||||
|  |         Py_CLEAR(data[i]); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | HeapCCollection_dealloc(PyObject *self) | ||||||
|  | { | ||||||
|  |     PyTypeObject *tp = Py_TYPE(self); | ||||||
|  |     HeapCCollection_clear(self); | ||||||
|  |     PyObject_GC_UnTrack(self); | ||||||
|  |     tp->tp_free(self); | ||||||
|  |     Py_DECREF(tp); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyType_Slot HeapCCollection_slots[] = { | ||||||
|  |     {Py_tp_new, HeapCCollection_new}, | ||||||
|  |     {Py_sq_length, HeapCCollection_length}, | ||||||
|  |     {Py_sq_item, HeapCCollection_item}, | ||||||
|  |     {Py_tp_traverse, HeapCCollection_traverse}, | ||||||
|  |     {Py_tp_clear, HeapCCollection_clear}, | ||||||
|  |     {Py_tp_dealloc, HeapCCollection_dealloc}, | ||||||
|  |     {Py_tp_doc, (void *)HeapCCollection_doc}, | ||||||
|  |     {0, 0}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static PyType_Spec HeapCCollection_spec = { | ||||||
|  |     .name = "_testcapi.HeapCCollection", | ||||||
|  |     .basicsize = sizeof(PyVarObject), | ||||||
|  |     .itemsize = sizeof(PyObject*), | ||||||
|  |     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | | ||||||
|  |               Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_ITEMS_AT_END), | ||||||
|  |     .slots = HeapCCollection_slots, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| int | int | ||||||
| _PyTestCapi_Init_Heaptype(PyObject *m) { | _PyTestCapi_Init_Heaptype(PyObject *m) { | ||||||
|     _testcapimodule = PyModule_GetDef(m); |     _testcapimodule = PyModule_GetDef(m); | ||||||
|  | @ -1110,5 +1228,16 @@ _PyTestCapi_Init_Heaptype(PyObject *m) { | ||||||
|     } |     } | ||||||
|     PyModule_AddObject(m, "HeapCTypeMetaclassCustomNew", HeapCTypeMetaclassCustomNew); |     PyModule_AddObject(m, "HeapCTypeMetaclassCustomNew", HeapCTypeMetaclassCustomNew); | ||||||
| 
 | 
 | ||||||
|  |     PyObject *HeapCCollection = PyType_FromMetaclass( | ||||||
|  |         NULL, m, &HeapCCollection_spec, NULL); | ||||||
|  |     if (HeapCCollection == NULL) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     int rc = PyModule_AddType(m, (PyTypeObject *)HeapCCollection); | ||||||
|  |     Py_DECREF(HeapCCollection); | ||||||
|  |     if (rc < 0) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										343
									
								
								Modules/_testcapi/heaptype_relative.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								Modules/_testcapi/heaptype_relative.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,343 @@ | ||||||
|  | #define Py_LIMITED_API 0x030c0000 // 3.12
 | ||||||
|  | #include "parts.h" | ||||||
|  | #include <stddef.h>               // max_align_t | ||||||
|  | #include <string.h>               // memset | ||||||
|  | 
 | ||||||
|  | #ifdef LIMITED_API_AVAILABLE | ||||||
|  | 
 | ||||||
|  | static PyType_Slot empty_slots[] = { | ||||||
|  |     {0, NULL}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | make_sized_heaptypes(PyObject *module, PyObject *args) | ||||||
|  | { | ||||||
|  |     PyObject *base = NULL; | ||||||
|  |     PyObject *sub = NULL; | ||||||
|  |     PyObject *instance = NULL; | ||||||
|  |     PyObject *result = NULL; | ||||||
|  | 
 | ||||||
|  |     int extra_base_size, basicsize; | ||||||
|  | 
 | ||||||
|  |     int r = PyArg_ParseTuple(args, "ii", &extra_base_size, &basicsize); | ||||||
|  |     if (!r) { | ||||||
|  |         goto finally; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     PyType_Spec base_spec = { | ||||||
|  |         .name = "_testcapi.Base", | ||||||
|  |         .basicsize = sizeof(PyObject) + extra_base_size, | ||||||
|  |         .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, | ||||||
|  |         .slots = empty_slots, | ||||||
|  |     }; | ||||||
|  |     PyType_Spec sub_spec = { | ||||||
|  |         .name = "_testcapi.Sub", | ||||||
|  |         .basicsize = basicsize, | ||||||
|  |         .flags = Py_TPFLAGS_DEFAULT, | ||||||
|  |         .slots = empty_slots, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     base = PyType_FromMetaclass(NULL, module, &base_spec, NULL); | ||||||
|  |     if (!base) { | ||||||
|  |         goto finally; | ||||||
|  |     } | ||||||
|  |     sub = PyType_FromMetaclass(NULL, module, &sub_spec, base); | ||||||
|  |     if (!sub) { | ||||||
|  |         goto finally; | ||||||
|  |     } | ||||||
|  |     instance = PyObject_CallNoArgs(sub); | ||||||
|  |     if (!instance) { | ||||||
|  |         goto finally; | ||||||
|  |     } | ||||||
|  |     char *data_ptr = PyObject_GetTypeData(instance, (PyTypeObject *)sub); | ||||||
|  |     if (!data_ptr) { | ||||||
|  |         goto finally; | ||||||
|  |     } | ||||||
|  |     Py_ssize_t data_size = PyType_GetTypeDataSize((PyTypeObject *)sub); | ||||||
|  |     if (data_size < 0) { | ||||||
|  |         goto finally; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     result = Py_BuildValue("OOOKnn", base, sub, instance, | ||||||
|  |                            (unsigned long long)data_ptr, | ||||||
|  |                            (Py_ssize_t)(data_ptr - (char*)instance), | ||||||
|  |                            data_size); | ||||||
|  |   finally: | ||||||
|  |     Py_XDECREF(base); | ||||||
|  |     Py_XDECREF(sub); | ||||||
|  |     Py_XDECREF(instance); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | var_heaptype_set_data_to_3s( | ||||||
|  |     PyObject *self, PyTypeObject *defining_class, | ||||||
|  |     PyObject **args, Py_ssize_t nargs, PyObject *kwnames) | ||||||
|  | { | ||||||
|  |     void *data_ptr = PyObject_GetTypeData(self, defining_class); | ||||||
|  |     if (!data_ptr) { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     Py_ssize_t data_size = PyType_GetTypeDataSize(defining_class); | ||||||
|  |     if (data_size < 0) { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     memset(data_ptr, 3, data_size); | ||||||
|  |     Py_RETURN_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | var_heaptype_get_data(PyObject *self, PyTypeObject *defining_class, | ||||||
|  |                       PyObject **args, Py_ssize_t nargs, PyObject *kwnames) | ||||||
|  | { | ||||||
|  |     void *data_ptr = PyObject_GetTypeData(self, defining_class); | ||||||
|  |     if (!data_ptr) { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     Py_ssize_t data_size = PyType_GetTypeDataSize(defining_class); | ||||||
|  |     if (data_size < 0) { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     return PyBytes_FromStringAndSize(data_ptr, data_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyMethodDef var_heaptype_methods[] = { | ||||||
|  |     {"set_data_to_3s", _PyCFunction_CAST(var_heaptype_set_data_to_3s), | ||||||
|  |         METH_METHOD | METH_FASTCALL | METH_KEYWORDS}, | ||||||
|  |     {"get_data", _PyCFunction_CAST(var_heaptype_get_data), | ||||||
|  |         METH_METHOD | METH_FASTCALL | METH_KEYWORDS}, | ||||||
|  |     {NULL}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | subclass_var_heaptype(PyObject *module, PyObject *args) | ||||||
|  | { | ||||||
|  |     PyObject *result = NULL; | ||||||
|  | 
 | ||||||
|  |     PyObject *base; // borrowed from args
 | ||||||
|  |     int basicsize, itemsize; | ||||||
|  |     long pfunc; | ||||||
|  | 
 | ||||||
|  |     int r = PyArg_ParseTuple(args, "Oiil", &base, &basicsize, &itemsize, &pfunc); | ||||||
|  |     if (!r) { | ||||||
|  |         goto finally; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     PyType_Slot slots[] = { | ||||||
|  |         {Py_tp_methods, var_heaptype_methods}, | ||||||
|  |         {0, NULL}, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     PyType_Spec sub_spec = { | ||||||
|  |         .name = "_testcapi.Sub", | ||||||
|  |         .basicsize = basicsize, | ||||||
|  |         .itemsize = itemsize, | ||||||
|  |         .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_ITEMS_AT_END, | ||||||
|  |         .slots = slots, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     result = PyType_FromMetaclass(NULL, module, &sub_spec, base); | ||||||
|  |   finally: | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | subclass_heaptype(PyObject *module, PyObject *args) | ||||||
|  | { | ||||||
|  |     PyObject *result = NULL; | ||||||
|  | 
 | ||||||
|  |     PyObject *base; // borrowed from args
 | ||||||
|  |     int basicsize, itemsize; | ||||||
|  | 
 | ||||||
|  |     int r = PyArg_ParseTuple(args, "Oii", &base, &basicsize, &itemsize); | ||||||
|  |     if (!r) { | ||||||
|  |         goto finally; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     PyType_Slot slots[] = { | ||||||
|  |         {Py_tp_methods, var_heaptype_methods}, | ||||||
|  |         {0, NULL}, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     PyType_Spec sub_spec = { | ||||||
|  |         .name = "_testcapi.Sub", | ||||||
|  |         .basicsize = basicsize, | ||||||
|  |         .itemsize = itemsize, | ||||||
|  |         .flags = Py_TPFLAGS_DEFAULT, | ||||||
|  |         .slots = slots, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     result = PyType_FromMetaclass(NULL, module, &sub_spec, base); | ||||||
|  |   finally: | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyMemberDef * | ||||||
|  | heaptype_with_member_extract_and_check_memb(PyObject *self) | ||||||
|  | { | ||||||
|  |     PyMemberDef *def = PyType_GetSlot(Py_TYPE(self), Py_tp_members); | ||||||
|  |     if (!def) { | ||||||
|  |         if (!PyErr_Occurred()) { | ||||||
|  |             PyErr_SetString(PyExc_ValueError, "tp_members is NULL"); | ||||||
|  |         } | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     if (!def[0].name) { | ||||||
|  |         PyErr_SetString(PyExc_ValueError, "tp_members[0] is NULL"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     if (def[1].name) { | ||||||
|  |         PyErr_SetString(PyExc_ValueError, "tp_members[1] is not NULL"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     if (strcmp(def[0].name, "memb")) { | ||||||
|  |         PyErr_SetString(PyExc_ValueError, "tp_members[0] is not for `memb`"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     if (def[0].flags) { | ||||||
|  |         PyErr_SetString(PyExc_ValueError, "tp_members[0] has flags set"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     return def; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | heaptype_with_member_get_memb(PyObject *self, PyObject *Py_UNUSED(ignored)) | ||||||
|  | { | ||||||
|  |     PyMemberDef *def = heaptype_with_member_extract_and_check_memb(self); | ||||||
|  |     return PyMember_GetOne((const char *)self, def); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | heaptype_with_member_set_memb(PyObject *self, PyObject *value) | ||||||
|  | { | ||||||
|  |     PyMemberDef *def = heaptype_with_member_extract_and_check_memb(self); | ||||||
|  |     int r = PyMember_SetOne((char *)self, def, value); | ||||||
|  |     if (r < 0) { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     Py_RETURN_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | get_memb_offset(PyObject *self, PyObject *Py_UNUSED(ignored)) | ||||||
|  | { | ||||||
|  |     PyMemberDef *def = heaptype_with_member_extract_and_check_memb(self); | ||||||
|  |     return PyLong_FromSsize_t(def->offset); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | heaptype_with_member_get_memb_relative(PyObject *self, PyObject *Py_UNUSED(ignored)) | ||||||
|  | { | ||||||
|  |     PyMemberDef def = {"memb", Py_T_BYTE, sizeof(PyObject), Py_RELATIVE_OFFSET}; | ||||||
|  |     return PyMember_GetOne((const char *)self, &def); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | heaptype_with_member_set_memb_relative(PyObject *self, PyObject *value) | ||||||
|  | { | ||||||
|  |     PyMemberDef def = {"memb", Py_T_BYTE, sizeof(PyObject), Py_RELATIVE_OFFSET}; | ||||||
|  |     int r = PyMember_SetOne((char *)self, &def, value); | ||||||
|  |     if (r < 0) { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     Py_RETURN_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyMethodDef heaptype_with_member_methods[] = { | ||||||
|  |     {"get_memb", heaptype_with_member_get_memb, METH_NOARGS}, | ||||||
|  |     {"set_memb", heaptype_with_member_set_memb, METH_O}, | ||||||
|  |     {"get_memb_offset", get_memb_offset, METH_NOARGS}, | ||||||
|  |     {"get_memb_relative", heaptype_with_member_get_memb_relative, METH_NOARGS}, | ||||||
|  |     {"set_memb_relative", heaptype_with_member_set_memb_relative, METH_O}, | ||||||
|  |     {NULL}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | make_heaptype_with_member(PyObject *module, PyObject *args) | ||||||
|  | { | ||||||
|  |     PyObject *base = NULL; | ||||||
|  |     PyObject *result = NULL; | ||||||
|  | 
 | ||||||
|  |     int extra_base_size, basicsize, offset, add_flag; | ||||||
|  | 
 | ||||||
|  |     int r = PyArg_ParseTuple(args, "iiip", &extra_base_size, &basicsize, &offset, &add_flag); | ||||||
|  |     if (!r) { | ||||||
|  |         goto finally; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     PyType_Spec base_spec = { | ||||||
|  |         .name = "_testcapi.Base", | ||||||
|  |         .basicsize = sizeof(PyObject) + extra_base_size, | ||||||
|  |         .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, | ||||||
|  |         .slots = empty_slots, | ||||||
|  |     }; | ||||||
|  |     base = PyType_FromMetaclass(NULL, module, &base_spec, NULL); | ||||||
|  |     if (!base) { | ||||||
|  |         goto finally; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     PyMemberDef members[] = { | ||||||
|  |         {"memb", Py_T_BYTE, offset, add_flag ? Py_RELATIVE_OFFSET : 0}, | ||||||
|  |         {0}, | ||||||
|  |     }; | ||||||
|  |     PyType_Slot slots[] = { | ||||||
|  |         {Py_tp_members, members}, | ||||||
|  |         {Py_tp_methods, heaptype_with_member_methods}, | ||||||
|  |         {0, NULL}, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     PyType_Spec sub_spec = { | ||||||
|  |         .name = "_testcapi.Sub", | ||||||
|  |         .basicsize = basicsize, | ||||||
|  |         .flags = Py_TPFLAGS_DEFAULT, | ||||||
|  |         .slots = slots, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     result = PyType_FromMetaclass(NULL, module, &sub_spec, base); | ||||||
|  |   finally: | ||||||
|  |     Py_XDECREF(base); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | test_alignof_max_align_t(PyObject *module, PyObject *Py_UNUSED(ignored)) | ||||||
|  | { | ||||||
|  |     // We define ALIGNOF_MAX_ALIGN_T even if the compiler doesn't support
 | ||||||
|  |     // max_align_t. Double-check that it's correct.
 | ||||||
|  |     assert(ALIGNOF_MAX_ALIGN_T > 0); | ||||||
|  |     assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(long long)); | ||||||
|  |     assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(long double)); | ||||||
|  |     assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(void*)); | ||||||
|  |     assert(ALIGNOF_MAX_ALIGN_T >= _Alignof(void (*)(void))); | ||||||
|  | 
 | ||||||
|  |     // Ensure it's a power of two
 | ||||||
|  |     assert((ALIGNOF_MAX_ALIGN_T & (ALIGNOF_MAX_ALIGN_T - 1)) == 0); | ||||||
|  | 
 | ||||||
|  |     Py_RETURN_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyMethodDef TestMethods[] = { | ||||||
|  |     {"make_sized_heaptypes", make_sized_heaptypes, METH_VARARGS}, | ||||||
|  |     {"subclass_var_heaptype", subclass_var_heaptype, METH_VARARGS}, | ||||||
|  |     {"subclass_heaptype", subclass_heaptype, METH_VARARGS}, | ||||||
|  |     {"make_heaptype_with_member", make_heaptype_with_member, METH_VARARGS}, | ||||||
|  |     {"test_alignof_max_align_t", test_alignof_max_align_t, METH_NOARGS}, | ||||||
|  |     {NULL}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | int | ||||||
|  | _PyTestCapi_Init_HeaptypeRelative(PyObject *m) { | ||||||
|  |     if (PyModule_AddFunctions(m, TestMethods) < 0) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (PyModule_AddIntMacro(m, ALIGNOF_MAX_ALIGN_T) < 0) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // LIMITED_API_AVAILABLE
 | ||||||
|  | @ -43,6 +43,7 @@ int _PyTestCapi_Init_Immortal(PyObject *module); | ||||||
| 
 | 
 | ||||||
| #ifdef LIMITED_API_AVAILABLE | #ifdef LIMITED_API_AVAILABLE | ||||||
| int _PyTestCapi_Init_VectorcallLimited(PyObject *module); | int _PyTestCapi_Init_VectorcallLimited(PyObject *module); | ||||||
|  | int _PyTestCapi_Init_HeaptypeRelative(PyObject *module); | ||||||
| #endif // LIMITED_API_AVAILABLE
 | #endif // LIMITED_API_AVAILABLE
 | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -4324,6 +4324,9 @@ PyInit__testcapi(void) | ||||||
|     if (_PyTestCapi_Init_VectorcallLimited(m) < 0) { |     if (_PyTestCapi_Init_VectorcallLimited(m) < 0) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |     if (_PyTestCapi_Init_HeaptypeRelative(m) < 0) { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|     PyState_AddModule(m, &_testcapimodule); |     PyState_AddModule(m, &_testcapimodule); | ||||||
|  |  | ||||||
|  | @ -978,6 +978,12 @@ PyDescr_NewMember(PyTypeObject *type, PyMemberDef *member) | ||||||
| { | { | ||||||
|     PyMemberDescrObject *descr; |     PyMemberDescrObject *descr; | ||||||
| 
 | 
 | ||||||
|  |     if (member->flags & Py_RELATIVE_OFFSET) { | ||||||
|  |         PyErr_SetString( | ||||||
|  |             PyExc_SystemError, | ||||||
|  |             "PyDescr_NewMember used with Py_RELATIVE_OFFSET"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|     descr = (PyMemberDescrObject *)descr_new(&PyMemberDescr_Type, |     descr = (PyMemberDescrObject *)descr_new(&PyMemberDescr_Type, | ||||||
|                                              type, member->name); |                                              type, member->name); | ||||||
|     if (descr != NULL) |     if (descr != NULL) | ||||||
|  |  | ||||||
|  | @ -18,6 +18,7 @@ | ||||||
| #include "structmember.h"         // PyMemberDef | #include "structmember.h"         // PyMemberDef | ||||||
| 
 | 
 | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
|  | #include <stddef.h>               // ptrdiff_t | ||||||
| 
 | 
 | ||||||
| /*[clinic input]
 | /*[clinic input]
 | ||||||
| class type "PyTypeObject *" "&PyType_Type" | class type "PyTypeObject *" "&PyType_Type" | ||||||
|  | @ -1686,6 +1687,12 @@ PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||||||
| 
 | 
 | ||||||
| /* Helpers for subtyping */ | /* Helpers for subtyping */ | ||||||
| 
 | 
 | ||||||
|  | static inline PyMemberDef * | ||||||
|  | _PyHeapType_GET_MEMBERS(PyHeapTypeObject* type) | ||||||
|  | { | ||||||
|  |     return PyObject_GetItemData((PyObject *)type); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int | static int | ||||||
| traverse_slots(PyTypeObject *type, PyObject *self, visitproc visit, void *arg) | traverse_slots(PyTypeObject *type, PyObject *self, visitproc visit, void *arg) | ||||||
| { | { | ||||||
|  | @ -3873,6 +3880,15 @@ static const PySlot_Offset pyslot_offsets[] = { | ||||||
| #include "typeslots.inc" | #include "typeslots.inc" | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /* Align up to the nearest multiple of alignof(max_align_t)
 | ||||||
|  |  * (like _Py_ALIGN_UP, but for a size rather than pointer) | ||||||
|  |  */ | ||||||
|  | static Py_ssize_t | ||||||
|  | _align_up(Py_ssize_t size) | ||||||
|  | { | ||||||
|  |     return (size + ALIGNOF_MAX_ALIGN_T - 1) & ~(ALIGNOF_MAX_ALIGN_T - 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Given a PyType_FromMetaclass `bases` argument (NULL, type, or tuple of
 | /* Given a PyType_FromMetaclass `bases` argument (NULL, type, or tuple of
 | ||||||
|  * types), return a tuple of types. |  * types), return a tuple of types. | ||||||
|  */ |  */ | ||||||
|  | @ -4013,6 +4029,20 @@ _PyType_FromMetaclass_impl( | ||||||
|                     assert(memb->flags == READONLY); |                     assert(memb->flags == READONLY); | ||||||
|                     vectorcalloffset = memb->offset; |                     vectorcalloffset = memb->offset; | ||||||
|                 } |                 } | ||||||
|  |                 if (memb->flags & Py_RELATIVE_OFFSET) { | ||||||
|  |                     if (spec->basicsize > 0) { | ||||||
|  |                         PyErr_SetString( | ||||||
|  |                             PyExc_SystemError, | ||||||
|  |                             "With Py_RELATIVE_OFFSET, basicsize must be negative."); | ||||||
|  |                         goto finally; | ||||||
|  |                     } | ||||||
|  |                     if (memb->offset < 0 || memb->offset >= -spec->basicsize) { | ||||||
|  |                         PyErr_SetString( | ||||||
|  |                             PyExc_SystemError, | ||||||
|  |                             "Member offset out of range (0..-basicsize)"); | ||||||
|  |                         goto finally; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case Py_tp_doc: |         case Py_tp_doc: | ||||||
|  | @ -4154,6 +4184,32 @@ _PyType_FromMetaclass_impl( | ||||||
|     // here we just check its work
 |     // here we just check its work
 | ||||||
|     assert(_PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)); |     assert(_PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)); | ||||||
| 
 | 
 | ||||||
|  |     /* Calculate sizes */ | ||||||
|  | 
 | ||||||
|  |     Py_ssize_t basicsize = spec->basicsize; | ||||||
|  |     Py_ssize_t type_data_offset = spec->basicsize; | ||||||
|  |     if (basicsize == 0) { | ||||||
|  |         /* Inherit */ | ||||||
|  |         basicsize = base->tp_basicsize; | ||||||
|  |     } | ||||||
|  |     else if (basicsize < 0) { | ||||||
|  |         /* Extend */ | ||||||
|  |         type_data_offset = _align_up(base->tp_basicsize); | ||||||
|  |         basicsize = type_data_offset + _align_up(-spec->basicsize); | ||||||
|  | 
 | ||||||
|  |         /* Inheriting variable-sized types is limited */ | ||||||
|  |         if (base->tp_itemsize | ||||||
|  |             && !((base->tp_flags | spec->flags) & Py_TPFLAGS_ITEMS_AT_END)) | ||||||
|  |         { | ||||||
|  |             PyErr_SetString( | ||||||
|  |                 PyExc_SystemError, | ||||||
|  |                 "Cannot extend variable-size class without Py_TPFLAGS_ITEMS_AT_END."); | ||||||
|  |             goto finally; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Py_ssize_t itemsize = spec->itemsize; | ||||||
|  | 
 | ||||||
|     /* Allocate the new type
 |     /* Allocate the new type
 | ||||||
|      * |      * | ||||||
|      * Between here and PyType_Ready, we should limit: |      * Between here and PyType_Ready, we should limit: | ||||||
|  | @ -4201,8 +4257,8 @@ _PyType_FromMetaclass_impl( | ||||||
| 
 | 
 | ||||||
|     /* Copy the sizes */ |     /* Copy the sizes */ | ||||||
| 
 | 
 | ||||||
|     type->tp_basicsize = spec->basicsize; |     type->tp_basicsize = basicsize; | ||||||
|     type->tp_itemsize = spec->itemsize; |     type->tp_itemsize = itemsize; | ||||||
| 
 | 
 | ||||||
|     /* Copy all the ordinary slots */ |     /* Copy all the ordinary slots */ | ||||||
| 
 | 
 | ||||||
|  | @ -4219,6 +4275,16 @@ _PyType_FromMetaclass_impl( | ||||||
|                 size_t len = Py_TYPE(type)->tp_itemsize * nmembers; |                 size_t len = Py_TYPE(type)->tp_itemsize * nmembers; | ||||||
|                 memcpy(_PyHeapType_GET_MEMBERS(res), slot->pfunc, len); |                 memcpy(_PyHeapType_GET_MEMBERS(res), slot->pfunc, len); | ||||||
|                 type->tp_members = _PyHeapType_GET_MEMBERS(res); |                 type->tp_members = _PyHeapType_GET_MEMBERS(res); | ||||||
|  |                 PyMemberDef *memb; | ||||||
|  |                 Py_ssize_t i; | ||||||
|  |                 for (memb = _PyHeapType_GET_MEMBERS(res), i = nmembers; | ||||||
|  |                      i > 0; ++memb, --i) | ||||||
|  |                 { | ||||||
|  |                     if (memb->flags & Py_RELATIVE_OFFSET) { | ||||||
|  |                         memb->flags &= ~Py_RELATIVE_OFFSET; | ||||||
|  |                         memb->offset += type_data_offset; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|  | @ -4227,6 +4293,7 @@ _PyType_FromMetaclass_impl( | ||||||
|                 PySlot_Offset slotoffsets = pyslot_offsets[slot->slot]; |                 PySlot_Offset slotoffsets = pyslot_offsets[slot->slot]; | ||||||
|                 short slot_offset = slotoffsets.slot_offset; |                 short slot_offset = slotoffsets.slot_offset; | ||||||
|                 if (slotoffsets.subslot_offset == -1) { |                 if (slotoffsets.subslot_offset == -1) { | ||||||
|  |                     /* Set a slot in the main PyTypeObject */ | ||||||
|                     *(void**)((char*)res_start + slot_offset) = slot->pfunc; |                     *(void**)((char*)res_start + slot_offset) = slot->pfunc; | ||||||
|                 } |                 } | ||||||
|                 else { |                 else { | ||||||
|  | @ -4461,6 +4528,34 @@ PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def) | ||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void * | ||||||
|  | PyObject_GetTypeData(PyObject *obj, PyTypeObject *cls) | ||||||
|  | { | ||||||
|  |     assert(PyObject_TypeCheck(obj, cls)); | ||||||
|  |     return (char *)obj + _align_up(cls->tp_base->tp_basicsize); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Py_ssize_t | ||||||
|  | PyType_GetTypeDataSize(PyTypeObject *cls) | ||||||
|  | { | ||||||
|  |     ptrdiff_t result = cls->tp_basicsize - _align_up(cls->tp_base->tp_basicsize); | ||||||
|  |     if (result < 0) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void * | ||||||
|  | PyObject_GetItemData(PyObject *obj) | ||||||
|  | { | ||||||
|  |     if (!PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_ITEMS_AT_END)) { | ||||||
|  |         PyErr_Format(PyExc_TypeError, | ||||||
|  |                      "type '%s' does not have Py_TPFLAGS_ITEMS_AT_END", | ||||||
|  |                      Py_TYPE(obj)->tp_name); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     return (char *)obj + Py_TYPE(obj)->tp_basicsize; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| /* Internal API to look for a name through the MRO, bypassing the method cache.
 | /* Internal API to look for a name through the MRO, bypassing the method cache.
 | ||||||
|    This returns a borrowed reference, and might set an exception. |    This returns a borrowed reference, and might set an exception. | ||||||
|  | @ -5158,7 +5253,8 @@ PyTypeObject PyType_Type = { | ||||||
|     0,                                          /* tp_as_buffer */ |     0,                                          /* tp_as_buffer */ | ||||||
|     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | |     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | | ||||||
|     Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TYPE_SUBCLASS | |     Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TYPE_SUBCLASS | | ||||||
|     Py_TPFLAGS_HAVE_VECTORCALL,                 /* tp_flags */ |     Py_TPFLAGS_HAVE_VECTORCALL | | ||||||
|  |     Py_TPFLAGS_ITEMS_AT_END,                    /* tp_flags */ | ||||||
|     type_doc,                                   /* tp_doc */ |     type_doc,                                   /* tp_doc */ | ||||||
|     (traverseproc)type_traverse,                /* tp_traverse */ |     (traverseproc)type_traverse,                /* tp_traverse */ | ||||||
|     (inquiry)type_clear,                        /* tp_clear */ |     (inquiry)type_clear,                        /* tp_clear */ | ||||||
|  | @ -6572,9 +6668,14 @@ inherit_special(PyTypeObject *type, PyTypeObject *base) | ||||||
|     else if (PyType_IsSubtype(base, &PyDict_Type)) { |     else if (PyType_IsSubtype(base, &PyDict_Type)) { | ||||||
|         type->tp_flags |= Py_TPFLAGS_DICT_SUBCLASS; |         type->tp_flags |= Py_TPFLAGS_DICT_SUBCLASS; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /* Setup some inheritable flags */ | ||||||
|     if (PyType_HasFeature(base, _Py_TPFLAGS_MATCH_SELF)) { |     if (PyType_HasFeature(base, _Py_TPFLAGS_MATCH_SELF)) { | ||||||
|         type->tp_flags |= _Py_TPFLAGS_MATCH_SELF; |         type->tp_flags |= _Py_TPFLAGS_MATCH_SELF; | ||||||
|     } |     } | ||||||
|  |     if (PyType_HasFeature(base, Py_TPFLAGS_ITEMS_AT_END)) { | ||||||
|  |         type->tp_flags |= Py_TPFLAGS_ITEMS_AT_END; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int | static int | ||||||
|  |  | ||||||
|  | @ -330,6 +330,7 @@ Py_NO_ENABLE_SHARED to find out.  Also support MS_NO_COREDLL for b/w compat */ | ||||||
| #       define SIZEOF_HKEY 8 | #       define SIZEOF_HKEY 8 | ||||||
| #       define SIZEOF_SIZE_T 8 | #       define SIZEOF_SIZE_T 8 | ||||||
| #       define ALIGNOF_SIZE_T 8 | #       define ALIGNOF_SIZE_T 8 | ||||||
|  | #       define ALIGNOF_MAX_ALIGN_T 8 | ||||||
| /* configure.ac defines HAVE_LARGEFILE_SUPPORT iff
 | /* configure.ac defines HAVE_LARGEFILE_SUPPORT iff
 | ||||||
|    sizeof(off_t) > sizeof(long), and sizeof(long long) >= sizeof(off_t). |    sizeof(off_t) > sizeof(long), and sizeof(long long) >= sizeof(off_t). | ||||||
|    On Win64 the second condition is not true, but if fpos_t replaces off_t |    On Win64 the second condition is not true, but if fpos_t replaces off_t | ||||||
|  | @ -351,6 +352,7 @@ Py_NO_ENABLE_SHARED to find out.  Also support MS_NO_COREDLL for b/w compat */ | ||||||
| #       else | #       else | ||||||
| #       define SIZEOF_TIME_T 4 | #       define SIZEOF_TIME_T 4 | ||||||
| #       endif | #       endif | ||||||
|  | #       define ALIGNOF_MAX_ALIGN_T 8 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef _DEBUG | #ifdef _DEBUG | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								PC/python3dll.c
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								PC/python3dll.c
									
										
									
										generated
									
									
									
								
							|  | @ -467,6 +467,7 @@ EXPORT_FUNC(PyObject_GetAttrString) | ||||||
| EXPORT_FUNC(PyObject_GetBuffer) | EXPORT_FUNC(PyObject_GetBuffer) | ||||||
| EXPORT_FUNC(PyObject_GetItem) | EXPORT_FUNC(PyObject_GetItem) | ||||||
| EXPORT_FUNC(PyObject_GetIter) | EXPORT_FUNC(PyObject_GetIter) | ||||||
|  | EXPORT_FUNC(PyObject_GetTypeData) | ||||||
| EXPORT_FUNC(PyObject_HasAttr) | EXPORT_FUNC(PyObject_HasAttr) | ||||||
| EXPORT_FUNC(PyObject_HasAttrString) | EXPORT_FUNC(PyObject_HasAttrString) | ||||||
| EXPORT_FUNC(PyObject_Hash) | EXPORT_FUNC(PyObject_Hash) | ||||||
|  | @ -618,6 +619,7 @@ EXPORT_FUNC(PyType_GetModuleState) | ||||||
| EXPORT_FUNC(PyType_GetName) | EXPORT_FUNC(PyType_GetName) | ||||||
| EXPORT_FUNC(PyType_GetQualName) | EXPORT_FUNC(PyType_GetQualName) | ||||||
| EXPORT_FUNC(PyType_GetSlot) | EXPORT_FUNC(PyType_GetSlot) | ||||||
|  | EXPORT_FUNC(PyType_GetTypeDataSize) | ||||||
| EXPORT_FUNC(PyType_IsSubtype) | EXPORT_FUNC(PyType_IsSubtype) | ||||||
| EXPORT_FUNC(PyType_Modified) | EXPORT_FUNC(PyType_Modified) | ||||||
| EXPORT_FUNC(PyType_Ready) | EXPORT_FUNC(PyType_Ready) | ||||||
|  |  | ||||||
|  | @ -98,6 +98,7 @@ | ||||||
|     <ClCompile Include="..\Modules\_testcapi\vectorcall.c" /> |     <ClCompile Include="..\Modules\_testcapi\vectorcall.c" /> | ||||||
|     <ClCompile Include="..\Modules\_testcapi\vectorcall_limited.c" /> |     <ClCompile Include="..\Modules\_testcapi\vectorcall_limited.c" /> | ||||||
|     <ClCompile Include="..\Modules\_testcapi\heaptype.c" /> |     <ClCompile Include="..\Modules\_testcapi\heaptype.c" /> | ||||||
|  |     <ClCompile Include="..\Modules\_testcapi\heaptype_relative.c" /> | ||||||
|     <ClCompile Include="..\Modules\_testcapi\unicode.c" /> |     <ClCompile Include="..\Modules\_testcapi\unicode.c" /> | ||||||
|     <ClCompile Include="..\Modules\_testcapi\pytime.c" /> |     <ClCompile Include="..\Modules\_testcapi\pytime.c" /> | ||||||
|     <ClCompile Include="..\Modules\_testcapi\datetime.c" /> |     <ClCompile Include="..\Modules\_testcapi\datetime.c" /> | ||||||
|  |  | ||||||
|  | @ -24,6 +24,9 @@ | ||||||
|     <ClCompile Include="..\Modules\_testcapi\heaptype.c"> |     <ClCompile Include="..\Modules\_testcapi\heaptype.c"> | ||||||
|       <Filter>Source Files</Filter> |       <Filter>Source Files</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|  |     <ClCompile Include="..\Modules\_testcapi\heaptype_relative.c"> | ||||||
|  |       <Filter>Source Files</Filter> | ||||||
|  |     </ClCompile> | ||||||
|     <ClCompile Include="..\Modules\_testcapi\unicode.c"> |     <ClCompile Include="..\Modules\_testcapi\unicode.c"> | ||||||
|       <Filter>Source Files</Filter> |       <Filter>Source Files</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|  |  | ||||||
|  | @ -8,6 +8,12 @@ PyObject * | ||||||
| PyMember_GetOne(const char *obj_addr, PyMemberDef *l) | PyMember_GetOne(const char *obj_addr, PyMemberDef *l) | ||||||
| { | { | ||||||
|     PyObject *v; |     PyObject *v; | ||||||
|  |     if (l->flags & Py_RELATIVE_OFFSET) { | ||||||
|  |         PyErr_SetString( | ||||||
|  |             PyExc_SystemError, | ||||||
|  |             "PyMember_GetOne used with Py_RELATIVE_OFFSET"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     const char* addr = obj_addr + l->offset; |     const char* addr = obj_addr + l->offset; | ||||||
|     switch (l->type) { |     switch (l->type) { | ||||||
|  | @ -103,6 +109,12 @@ int | ||||||
| PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) | PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) | ||||||
| { | { | ||||||
|     PyObject *oldv; |     PyObject *oldv; | ||||||
|  |     if (l->flags & Py_RELATIVE_OFFSET) { | ||||||
|  |         PyErr_SetString( | ||||||
|  |             PyExc_SystemError, | ||||||
|  |             "PyMember_SetOne used with Py_RELATIVE_OFFSET"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     addr += l->offset; |     addr += l->offset; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										35
									
								
								configure
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								configure
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -10729,6 +10729,41 @@ cat >>confdefs.h <<_ACEOF | ||||||
| _ACEOF | _ACEOF | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | # The cast to long int works around a bug in the HP C Compiler, | ||||||
|  | # see AC_CHECK_SIZEOF for more information. | ||||||
|  | { $as_echo "$as_me:${as_lineno-$LINENO}: checking alignment of max_align_t" >&5 | ||||||
|  | $as_echo_n "checking alignment of max_align_t... " >&6; } | ||||||
|  | if ${ac_cv_alignof_max_align_t+:} false; then : | ||||||
|  |   $as_echo_n "(cached) " >&6 | ||||||
|  | else | ||||||
|  |   if ac_fn_c_compute_int "$LINENO" "(long int) offsetof (ac__type_alignof_, y)" "ac_cv_alignof_max_align_t"        "$ac_includes_default | ||||||
|  | #ifndef offsetof | ||||||
|  | # define offsetof(type, member) ((char *) &((type *) 0)->member - (char *) 0) | ||||||
|  | #endif | ||||||
|  | typedef struct { char x; max_align_t y; } ac__type_alignof_;"; then : | ||||||
|  | 
 | ||||||
|  | else | ||||||
|  |   if test "$ac_cv_type_max_align_t" = yes; then | ||||||
|  |      { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 | ||||||
|  | $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} | ||||||
|  | as_fn_error 77 "cannot compute alignment of max_align_t | ||||||
|  | See \`config.log' for more details" "$LINENO" 5; } | ||||||
|  |    else | ||||||
|  |      ac_cv_alignof_max_align_t=0 | ||||||
|  |    fi | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | fi | ||||||
|  | { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_alignof_max_align_t" >&5 | ||||||
|  | $as_echo "$ac_cv_alignof_max_align_t" >&6; } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | cat >>confdefs.h <<_ACEOF | ||||||
|  | #define ALIGNOF_MAX_ALIGN_T $ac_cv_alignof_max_align_t | ||||||
|  | _ACEOF | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|   { $as_echo "$as_me:${as_lineno-$LINENO}: checking for long double" >&5 |   { $as_echo "$as_me:${as_lineno-$LINENO}: checking for long double" >&5 | ||||||
|  |  | ||||||
|  | @ -2921,6 +2921,7 @@ AC_CHECK_SIZEOF(size_t, 4) | ||||||
| AC_CHECK_ALIGNOF(size_t) | AC_CHECK_ALIGNOF(size_t) | ||||||
| AC_CHECK_SIZEOF(pid_t, 4) | AC_CHECK_SIZEOF(pid_t, 4) | ||||||
| AC_CHECK_SIZEOF(uintptr_t) | AC_CHECK_SIZEOF(uintptr_t) | ||||||
|  | AC_CHECK_ALIGNOF(max_align_t) | ||||||
| 
 | 
 | ||||||
| AC_TYPE_LONG_DOUBLE | AC_TYPE_LONG_DOUBLE | ||||||
| AC_CHECK_SIZEOF(long double, 16) | AC_CHECK_SIZEOF(long double, 16) | ||||||
|  |  | ||||||
|  | @ -19,6 +19,9 @@ | ||||||
| /* The normal alignment of `long', in bytes. */ | /* The normal alignment of `long', in bytes. */ | ||||||
| #undef ALIGNOF_LONG | #undef ALIGNOF_LONG | ||||||
| 
 | 
 | ||||||
|  | /* The normal alignment of `max_align_t', in bytes. */ | ||||||
|  | #undef ALIGNOF_MAX_ALIGN_T | ||||||
|  | 
 | ||||||
| /* The normal alignment of `size_t', in bytes. */ | /* The normal alignment of `size_t', in bytes. */ | ||||||
| #undef ALIGNOF_SIZE_T | #undef ALIGNOF_SIZE_T | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Petr Viktorin
						Petr Viktorin