Skip to content

Commit

Permalink
bpo-41073: PyType_GetSlot() can now accept static types. (pythonGH-21931
Browse files Browse the repository at this point in the history
)

PyType_GetSlot() can now accept static types.

Co-Authored-By: Petr Viktorin <encukou@gmail.com>

Automerge-Triggered-By: GH:encukou
  • Loading branch information
shihai1991 committed Nov 10, 2020
1 parent ace3f9a commit a13b26c
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 101 deletions.
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

0 comments on commit a13b26c

Please sign in to comment.