https://github.com/python/cpython/commit/16be8db6bec7bf8b58df80601cab58a26eee4afa
commit: 16be8db6bec7bf8b58df80601cab58a26eee4afa
branch: main
author: Petr Viktorin <[email protected]>
committer: encukou <[email protected]>
date: 2024-09-05T14:14:05+02:00
summary:
gh-123465: Allow Py_RELATIVE_OFFSET for __*offset__ members (GH-123474)
files:
A Misc/NEWS.d/next/C_API/2024-08-29-15-05-19.gh-issue-123465.eqwNWq.rst
A Modules/_testlimitedcapi/clinic/heaptype_relative.c.h
M Doc/c-api/structures.rst
M Lib/test/test_call.py
M Lib/test/test_capi/test_misc.py
M Modules/_testlimitedcapi/heaptype_relative.c
M Modules/_testlimitedcapi/vectorcall_limited.c
M Objects/typeobject.c
diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst
index f9461ab01f6049..d333df397782e0 100644
--- a/Doc/c-api/structures.rst
+++ b/Doc/c-api/structures.rst
@@ -485,7 +485,8 @@ Accessing attributes of extension types
``PyMemberDef`` may contain a definition for the special member
``"__vectorcalloffset__"``, corresponding to
:c:member:`~PyTypeObject.tp_vectorcall_offset` in type objects.
- These must be defined with ``Py_T_PYSSIZET`` and ``Py_READONLY``, for
example::
+ This member must be defined with ``Py_T_PYSSIZET``, and either
+ ``Py_READONLY`` or ``Py_READONLY | Py_RELATIVE_OFFSET``. For example::
static PyMemberDef spam_type_members[] = {
{"__vectorcalloffset__", Py_T_PYSSIZET,
@@ -506,6 +507,12 @@ Accessing attributes of extension types
``PyMemberDef`` is always available.
Previously, it required including ``"structmember.h"``.
+ .. versionchanged:: 3.14
+
+ :c:macro:`Py_RELATIVE_OFFSET` is now allowed for
+ ``"__vectorcalloffset__"``, ``"__dictoffset__"`` and
+ ``"__weaklistoffset__"``.
+
.. c:function:: PyObject* PyMember_GetOne(const char *obj_addr, struct
PyMemberDef *m)
Get an attribute belonging to the object at address *obj_addr*. The
diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py
index 504f8800a00aa5..68e3b2a0d4d932 100644
--- a/Lib/test/test_call.py
+++ b/Lib/test/test_call.py
@@ -851,8 +851,13 @@ def get_a(x):
@requires_limited_api
def test_vectorcall_limited_incoming(self):
from _testcapi import pyobject_vectorcall
- obj = _testlimitedcapi.LimitedVectorCallClass()
- self.assertEqual(pyobject_vectorcall(obj, (), ()), "vectorcall called")
+ for cls in (_testlimitedcapi.LimitedVectorCallClass,
+ _testlimitedcapi.LimitedRelativeVectorCallClass):
+ with self.subTest(cls=cls):
+ obj = cls()
+ self.assertEqual(
+ pyobject_vectorcall(obj, (), ()),
+ "vectorcall called")
@requires_limited_api
def test_vectorcall_limited_outgoing(self):
diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py
index b103bf2450bde0..d50217b695967e 100644
--- a/Lib/test/test_capi/test_misc.py
+++ b/Lib/test/test_capi/test_misc.py
@@ -541,14 +541,19 @@ def __del__(self):
self.assertEqual(new_type_refcnt, sys.getrefcount(A))
def test_heaptype_with_dict(self):
- inst = _testcapi.HeapCTypeWithDict()
- inst.foo = 42
- self.assertEqual(inst.foo, 42)
- self.assertEqual(inst.dictobj, inst.__dict__)
- self.assertEqual(inst.dictobj, {"foo": 42})
+ for cls in (
+ _testcapi.HeapCTypeWithDict,
+ _testlimitedcapi.HeapCTypeWithRelativeDict,
+ ):
+ with self.subTest(cls=cls):
+ inst = cls()
+ inst.foo = 42
+ self.assertEqual(inst.foo, 42)
+ self.assertEqual(inst.dictobj, inst.__dict__)
+ self.assertEqual(inst.dictobj, {"foo": 42})
- inst = _testcapi.HeapCTypeWithDict()
- self.assertEqual({}, inst.__dict__)
+ inst = cls()
+ self.assertEqual({}, inst.__dict__)
def test_heaptype_with_managed_dict(self):
inst = _testcapi.HeapCTypeWithManagedDict()
@@ -585,10 +590,15 @@ def test_heaptype_with_negative_dict(self):
self.assertEqual({}, inst.__dict__)
def test_heaptype_with_weakref(self):
- inst = _testcapi.HeapCTypeWithWeakref()
- ref = weakref.ref(inst)
- self.assertEqual(ref(), inst)
- self.assertEqual(inst.weakreflist, ref)
+ for cls in (
+ _testcapi.HeapCTypeWithWeakref,
+ _testlimitedcapi.HeapCTypeWithRelativeWeakref,
+ ):
+ with self.subTest(cls=cls):
+ inst = cls()
+ ref = weakref.ref(inst)
+ self.assertEqual(ref(), inst)
+ self.assertEqual(inst.weakreflist, ref)
def test_heaptype_with_managed_weakref(self):
inst = _testcapi.HeapCTypeWithManagedWeakref()
@@ -730,45 +740,56 @@ class Base(metaclass=metaclass):
self.assertIsInstance(sub, metaclass)
def test_multiple_inheritance_ctypes_with_weakref_or_dict(self):
+ for weakref_cls in (_testcapi.HeapCTypeWithWeakref,
+ _testlimitedcapi.HeapCTypeWithRelativeWeakref):
+ for dict_cls in (_testcapi.HeapCTypeWithDict,
+ _testlimitedcapi.HeapCTypeWithRelativeDict):
+ with self.subTest(weakref_cls=weakref_cls, dict_cls=dict_cls):
- with self.assertRaises(TypeError):
- class Both1(_testcapi.HeapCTypeWithWeakref,
_testcapi.HeapCTypeWithDict):
- pass
- with self.assertRaises(TypeError):
- class Both2(_testcapi.HeapCTypeWithDict,
_testcapi.HeapCTypeWithWeakref):
- pass
+ with self.assertRaises(TypeError):
+ class Both1(weakref_cls, dict_cls):
+ pass
+ with self.assertRaises(TypeError):
+ class Both2(dict_cls, weakref_cls):
+ pass
def
test_multiple_inheritance_ctypes_with_weakref_or_dict_and_other_builtin(self):
+ for dict_cls in (_testcapi.HeapCTypeWithDict,
+ _testlimitedcapi.HeapCTypeWithRelativeDict):
+ for weakref_cls in (_testcapi.HeapCTypeWithWeakref,
+ _testlimitedcapi.HeapCTypeWithRelativeWeakref):
+ with self.subTest(dict_cls=dict_cls, weakref_cls=weakref_cls):
- with self.assertRaises(TypeError):
- class C1(_testcapi.HeapCTypeWithDict, list):
- pass
+ with self.assertRaises(TypeError):
+ class C1(dict_cls, list):
+ pass
- with self.assertRaises(TypeError):
- class C2(_testcapi.HeapCTypeWithWeakref, list):
- pass
+ with self.assertRaises(TypeError):
+ class C2(weakref_cls, list):
+ pass
- class C3(_testcapi.HeapCTypeWithManagedDict, list):
- pass
- class C4(_testcapi.HeapCTypeWithManagedWeakref, list):
- pass
+ class C3(_testcapi.HeapCTypeWithManagedDict, list):
+ pass
+ class C4(_testcapi.HeapCTypeWithManagedWeakref, list):
+ pass
- inst = C3()
- inst.append(0)
- str(inst.__dict__)
+ inst = C3()
+ inst.append(0)
+ str(inst.__dict__)
- inst = C4()
- inst.append(0)
- str(inst.__weakref__)
+ inst = C4()
+ inst.append(0)
+ str(inst.__weakref__)
- for cls in (_testcapi.HeapCTypeWithManagedDict,
_testcapi.HeapCTypeWithManagedWeakref):
- for cls2 in (_testcapi.HeapCTypeWithDict,
_testcapi.HeapCTypeWithWeakref):
- class S(cls, cls2):
- pass
- class B1(C3, cls):
- pass
- class B2(C4, cls):
- pass
+ for cls in (_testcapi.HeapCTypeWithManagedDict,
+ _testcapi.HeapCTypeWithManagedWeakref):
+ for cls2 in (dict_cls, weakref_cls):
+ class S(cls, cls2):
+ pass
+ class B1(C3, cls):
+ pass
+ class B2(C4, cls):
+ pass
def test_pytype_fromspec_with_repeated_slots(self):
for variant in range(2):
@@ -1272,6 +1293,53 @@ def test_heaptype_relative_members_errors(self):
SystemError, r"PyMember_SetOne used with Py_RELATIVE_OFFSET"):
instance.set_memb_relative(0)
+ def test_heaptype_relative_special_members_errors(self):
+ for member_name in "__vectorcalloffset__", "__dictoffset__",
"__weaklistoffset__":
+ with self.subTest(member_name=member_name):
+ with self.assertRaisesRegex(
+ SystemError,
+ r"With Py_RELATIVE_OFFSET, basicsize must be
negative."):
+ _testlimitedcapi.make_heaptype_with_member(
+ basicsize=sys.getsizeof(object()) + 100,
+ add_relative_flag=True,
+ member_name=member_name,
+ member_offset=0,
+ member_type=_testlimitedcapi.Py_T_PYSSIZET,
+ member_flags=_testlimitedcapi.Py_READONLY,
+ )
+ with self.assertRaisesRegex(
+ SystemError,
+ r"Member offset out of range \(0\.\.-basicsize\)"):
+ _testlimitedcapi.make_heaptype_with_member(
+ basicsize=-8,
+ add_relative_flag=True,
+ member_name=member_name,
+ member_offset=-1,
+ member_type=_testlimitedcapi.Py_T_PYSSIZET,
+ member_flags=_testlimitedcapi.Py_READONLY,
+ )
+ with self.assertRaisesRegex(
+ SystemError,
+ r"type of %s must be Py_T_PYSSIZET" % member_name):
+ _testlimitedcapi.make_heaptype_with_member(
+ basicsize=-100,
+ add_relative_flag=True,
+ member_name=member_name,
+ member_offset=0,
+ member_flags=_testlimitedcapi.Py_READONLY,
+ )
+ with self.assertRaisesRegex(
+ SystemError,
+ r"flags for %s must be " % member_name):
+ _testlimitedcapi.make_heaptype_with_member(
+ basicsize=-100,
+ add_relative_flag=True,
+ member_name=member_name,
+ member_offset=0,
+ member_type=_testlimitedcapi.Py_T_PYSSIZET,
+ member_flags=0,
+ )
+
def test_pyobject_getitemdata_error(self):
"""Test PyObject_GetItemData fails on unsupported types"""
with self.assertRaises(TypeError):
diff --git
a/Misc/NEWS.d/next/C_API/2024-08-29-15-05-19.gh-issue-123465.eqwNWq.rst
b/Misc/NEWS.d/next/C_API/2024-08-29-15-05-19.gh-issue-123465.eqwNWq.rst
new file mode 100644
index 00000000000000..1935adfad8885b
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2024-08-29-15-05-19.gh-issue-123465.eqwNWq.rst
@@ -0,0 +1,4 @@
+:c:macro:`Py_RELATIVE_OFFSET` is now allowed in :c:type:`PyMemberDef` for
+the special offset member ``"__vectorcalloffset__"``, as well as the
+discouraged special offset members ``"__dictoffset__"`` and
+``"__weaklistoffset__"``
diff --git a/Modules/_testlimitedcapi/clinic/heaptype_relative.c.h
b/Modules/_testlimitedcapi/clinic/heaptype_relative.c.h
new file mode 100644
index 00000000000000..994f83102adfb0
--- /dev/null
+++ b/Modules/_testlimitedcapi/clinic/heaptype_relative.c.h
@@ -0,0 +1,44 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+PyDoc_STRVAR(make_heaptype_with_member__doc__,
+"make_heaptype_with_member($module, /, extra_base_size=0, basicsize=0,\n"
+" member_offset=0, add_relative_flag=False, *,\n"
+" member_name=\'memb\', member_flags=0,\n"
+" member_type=-1)\n"
+"--\n"
+"\n");
+
+#define MAKE_HEAPTYPE_WITH_MEMBER_METHODDEF \
+ {"make_heaptype_with_member",
(PyCFunction)(void(*)(void))make_heaptype_with_member,
METH_VARARGS|METH_KEYWORDS, make_heaptype_with_member__doc__},
+
+static PyObject *
+make_heaptype_with_member_impl(PyObject *module, int extra_base_size,
+ int basicsize, int member_offset,
+ int add_relative_flag,
+ const char *member_name, int member_flags,
+ int member_type);
+
+static PyObject *
+make_heaptype_with_member(PyObject *module, PyObject *args, PyObject *kwargs)
+{
+ PyObject *return_value = NULL;
+ static char *_keywords[] = {"extra_base_size", "basicsize",
"member_offset", "add_relative_flag", "member_name", "member_flags",
"member_type", NULL};
+ int extra_base_size = 0;
+ int basicsize = 0;
+ int member_offset = 0;
+ int add_relative_flag = 0;
+ const char *member_name = "memb";
+ int member_flags = 0;
+ int member_type = Py_T_BYTE;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"|iiip$sii:make_heaptype_with_member", _keywords,
+ &extra_base_size, &basicsize, &member_offset, &add_relative_flag,
&member_name, &member_flags, &member_type))
+ goto exit;
+ return_value = make_heaptype_with_member_impl(module, extra_base_size,
basicsize, member_offset, add_relative_flag, member_name, member_flags,
member_type);
+
+exit:
+ return return_value;
+}
+/*[clinic end generated code: output=01933185947faecc input=a9049054013a1b77]*/
diff --git a/Modules/_testlimitedcapi/heaptype_relative.c
b/Modules/_testlimitedcapi/heaptype_relative.c
index c2531518d86a51..45d65ee47349f9 100644
--- a/Modules/_testlimitedcapi/heaptype_relative.c
+++ b/Modules/_testlimitedcapi/heaptype_relative.c
@@ -8,6 +8,8 @@
#include <stddef.h> // max_align_t
#include <string.h> // memset
+#include "clinic/heaptype_relative.c.h"
+
static PyType_Slot empty_slots[] = {
{0, NULL},
};
@@ -247,6 +249,81 @@ heaptype_with_member_set_memb_relative(PyObject *self,
PyObject *value)
Py_RETURN_NONE;
}
+typedef struct {
+ int padding; // just so the offset isn't 0
+ PyObject *dict;
+} HeapCTypeWithDictStruct;
+
+static void
+heapctypewithrelativedict_dealloc(PyObject* self)
+{
+ PyTypeObject *tp = Py_TYPE(self);
+ HeapCTypeWithDictStruct *data = PyObject_GetTypeData(self, tp);
+ Py_XDECREF(data->dict);
+ PyObject_Free(self);
+ Py_DECREF(tp);
+}
+
+static PyType_Spec HeapCTypeWithRelativeDict_spec = {
+ .name = "_testcapi.HeapCTypeWithRelativeDict",
+ .basicsize = -(int)sizeof(HeapCTypeWithDictStruct),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .slots = (PyType_Slot[]) {
+ {Py_tp_dealloc, heapctypewithrelativedict_dealloc},
+ {Py_tp_getset, (PyGetSetDef[]) {
+ {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
+ {NULL} /* Sentinel */
+ }},
+ {Py_tp_members, (PyMemberDef[]) {
+ {"dictobj", _Py_T_OBJECT,
+ offsetof(HeapCTypeWithDictStruct, dict),
+ Py_RELATIVE_OFFSET},
+ {"__dictoffset__", Py_T_PYSSIZET,
+ offsetof(HeapCTypeWithDictStruct, dict),
+ Py_READONLY | Py_RELATIVE_OFFSET},
+ {NULL} /* Sentinel */
+ }},
+ {0, 0},
+ }
+};
+
+typedef struct {
+ char padding; // just so the offset isn't 0
+ PyObject *weakreflist;
+} HeapCTypeWithWeakrefStruct;
+
+static void
+heapctypewithrelativeweakref_dealloc(PyObject* self)
+{
+ PyTypeObject *tp = Py_TYPE(self);
+ HeapCTypeWithWeakrefStruct *data = PyObject_GetTypeData(self, tp);
+ if (data->weakreflist != NULL) {
+ PyObject_ClearWeakRefs(self);
+ }
+ Py_XDECREF(data->weakreflist);
+ PyObject_Free(self);
+ Py_DECREF(tp);
+}
+
+static PyType_Spec HeapCTypeWithRelativeWeakref_spec = {
+ .name = "_testcapi.HeapCTypeWithRelativeWeakref",
+ .basicsize = -(int)sizeof(HeapCTypeWithWeakrefStruct),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .slots = (PyType_Slot[]) {
+ {Py_tp_dealloc, heapctypewithrelativeweakref_dealloc},
+ {Py_tp_members, (PyMemberDef[]) {
+ {"weakreflist", _Py_T_OBJECT,
+ offsetof(HeapCTypeWithWeakrefStruct, weakreflist),
+ Py_RELATIVE_OFFSET},
+ {"__weaklistoffset__", Py_T_PYSSIZET,
+ offsetof(HeapCTypeWithWeakrefStruct, weakreflist),
+ Py_READONLY | Py_RELATIVE_OFFSET},
+ {NULL} /* Sentinel */
+ }},
+ {0, 0},
+ }
+};
+
static PyMethodDef heaptype_with_member_methods[] = {
{"get_memb", heaptype_with_member_get_memb, METH_NOARGS},
{"set_memb", heaptype_with_member_set_memb, METH_O},
@@ -256,19 +333,31 @@ static PyMethodDef heaptype_with_member_methods[] = {
{NULL},
};
+/*[clinic input]
+make_heaptype_with_member
+
+ extra_base_size: int = 0
+ basicsize: int = 0
+ member_offset: int = 0
+ add_relative_flag: bool = False
+ *
+ member_name: str = "memb"
+ member_flags: int = 0
+ member_type: int(c_default="Py_T_BYTE") = -1
+
+[clinic start generated code]*/
+
static PyObject *
-make_heaptype_with_member(PyObject *module, PyObject *args)
+make_heaptype_with_member_impl(PyObject *module, int extra_base_size,
+ int basicsize, int member_offset,
+ int add_relative_flag,
+ const char *member_name, int member_flags,
+ int member_type)
+/*[clinic end generated code: output=7005db9a07396997 input=007e29cdbe1d3390]*/
{
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,
@@ -281,7 +370,8 @@ make_heaptype_with_member(PyObject *module, PyObject *args)
}
PyMemberDef members[] = {
- {"memb", Py_T_BYTE, offset, add_flag ? Py_RELATIVE_OFFSET : 0},
+ {member_name, member_type, member_offset,
+ member_flags | (add_relative_flag ? Py_RELATIVE_OFFSET : 0)},
{0},
};
PyType_Slot slots[] = {
@@ -325,7 +415,7 @@ 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},
+ MAKE_HEAPTYPE_WITH_MEMBER_METHODDEF
{"test_alignof_max_align_t", test_alignof_max_align_t, METH_NOARGS},
{NULL},
};
@@ -341,5 +431,42 @@ _PyTestLimitedCAPI_Init_HeaptypeRelative(PyObject *m)
return -1;
}
+#define ADD_FROM_SPEC(SPEC) do { \
+ PyObject *tp = PyType_FromSpec(SPEC); \
+ if (!tp) { \
+ return -1; \
+ } \
+ if (PyModule_AddType(m, (PyTypeObject *)tp) < 0) { \
+ return -1; \
+ } \
+ } while (0)
+
+ PyObject *tp;
+
+ tp = PyType_FromSpec(&HeapCTypeWithRelativeDict_spec);
+ if (!tp) {
+ return -1;
+ }
+ if (PyModule_AddType(m, (PyTypeObject *)tp) < 0) {
+ return -1;
+ }
+ Py_DECREF(tp);
+
+ tp = PyType_FromSpec(&HeapCTypeWithRelativeWeakref_spec);
+ if (!tp) {
+ return -1;
+ }
+ if (PyModule_AddType(m, (PyTypeObject *)tp) < 0) {
+ return -1;
+ }
+ Py_DECREF(tp);
+
+ if (PyModule_AddIntMacro(m, Py_T_PYSSIZET) < 0) {
+ return -1;
+ }
+ if (PyModule_AddIntMacro(m, Py_READONLY) < 0) {
+ return -1;
+ }
+
return 0;
}
diff --git a/Modules/_testlimitedcapi/vectorcall_limited.c
b/Modules/_testlimitedcapi/vectorcall_limited.c
index 5ef97ca8a063e1..4a7af965776470 100644
--- a/Modules/_testlimitedcapi/vectorcall_limited.c
+++ b/Modules/_testlimitedcapi/vectorcall_limited.c
@@ -6,6 +6,8 @@
# define Py_LIMITED_API 0x030c0000
#endif
+#include <stddef.h> // offsetof
+
#include "parts.h"
#include "clinic/vectorcall_limited.c.h"
@@ -175,6 +177,41 @@ static PyType_Spec LimitedVectorCallClass_spec = {
.slots = LimitedVectorallClass_slots,
};
+typedef struct {
+ vectorcallfunc vfunc;
+} LimitedRelativeVectorCallStruct;
+
+static PyObject *
+LimitedRelativeVectorCallClass_new(PyTypeObject *tp, PyTypeObject *a,
PyTypeObject *kw)
+{
+ PyObject *self = ((allocfunc)PyType_GetSlot(tp, Py_tp_alloc))(tp, 0);
+ if (!self) {
+ return NULL;
+ }
+ LimitedRelativeVectorCallStruct *data = PyObject_GetTypeData(self, tp);
+ data->vfunc = LimitedVectorCallClass_vectorcall;
+ return self;
+}
+
+
+static PyType_Spec LimitedRelativeVectorCallClass_spec = {
+ .name = "_testlimitedcapi.LimitedRelativeVectorCallClass",
+ .basicsize = -(int)sizeof(LimitedRelativeVectorCallStruct),
+ .flags = Py_TPFLAGS_DEFAULT
+ | Py_TPFLAGS_HAVE_VECTORCALL,
+ .slots = (PyType_Slot[]) {
+ {Py_tp_new, LimitedRelativeVectorCallClass_new},
+ {Py_tp_call, LimitedVectorCallClass_tpcall},
+ {Py_tp_members, (PyMemberDef[]){
+ {"__vectorcalloffset__", Py_T_PYSSIZET,
+ offsetof(LimitedRelativeVectorCallStruct, vfunc),
+ Py_READONLY | Py_RELATIVE_OFFSET},
+ {NULL}
+ }},
+ {0}
+ },
+};
+
static PyMethodDef TestMethods[] = {
_TESTLIMITEDCAPI_CALL_VECTORCALL_METHODDEF
_TESTLIMITEDCAPI_CALL_VECTORCALL_METHOD_METHODDEF
@@ -197,5 +234,16 @@ _PyTestLimitedCAPI_Init_VectorcallLimited(PyObject *m)
return -1;
}
Py_DECREF(LimitedVectorCallClass);
+
+ PyObject *LimitedRelativeVectorCallClass = PyType_FromModuleAndSpec(
+ m, &LimitedRelativeVectorCallClass_spec, NULL);
+ if (!LimitedRelativeVectorCallClass) {
+ return -1;
+ }
+ if (PyModule_AddType(m, (PyTypeObject *)LimitedRelativeVectorCallClass) <
0) {
+ return -1;
+ }
+ Py_DECREF(LimitedRelativeVectorCallClass);
+
return 0;
}
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 78f6931dc1d289..9dc0ebd1c6a852 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -4642,6 +4642,41 @@ check_basicsize_includes_size_and_offsets(PyTypeObject*
type)
return 1;
}
+/* Set *dest to the offset specified by a special "__*offset__" member.
+ * Return 0 on success, -1 on failure.
+ */
+static inline int
+special_offset_from_member(
+ const PyMemberDef *memb /* may be NULL */,
+ Py_ssize_t type_data_offset,
+ Py_ssize_t *dest /* not NULL */)
+{
+ if (memb == NULL) {
+ *dest = 0;
+ return 0;
+ }
+ if (memb->type != Py_T_PYSSIZET) {
+ PyErr_Format(
+ PyExc_SystemError,
+ "type of %s must be Py_T_PYSSIZET",
+ memb->name);
+ return -1;
+ }
+ if (memb->flags == Py_READONLY) {
+ *dest = memb->offset;
+ return 0;
+ }
+ else if (memb->flags == (Py_READONLY | Py_RELATIVE_OFFSET)) {
+ *dest = memb->offset + type_data_offset;
+ return 0;
+ }
+ PyErr_Format(
+ PyExc_SystemError,
+ "flags for %s must be Py_READONLY or (Py_READONLY |
Py_RELATIVE_OFFSET)",
+ memb->name);
+ return -1;
+}
+
static PyObject *
_PyType_FromMetaclass_impl(
PyTypeObject *metaclass, PyObject *module,
@@ -4667,10 +4702,11 @@ _PyType_FromMetaclass_impl(
const PyType_Slot *slot;
Py_ssize_t nmembers = 0;
- Py_ssize_t weaklistoffset, dictoffset, vectorcalloffset;
+ const PyMemberDef *weaklistoffset_member = NULL;
+ const PyMemberDef *dictoffset_member = NULL;
+ const PyMemberDef *vectorcalloffset_member = NULL;
char *res_start;
- nmembers = weaklistoffset = dictoffset = vectorcalloffset = 0;
for (slot = spec->slots; slot->slot; slot++) {
if (slot->slot < 0
|| (size_t)slot->slot >= Py_ARRAY_LENGTH(pyslot_offsets)) {
@@ -4687,24 +4723,6 @@ _PyType_FromMetaclass_impl(
}
for (const PyMemberDef *memb = slot->pfunc; memb->name != NULL;
memb++) {
nmembers++;
- if (strcmp(memb->name, "__weaklistoffset__") == 0) {
- // The PyMemberDef must be a Py_ssize_t and readonly
- assert(memb->type == Py_T_PYSSIZET);
- assert(memb->flags == Py_READONLY);
- weaklistoffset = memb->offset;
- }
- if (strcmp(memb->name, "__dictoffset__") == 0) {
- // The PyMemberDef must be a Py_ssize_t and readonly
- assert(memb->type == Py_T_PYSSIZET);
- assert(memb->flags == Py_READONLY);
- dictoffset = memb->offset;
- }
- if (strcmp(memb->name, "__vectorcalloffset__") == 0) {
- // The PyMemberDef must be a Py_ssize_t and readonly
- assert(memb->type == Py_T_PYSSIZET);
- assert(memb->flags == Py_READONLY);
- vectorcalloffset = memb->offset;
- }
if (memb->flags & Py_RELATIVE_OFFSET) {
if (spec->basicsize > 0) {
PyErr_SetString(
@@ -4719,6 +4737,15 @@ _PyType_FromMetaclass_impl(
goto finally;
}
}
+ if (strcmp(memb->name, "__weaklistoffset__") == 0) {
+ weaklistoffset_member = memb;
+ }
+ if (strcmp(memb->name, "__dictoffset__") == 0) {
+ dictoffset_member = memb;
+ }
+ if (strcmp(memb->name, "__vectorcalloffset__") == 0) {
+ vectorcalloffset_member = memb;
+ }
}
break;
case Py_tp_doc:
@@ -4882,6 +4909,24 @@ _PyType_FromMetaclass_impl(
Py_ssize_t itemsize = spec->itemsize;
+ /* Compute special offsets */
+
+ Py_ssize_t weaklistoffset = 0;
+ if (special_offset_from_member(weaklistoffset_member, type_data_offset,
+ &weaklistoffset) < 0) {
+ goto finally;
+ }
+ Py_ssize_t dictoffset = 0;
+ if (special_offset_from_member(dictoffset_member, type_data_offset,
+ &dictoffset) < 0) {
+ goto finally;
+ }
+ Py_ssize_t vectorcalloffset = 0;
+ if (special_offset_from_member(vectorcalloffset_member, type_data_offset,
+ &vectorcalloffset) < 0) {
+ goto finally;
+ }
+
/* Allocate the new type
*
* Between here and PyType_Ready, we should limit:
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]