mirror of
https://github.com/python/cpython.git
synced 2025-08-03 00:23:06 +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
|
@ -18,6 +18,7 @@
|
|||
#include "structmember.h" // PyMemberDef
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stddef.h> // ptrdiff_t
|
||||
|
||||
/*[clinic input]
|
||||
class type "PyTypeObject *" "&PyType_Type"
|
||||
|
@ -1686,6 +1687,12 @@ PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
|
||||
/* Helpers for subtyping */
|
||||
|
||||
static inline PyMemberDef *
|
||||
_PyHeapType_GET_MEMBERS(PyHeapTypeObject* type)
|
||||
{
|
||||
return PyObject_GetItemData((PyObject *)type);
|
||||
}
|
||||
|
||||
static int
|
||||
traverse_slots(PyTypeObject *type, PyObject *self, visitproc visit, void *arg)
|
||||
{
|
||||
|
@ -3873,6 +3880,15 @@ static const PySlot_Offset pyslot_offsets[] = {
|
|||
#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
|
||||
* types), return a tuple of types.
|
||||
*/
|
||||
|
@ -4013,6 +4029,20 @@ _PyType_FromMetaclass_impl(
|
|||
assert(memb->flags == READONLY);
|
||||
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;
|
||||
case Py_tp_doc:
|
||||
|
@ -4154,6 +4184,32 @@ _PyType_FromMetaclass_impl(
|
|||
// here we just check its work
|
||||
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
|
||||
*
|
||||
* Between here and PyType_Ready, we should limit:
|
||||
|
@ -4201,8 +4257,8 @@ _PyType_FromMetaclass_impl(
|
|||
|
||||
/* Copy the sizes */
|
||||
|
||||
type->tp_basicsize = spec->basicsize;
|
||||
type->tp_itemsize = spec->itemsize;
|
||||
type->tp_basicsize = basicsize;
|
||||
type->tp_itemsize = itemsize;
|
||||
|
||||
/* Copy all the ordinary slots */
|
||||
|
||||
|
@ -4219,6 +4275,16 @@ _PyType_FromMetaclass_impl(
|
|||
size_t len = Py_TYPE(type)->tp_itemsize * nmembers;
|
||||
memcpy(_PyHeapType_GET_MEMBERS(res), slot->pfunc, len);
|
||||
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;
|
||||
default:
|
||||
|
@ -4227,6 +4293,7 @@ _PyType_FromMetaclass_impl(
|
|||
PySlot_Offset slotoffsets = pyslot_offsets[slot->slot];
|
||||
short slot_offset = slotoffsets.slot_offset;
|
||||
if (slotoffsets.subslot_offset == -1) {
|
||||
/* Set a slot in the main PyTypeObject */
|
||||
*(void**)((char*)res_start + slot_offset) = slot->pfunc;
|
||||
}
|
||||
else {
|
||||
|
@ -4461,6 +4528,34 @@ PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def)
|
|||
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.
|
||||
This returns a borrowed reference, and might set an exception.
|
||||
|
@ -5158,7 +5253,8 @@ PyTypeObject PyType_Type = {
|
|||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
||||
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 */
|
||||
(traverseproc)type_traverse, /* tp_traverse */
|
||||
(inquiry)type_clear, /* tp_clear */
|
||||
|
@ -6572,9 +6668,14 @@ inherit_special(PyTypeObject *type, PyTypeObject *base)
|
|||
else if (PyType_IsSubtype(base, &PyDict_Type)) {
|
||||
type->tp_flags |= Py_TPFLAGS_DICT_SUBCLASS;
|
||||
}
|
||||
|
||||
/* Setup some inheritable flags */
|
||||
if (PyType_HasFeature(base, _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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue