Skip to content

bpo-41073: PyType_GetSlot() can now accept static types. #21931

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Nov 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
25e6a1b
PyType_GetSlot() could accept static types
shihai1991 Jul 8, 2020
7fcf9eb
update docs' style
shihai1991 Jul 8, 2020
159fc09
use typeslots not in limit apis
shihai1991 Jul 9, 2020
588a847
add test cases to against PyType_GetSlot()
shihai1991 Aug 20, 2020
ec0348d
update typeslots.h
shihai1991 Aug 20, 2020
abda762
Add generate statictypeslots feature
shihai1991 Aug 22, 2020
6d94381
revert typeslots.inc to master base
shihai1991 Aug 23, 2020
eb3907d
update get static type slots
shihai1991 Sep 2, 2020
7537c86
Merge remote-tracking branch 'origin/master' into bpo_41073_add_types…
shihai1991 Sep 5, 2020
fe9f858
Fix compile error
shihai1991 Sep 5, 2020
93d849c
Use petr's idea
shihai1991 Oct 20, 2020
5171eb7
apply petr's comment
shihai1991 Oct 25, 2020
b3f992a
Merge remote-tracking branch 'origin/master' into bpo_41073_add_types…
shihai1991 Oct 29, 2020
2a6e0f4
update release doc
shihai1991 Oct 29, 2020
a9f66f3
remove new adding slots
shihai1991 Oct 30, 2020
c1844e3
apply petr's comment
shihai1991 Nov 1, 2020
ccdd9b6
Call test_get_statictype_slots in the test suite
encukou Nov 3, 2020
9194565
Raise exception on the invalid slot 0
encukou Nov 3, 2020
708cd82
Use if rather than assert in tests
encukou Nov 3, 2020
b351b2c
Add/adjust comments & news entries
encukou Nov 3, 2020
2df9180
Align method definitions in _testcapi
encukou Nov 3, 2020
2b077e3
Rewrite the slot-setting code a bit
encukou Nov 3, 2020
943174b
Remove the loop in PyType_GetSlot; we already know the position
encukou Nov 3, 2020
2d8b57f
typeslots.py: Add back code writing null entries
encukou Nov 3, 2020
2d72953
Merge remote-tracking branch 'origin/master' into bpo_41073_add_types…
shihai1991 Nov 7, 2020
6a688d9
Remove redundant test function
encukou Nov 10, 2020
fc2c06b
Merge master branch; regenerate typeslots.inc
encukou Nov 10, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Doc/c-api/type.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,12 @@ Type Objects

See :c:member:`PyType_Slot.slot` for possible values of the *slot* argument.

An exception is raised if *type* is not a heap type.

.. versionadded:: 3.4

.. versionchanged:: 3.10
:c:func:`PyType_GetSlot` can now accept all types.
Previously, it was limited to heap types.

.. c:function:: PyObject* PyType_GetModule(PyTypeObject *type)

Return the module object associated with the given type when the type was
Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,9 @@ New Features
slot.
(Contributed by Hai Shi in :issue:`41832`.)

* The :c:func:`PyType_GetSlot` function can accept static types.
(Contributed by Hai Shi and Petr Viktorin in :issue:`41073`.)


Porting to Python 3.10
----------------------
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:c:func:`PyType_GetSlot()` can now accept static types.
62 changes: 60 additions & 2 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,62 @@ test_buildvalue_N(PyObject *self, PyObject *Py_UNUSED(ignored))
}


static PyObject *
test_get_statictype_slots(PyObject *self, PyObject *Py_UNUSED(ignored))
{
newfunc tp_new = PyType_GetSlot(&PyLong_Type, Py_tp_new);
if (PyLong_Type.tp_new != tp_new) {
PyErr_SetString(PyExc_AssertionError, "mismatch: tp_new of long");
return NULL;
}

reprfunc tp_repr = PyType_GetSlot(&PyLong_Type, Py_tp_repr);
if (PyLong_Type.tp_repr != tp_repr) {
PyErr_SetString(PyExc_AssertionError, "mismatch: tp_repr of long");
return NULL;
}

ternaryfunc tp_call = PyType_GetSlot(&PyLong_Type, Py_tp_call);
if (tp_call != NULL) {
PyErr_SetString(PyExc_AssertionError, "mismatch: tp_call of long");
return NULL;
}

binaryfunc nb_add = PyType_GetSlot(&PyLong_Type, Py_nb_add);
if (PyLong_Type.tp_as_number->nb_add != nb_add) {
PyErr_SetString(PyExc_AssertionError, "mismatch: nb_add of long");
return NULL;
}

lenfunc mp_length = PyType_GetSlot(&PyLong_Type, Py_mp_length);
if (mp_length != NULL) {
PyErr_SetString(PyExc_AssertionError, "mismatch: mp_length of long");
return NULL;
}

void *over_value = PyType_GetSlot(&PyLong_Type, Py_bf_releasebuffer + 1);
if (over_value != NULL) {
PyErr_SetString(PyExc_AssertionError, "mismatch: max+1 of long");
return NULL;
}

tp_new = PyType_GetSlot(&PyLong_Type, 0);
if (tp_new != NULL) {
PyErr_SetString(PyExc_AssertionError, "mismatch: slot 0 of long");
return NULL;
}
if (PyErr_ExceptionMatches(PyExc_SystemError)) {
// This is the right exception
PyErr_Clear();
}
else {
return NULL;
}

Py_RETURN_NONE;
}


static PyObject *
get_args(PyObject *self, PyObject *args)
{
Expand Down Expand Up @@ -5627,8 +5683,10 @@ static PyMethodDef TestMethods[] = {
{"PyBuffer_SizeFromFormat", test_PyBuffer_SizeFromFormat, METH_VARARGS},
{"test_buildvalue_N", test_buildvalue_N, METH_NOARGS},
{"test_buildvalue_issue38913", test_buildvalue_issue38913, METH_NOARGS},
{"get_args", get_args, METH_VARARGS},
{"get_kwargs", (PyCFunction)(void(*)(void))get_kwargs, METH_VARARGS|METH_KEYWORDS},
{"get_args", get_args, METH_VARARGS},
{"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS},
{"get_kwargs", (PyCFunction)(void(*)(void))get_kwargs,
METH_VARARGS|METH_KEYWORDS},
{"getargs_tuple", getargs_tuple, METH_VARARGS},
{"getargs_keywords", (PyCFunction)(void(*)(void))getargs_keywords,
METH_VARARGS|METH_KEYWORDS},
Expand Down
48 changes: 40 additions & 8 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ static struct method_cache_entry method_cache[1 << MCACHE_SIZE_EXP];
static unsigned int next_version_tag = 0;
#endif

typedef struct PySlot_Offset {
short subslot_offset;
short slot_offset;
} PySlot_Offset;

#define MCACHE_STATS 0

#if MCACHE_STATS
Expand Down Expand Up @@ -2870,8 +2875,18 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
return NULL;
}

static const short slotoffsets[] = {
-1, /* invalid slot */
/* An array of type slot offsets corresponding to Py_tp_* constants,
* for use in e.g. PyType_Spec and PyType_GetSlot.
* Each entry has two offsets: "slot_offset" and "subslot_offset".
* If is subslot_offset is -1, slot_offset is an offset within the
* PyTypeObject struct.
* Otherwise slot_offset is an offset to a pointer to a sub-slots struct
* (such as "tp_as_number"), and subslot_offset is the offset within
* that struct.
* The actual table is generated by a script.
*/
static const PySlot_Offset pyslot_offsets[] = {
{0, 0},
#include "typeslots.inc"
};

Expand All @@ -2892,6 +2907,7 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
const PyType_Slot *slot;
Py_ssize_t nmembers, weaklistoffset, dictoffset, vectorcalloffset;
char *res_start;
short slot_offset, subslot_offset;

nmembers = weaklistoffset = dictoffset = vectorcalloffset = 0;
for (slot = spec->slots; slot->slot; slot++) {
Expand Down Expand Up @@ -3001,7 +3017,7 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)

for (slot = spec->slots; slot->slot; slot++) {
if (slot->slot < 0
|| (size_t)slot->slot >= Py_ARRAY_LENGTH(slotoffsets)) {
|| (size_t)slot->slot >= Py_ARRAY_LENGTH(pyslot_offsets)) {
PyErr_SetString(PyExc_RuntimeError, "invalid slot offset");
goto fail;
}
Expand Down Expand Up @@ -3034,7 +3050,15 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
}
else {
/* Copy other slots directly */
*(void**)(res_start + slotoffsets[slot->slot]) = slot->pfunc;
PySlot_Offset slotoffsets = pyslot_offsets[slot->slot];
slot_offset = slotoffsets.slot_offset;
if (slotoffsets.subslot_offset == -1) {
*(void**)((char*)res_start + slot_offset) = slot->pfunc;
} else {
void *parent_slot = *(void**)((char*)res_start + slot_offset);
subslot_offset = slotoffsets.subslot_offset;
*(void**)((char*)parent_slot + subslot_offset) = slot->pfunc;
}
}
}
if (type->tp_dealloc == NULL) {
Expand Down Expand Up @@ -3117,15 +3141,23 @@ PyType_FromSpec(PyType_Spec *spec)
void *
PyType_GetSlot(PyTypeObject *type, int slot)
{
if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE) || slot < 0) {
void *parent_slot;
int slots_len = Py_ARRAY_LENGTH(pyslot_offsets);

if (slot <= 0 || slot >= slots_len) {
PyErr_BadInternalCall();
return NULL;
}
if ((size_t)slot >= Py_ARRAY_LENGTH(slotoffsets)) {
/* Extension module requesting slot from a future version */

parent_slot = *(void**)((char*)type + pyslot_offsets[slot].slot_offset);
if (parent_slot == NULL) {
return NULL;
}
return *(void**)(((char*)type) + slotoffsets[slot]);
/* Return slot directly if we have no sub slot. */
if (pyslot_offsets[slot].subslot_offset == -1) {
return parent_slot;
}
return *(void**)((char*)parent_slot + pyslot_offsets[slot].subslot_offset);
}

PyObject *
Expand Down
162 changes: 81 additions & 81 deletions Objects/typeslots.inc

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading