Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6395241

Browse files
committed
list slotdefs in offset order rather than sorting them (closes #17610)
This means we can remove our usage of qsort() than relied on undefined behavior.
1 parent f8ffec0 commit 6395241

2 files changed

Lines changed: 101 additions & 112 deletions

File tree

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ What's New in Python 3.3.2?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #17610: Don't rely on non-standard behavior of the C qsort() function.
16+
1517
- Issue #17357: Add missing verbosity output when using -v/-vv.
1618

1719
Library

Objects/typeobject.c

Lines changed: 99 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -5667,15 +5667,16 @@ slot_tp_del(PyObject *self)
56675667
}
56685668

56695669

5670-
/* Table mapping __foo__ names to tp_foo offsets and slot_tp_foo wrapper
5671-
functions. The offsets here are relative to the 'PyHeapTypeObject'
5672-
structure, which incorporates the additional structures used for numbers,
5673-
sequences and mappings.
5674-
Note that multiple names may map to the same slot (e.g. __eq__,
5675-
__ne__ etc. all map to tp_richcompare) and one name may map to multiple
5676-
slots (e.g. __str__ affects tp_str as well as tp_repr). The table is
5677-
terminated with an all-zero entry. (This table is further initialized and
5678-
sorted in init_slotdefs() below.) */
5670+
/*
5671+
Table mapping __foo__ names to tp_foo offsets and slot_tp_foo wrapper functions.
5672+
5673+
The table is ordered by offsets relative to the 'PyHeapTypeObject' structure,
5674+
which incorporates the additional structures used for numbers, sequences and
5675+
mappings. Note that multiple names may map to the same slot (e.g. __eq__,
5676+
__ne__ etc. all map to tp_richcompare) and one name may map to multiple slots
5677+
(e.g. __str__ affects tp_str as well as tp_repr). The table is terminated with
5678+
an all-zero entry. (This table is further initialized in init_slotdefs().)
5679+
*/
56795680

56805681
typedef struct wrapperbase slotdef;
56815682

@@ -5725,43 +5726,53 @@ typedef struct wrapperbase slotdef;
57255726
"x." NAME "(y) <==> " DOC)
57265727

57275728
static slotdef slotdefs[] = {
5728-
SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc,
5729-
"x.__len__() <==> len(x)"),
5730-
/* Heap types defining __add__/__mul__ have sq_concat/sq_repeat == NULL.
5731-
The logic in abstract.c always falls back to nb_add/nb_multiply in
5732-
this case. Defining both the nb_* and the sq_* slots to call the
5733-
user-defined methods has unexpected side-effects, as shown by
5734-
test_descr.notimplemented() */
5735-
SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc,
5736-
"x.__add__(y) <==> x+y"),
5737-
SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc,
5738-
"x.__mul__(n) <==> x*n"),
5739-
SQSLOT("__rmul__", sq_repeat, NULL, wrap_indexargfunc,
5740-
"x.__rmul__(n) <==> n*x"),
5741-
SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item,
5742-
"x.__getitem__(y) <==> x[y]"),
5743-
SQSLOT("__setitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_setitem,
5744-
"x.__setitem__(i, y) <==> x[i]=y"),
5745-
SQSLOT("__delitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_delitem,
5746-
"x.__delitem__(y) <==> del x[y]"),
5747-
SQSLOT("__contains__", sq_contains, slot_sq_contains, wrap_objobjproc,
5748-
"x.__contains__(y) <==> y in x"),
5749-
SQSLOT("__iadd__", sq_inplace_concat, NULL,
5750-
wrap_binaryfunc, "x.__iadd__(y) <==> x+=y"),
5751-
SQSLOT("__imul__", sq_inplace_repeat, NULL,
5752-
wrap_indexargfunc, "x.__imul__(y) <==> x*=y"),
5753-
5754-
MPSLOT("__len__", mp_length, slot_mp_length, wrap_lenfunc,
5755-
"x.__len__() <==> len(x)"),
5756-
MPSLOT("__getitem__", mp_subscript, slot_mp_subscript,
5757-
wrap_binaryfunc,
5758-
"x.__getitem__(y) <==> x[y]"),
5759-
MPSLOT("__setitem__", mp_ass_subscript, slot_mp_ass_subscript,
5760-
wrap_objobjargproc,
5761-
"x.__setitem__(i, y) <==> x[i]=y"),
5762-
MPSLOT("__delitem__", mp_ass_subscript, slot_mp_ass_subscript,
5763-
wrap_delitem,
5764-
"x.__delitem__(y) <==> del x[y]"),
5729+
TPSLOT("__getattribute__", tp_getattr, NULL, NULL, ""),
5730+
TPSLOT("__getattr__", tp_getattr, NULL, NULL, ""),
5731+
TPSLOT("__setattr__", tp_setattr, NULL, NULL, ""),
5732+
TPSLOT("__delattr__", tp_setattr, NULL, NULL, ""),
5733+
TPSLOT("__repr__", tp_repr, slot_tp_repr, wrap_unaryfunc,
5734+
"x.__repr__() <==> repr(x)"),
5735+
TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc,
5736+
"x.__hash__() <==> hash(x)"),
5737+
FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call,
5738+
"x.__call__(...) <==> x(...)", PyWrapperFlag_KEYWORDS),
5739+
TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc,
5740+
"x.__str__() <==> str(x)"),
5741+
TPSLOT("__getattribute__", tp_getattro, slot_tp_getattr_hook,
5742+
wrap_binaryfunc, "x.__getattribute__('name') <==> x.name"),
5743+
TPSLOT("__getattr__", tp_getattro, slot_tp_getattr_hook, NULL, ""),
5744+
TPSLOT("__setattr__", tp_setattro, slot_tp_setattro, wrap_setattr,
5745+
"x.__setattr__('name', value) <==> x.name = value"),
5746+
TPSLOT("__delattr__", tp_setattro, slot_tp_setattro, wrap_delattr,
5747+
"x.__delattr__('name') <==> del x.name"),
5748+
TPSLOT("__lt__", tp_richcompare, slot_tp_richcompare, richcmp_lt,
5749+
"x.__lt__(y) <==> x<y"),
5750+
TPSLOT("__le__", tp_richcompare, slot_tp_richcompare, richcmp_le,
5751+
"x.__le__(y) <==> x<=y"),
5752+
TPSLOT("__eq__", tp_richcompare, slot_tp_richcompare, richcmp_eq,
5753+
"x.__eq__(y) <==> x==y"),
5754+
TPSLOT("__ne__", tp_richcompare, slot_tp_richcompare, richcmp_ne,
5755+
"x.__ne__(y) <==> x!=y"),
5756+
TPSLOT("__gt__", tp_richcompare, slot_tp_richcompare, richcmp_gt,
5757+
"x.__gt__(y) <==> x>y"),
5758+
TPSLOT("__ge__", tp_richcompare, slot_tp_richcompare, richcmp_ge,
5759+
"x.__ge__(y) <==> x>=y"),
5760+
TPSLOT("__iter__", tp_iter, slot_tp_iter, wrap_unaryfunc,
5761+
"x.__iter__() <==> iter(x)"),
5762+
TPSLOT("__next__", tp_iternext, slot_tp_iternext, wrap_next,
5763+
"x.__next__() <==> next(x)"),
5764+
TPSLOT("__get__", tp_descr_get, slot_tp_descr_get, wrap_descr_get,
5765+
"descr.__get__(obj[, type]) -> value"),
5766+
TPSLOT("__set__", tp_descr_set, slot_tp_descr_set, wrap_descr_set,
5767+
"descr.__set__(obj, value)"),
5768+
TPSLOT("__delete__", tp_descr_set, slot_tp_descr_set,
5769+
wrap_descr_delete, "descr.__delete__(obj)"),
5770+
FLSLOT("__init__", tp_init, slot_tp_init, (wrapperfunc)wrap_init,
5771+
"x.__init__(...) initializes x; "
5772+
"see help(type(x)) for signature",
5773+
PyWrapperFlag_KEYWORDS),
5774+
TPSLOT("__new__", tp_new, slot_tp_new, NULL, ""),
5775+
TPSLOT("__del__", tp_del, slot_tp_del, NULL, ""),
57655776

57665777
BINSLOT("__add__", nb_add, slot_nb_add,
57675778
"+"),
@@ -5808,8 +5819,6 @@ static slotdef slotdefs[] = {
58085819
"int(x)"),
58095820
UNSLOT("__float__", nb_float, slot_nb_float, wrap_unaryfunc,
58105821
"float(x)"),
5811-
NBSLOT("__index__", nb_index, slot_nb_index, wrap_unaryfunc,
5812-
"x[y:z] <==> x[y.__index__():z.__index__()]"),
58135822
IBSLOT("__iadd__", nb_inplace_add, slot_nb_inplace_add,
58145823
wrap_binaryfunc, "+="),
58155824
IBSLOT("__isub__", nb_inplace_subtract, slot_nb_inplace_subtract,
@@ -5838,54 +5847,47 @@ static slotdef slotdefs[] = {
58385847
slot_nb_inplace_floor_divide, wrap_binaryfunc, "//"),
58395848
IBSLOT("__itruediv__", nb_inplace_true_divide,
58405849
slot_nb_inplace_true_divide, wrap_binaryfunc, "/"),
5850+
NBSLOT("__index__", nb_index, slot_nb_index, wrap_unaryfunc,
5851+
"x[y:z] <==> x[y.__index__():z.__index__()]"),
5852+
5853+
MPSLOT("__len__", mp_length, slot_mp_length, wrap_lenfunc,
5854+
"x.__len__() <==> len(x)"),
5855+
MPSLOT("__getitem__", mp_subscript, slot_mp_subscript,
5856+
wrap_binaryfunc,
5857+
"x.__getitem__(y) <==> x[y]"),
5858+
MPSLOT("__setitem__", mp_ass_subscript, slot_mp_ass_subscript,
5859+
wrap_objobjargproc,
5860+
"x.__setitem__(i, y) <==> x[i]=y"),
5861+
MPSLOT("__delitem__", mp_ass_subscript, slot_mp_ass_subscript,
5862+
wrap_delitem,
5863+
"x.__delitem__(y) <==> del x[y]"),
5864+
5865+
SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc,
5866+
"x.__len__() <==> len(x)"),
5867+
/* Heap types defining __add__/__mul__ have sq_concat/sq_repeat == NULL.
5868+
The logic in abstract.c always falls back to nb_add/nb_multiply in
5869+
this case. Defining both the nb_* and the sq_* slots to call the
5870+
user-defined methods has unexpected side-effects, as shown by
5871+
test_descr.notimplemented() */
5872+
SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc,
5873+
"x.__add__(y) <==> x+y"),
5874+
SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc,
5875+
"x.__mul__(n) <==> x*n"),
5876+
SQSLOT("__rmul__", sq_repeat, NULL, wrap_indexargfunc,
5877+
"x.__rmul__(n) <==> n*x"),
5878+
SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item,
5879+
"x.__getitem__(y) <==> x[y]"),
5880+
SQSLOT("__setitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_setitem,
5881+
"x.__setitem__(i, y) <==> x[i]=y"),
5882+
SQSLOT("__delitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_delitem,
5883+
"x.__delitem__(y) <==> del x[y]"),
5884+
SQSLOT("__contains__", sq_contains, slot_sq_contains, wrap_objobjproc,
5885+
"x.__contains__(y) <==> y in x"),
5886+
SQSLOT("__iadd__", sq_inplace_concat, NULL,
5887+
wrap_binaryfunc, "x.__iadd__(y) <==> x+=y"),
5888+
SQSLOT("__imul__", sq_inplace_repeat, NULL,
5889+
wrap_indexargfunc, "x.__imul__(y) <==> x*=y"),
58415890

5842-
TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc,
5843-
"x.__str__() <==> str(x)"),
5844-
TPSLOT("__repr__", tp_repr, slot_tp_repr, wrap_unaryfunc,
5845-
"x.__repr__() <==> repr(x)"),
5846-
TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc,
5847-
"x.__hash__() <==> hash(x)"),
5848-
FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call,
5849-
"x.__call__(...) <==> x(...)", PyWrapperFlag_KEYWORDS),
5850-
TPSLOT("__getattribute__", tp_getattro, slot_tp_getattr_hook,
5851-
wrap_binaryfunc, "x.__getattribute__('name') <==> x.name"),
5852-
TPSLOT("__getattribute__", tp_getattr, NULL, NULL, ""),
5853-
TPSLOT("__getattr__", tp_getattro, slot_tp_getattr_hook, NULL, ""),
5854-
TPSLOT("__getattr__", tp_getattr, NULL, NULL, ""),
5855-
TPSLOT("__setattr__", tp_setattro, slot_tp_setattro, wrap_setattr,
5856-
"x.__setattr__('name', value) <==> x.name = value"),
5857-
TPSLOT("__setattr__", tp_setattr, NULL, NULL, ""),
5858-
TPSLOT("__delattr__", tp_setattro, slot_tp_setattro, wrap_delattr,
5859-
"x.__delattr__('name') <==> del x.name"),
5860-
TPSLOT("__delattr__", tp_setattr, NULL, NULL, ""),
5861-
TPSLOT("__lt__", tp_richcompare, slot_tp_richcompare, richcmp_lt,
5862-
"x.__lt__(y) <==> x<y"),
5863-
TPSLOT("__le__", tp_richcompare, slot_tp_richcompare, richcmp_le,
5864-
"x.__le__(y) <==> x<=y"),
5865-
TPSLOT("__eq__", tp_richcompare, slot_tp_richcompare, richcmp_eq,
5866-
"x.__eq__(y) <==> x==y"),
5867-
TPSLOT("__ne__", tp_richcompare, slot_tp_richcompare, richcmp_ne,
5868-
"x.__ne__(y) <==> x!=y"),
5869-
TPSLOT("__gt__", tp_richcompare, slot_tp_richcompare, richcmp_gt,
5870-
"x.__gt__(y) <==> x>y"),
5871-
TPSLOT("__ge__", tp_richcompare, slot_tp_richcompare, richcmp_ge,
5872-
"x.__ge__(y) <==> x>=y"),
5873-
TPSLOT("__iter__", tp_iter, slot_tp_iter, wrap_unaryfunc,
5874-
"x.__iter__() <==> iter(x)"),
5875-
TPSLOT("__next__", tp_iternext, slot_tp_iternext, wrap_next,
5876-
"x.__next__() <==> next(x)"),
5877-
TPSLOT("__get__", tp_descr_get, slot_tp_descr_get, wrap_descr_get,
5878-
"descr.__get__(obj[, type]) -> value"),
5879-
TPSLOT("__set__", tp_descr_set, slot_tp_descr_set, wrap_descr_set,
5880-
"descr.__set__(obj, value)"),
5881-
TPSLOT("__delete__", tp_descr_set, slot_tp_descr_set,
5882-
wrap_descr_delete, "descr.__delete__(obj)"),
5883-
FLSLOT("__init__", tp_init, slot_tp_init, (wrapperfunc)wrap_init,
5884-
"x.__init__(...) initializes x; "
5885-
"see help(type(x)) for signature",
5886-
PyWrapperFlag_KEYWORDS),
5887-
TPSLOT("__new__", tp_new, slot_tp_new, NULL, ""),
5888-
TPSLOT("__del__", tp_del, slot_tp_del, NULL, ""),
58895891
{NULL}
58905892
};
58915893

@@ -6066,21 +6068,6 @@ update_slots_callback(PyTypeObject *type, void *data)
60666068
return 0;
60676069
}
60686070

6069-
/* Comparison function for qsort() to compare slotdefs by their offset, and
6070-
for equal offset by their address (to force a stable sort). */
6071-
static int
6072-
slotdef_cmp(const void *aa, const void *bb)
6073-
{
6074-
const slotdef *a = (const slotdef *)aa, *b = (const slotdef *)bb;
6075-
int c = a->offset - b->offset;
6076-
if (c != 0)
6077-
return c;
6078-
else
6079-
/* Cannot use a-b, as this gives off_t,
6080-
which may lose precision when converted to int. */
6081-
return (a > b) ? 1 : (a < b) ? -1 : 0;
6082-
}
6083-
60846071
/* Initialize the slotdefs table by adding interned string objects for the
60856072
names and sorting the entries. */
60866073
static void
@@ -6092,12 +6079,12 @@ init_slotdefs(void)
60926079
if (initialized)
60936080
return;
60946081
for (p = slotdefs; p->name; p++) {
6082+
/* Slots must be ordered by their offset in the PyHeapTypeObject. */
6083+
assert(!p[1].name || p->offset <= p[1].offset);
60956084
p->name_strobj = PyUnicode_InternFromString(p->name);
60966085
if (!p->name_strobj)
60976086
Py_FatalError("Out of memory interning slotdef names");
60986087
}
6099-
qsort((void *)slotdefs, (size_t)(p-slotdefs), sizeof(slotdef),
6100-
slotdef_cmp);
61016088
initialized = 1;
61026089
}
61036090

0 commit comments

Comments
 (0)