mirror of https://github.com/python/cpython
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
|
@ -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?
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 time
|
||||||
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 @@ class CAPITest(unittest.TestCase):
|
||||||
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 CAPITest(unittest.TestCase):
|
||||||
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):
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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…
Reference in New Issue