diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 3bdf45ad9b61f7..6d7f77f8508650 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1026,9 +1026,9 @@ type objects) *must* have the :attr:`ob_size` field. .. seealso:: "Safe object finalization" (:pep:`442`) -.. c:member:: PyObject* PyTypeObject.tp_cache +.. c:member:: PyObject* PyTypeObject.tp_defined_slots - Unused. Not inherited. Internal use only. + Not inherited. Internal use only. .. c:member:: PyObject* PyTypeObject.tp_subclasses diff --git a/Doc/includes/typestruct.h b/Doc/includes/typestruct.h index 9f47899a198e01..935ed3649446ff 100644 --- a/Doc/includes/typestruct.h +++ b/Doc/includes/typestruct.h @@ -67,7 +67,7 @@ typedef struct _typeobject { inquiry tp_is_gc; /* For PyObject_IS_GC */ PyObject *tp_bases; PyObject *tp_mro; /* method resolution order */ - PyObject *tp_cache; + PyObject *tp_defined_slots; PyObject *tp_subclasses; PyObject *tp_weaklist; destructor tp_del; diff --git a/Include/object.h b/Include/object.h index 7db5bfea615e98..27549796b48719 100644 --- a/Include/object.h +++ b/Include/object.h @@ -414,7 +414,7 @@ typedef struct _typeobject { inquiry tp_is_gc; /* For PyObject_IS_GC */ PyObject *tp_bases; PyObject *tp_mro; /* method resolution order */ - PyObject *tp_cache; + PyObject *tp_defined_slots; /* internal cache; changed in version 3.7 */ PyObject *tp_subclasses; PyObject *tp_weaklist; destructor tp_del; diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-12-19-22-24-36.bpo-32346.Pfuszf.rst b/Misc/NEWS.d/next/Core and Builtins/2017-12-19-22-24-36.bpo-32346.Pfuszf.rst new file mode 100644 index 00000000000000..9467aa9b5b9925 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2017-12-19-22-24-36.bpo-32346.Pfuszf.rst @@ -0,0 +1 @@ +Speed up slot lookup during class creation diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index d7e82b9dba1ac2..8e24296c1dc471 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -2374,7 +2374,7 @@ PyTypeObject PyBufferedIOBase_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ @@ -2466,7 +2466,7 @@ PyTypeObject PyBufferedReader_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ @@ -2553,7 +2553,7 @@ PyTypeObject PyBufferedWriter_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ @@ -2632,7 +2632,7 @@ PyTypeObject PyBufferedRWPair_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ @@ -2727,7 +2727,7 @@ PyTypeObject PyBufferedRandom_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 269142cfee5242..afcd861c7b17f1 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -1231,7 +1231,7 @@ PyTypeObject PyFileIO_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index bcefb3862c3200..be090e9cb79e7a 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -831,7 +831,7 @@ PyTypeObject PyIOBase_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ @@ -1026,7 +1026,7 @@ PyTypeObject PyRawIOBase_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index d776b5de69910d..09c11df09e952a 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -207,7 +207,7 @@ PyTypeObject PyTextIOBase_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ @@ -3202,7 +3202,7 @@ PyTypeObject PyTextIOWrapper_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 30d1c767afcf74..70a8c7e1180f62 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -1155,7 +1155,7 @@ PyTypeObject PyWindowsConsoleIO_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 47b79fcc798509..b15865d3801118 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -12141,7 +12141,7 @@ static PyTypeObject ScandirIteratorType = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index d52d9db743a67f..c28acb0e157463 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -4925,7 +4925,7 @@ static PyTypeObject sock_type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ diff --git a/Objects/genobject.c b/Objects/genobject.c index 00a882379fcabe..92b65e1a925b93 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -807,7 +807,7 @@ PyTypeObject PyGen_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ @@ -1062,7 +1062,7 @@ PyTypeObject PyCoro_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ @@ -1407,7 +1407,7 @@ PyTypeObject PyAsyncGen_Type = { 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ - 0, /* tp_cache */ + 0, /* tp_defined_slots */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index e570c4145dfd89..dc6d83c1d2d840 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -253,6 +253,8 @@ PyType_Modified(PyTypeObject *type) PyObject *raw, *ref; Py_ssize_t i; + Py_CLEAR(type->tp_defined_slots); + if (!PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) return; @@ -3268,8 +3270,8 @@ type_dealloc(PyTypeObject *type) Py_XDECREF(type->tp_dict); Py_XDECREF(type->tp_bases); Py_XDECREF(type->tp_mro); - Py_XDECREF(type->tp_cache); Py_XDECREF(type->tp_subclasses); + Py_XDECREF(type->tp_defined_slots); /* A type's tp_doc is heap allocated, unlike the tp_doc slots * of most other objects. It's okay to cast it to char *. */ @@ -3462,7 +3464,7 @@ type_traverse(PyTypeObject *type, visitproc visit, void *arg) } Py_VISIT(type->tp_dict); - Py_VISIT(type->tp_cache); + Py_VISIT(type->tp_defined_slots); Py_VISIT(type->tp_mro); Py_VISIT(type->tp_bases); Py_VISIT(type->tp_base); @@ -3493,9 +3495,6 @@ type_clear(PyTypeObject *type) tp_clear handler). None of the other fields need to be cleared, and here's why: - tp_cache: - Not used; if it were, it would be a dict. - tp_bases, tp_base: If these are involved in a cycle, there must be at least one other, mutable object in the cycle, e.g. a base @@ -3518,6 +3517,7 @@ type_clear(PyTypeObject *type) if (type->tp_dict) PyDict_Clear(type->tp_dict); Py_CLEAR(type->tp_mro); + Py_CLEAR(type->tp_defined_slots); return 0; } @@ -6955,6 +6955,244 @@ static slotdef slotdefs[] = { {NULL} }; +#define N_SLOTDEFS (sizeof(slotdefs) / sizeof(slotdef) - 1) + +/* A dict mapping __dunder__ names to their index in slotdefs[] */ +static PyObject *dunder_to_slotdef_index; + +/* Fast check for whether *nameobj* is a __dunder__ string */ +static inline int +is_dunder(PyObject *nameobj) +{ + if (!PyUnicode_IS_ASCII(nameobj)) + return 0; + Py_ssize_t len = PyUnicode_GET_LENGTH(nameobj); + Py_UCS1 *name = PyUnicode_1BYTE_DATA(nameobj); + return (len >= 5 && name[0] == '_' && name[1] == '_' && + name[len - 2] == '_' && name[len - 1] == '_'); +} + +static int slotdefs_initialized = 0; + +static void +init_slotdefs(void) +{ + slotdef *p; + + if (slotdefs_initialized == 2) + return; + + /* First slotdefs initialization */ + if (slotdefs_initialized == 0) { + /* Add interned string objects for the names in slotdefs[]. */ + for (p = slotdefs; p->name; p++) { + /* Slots must be ordered by their offset in the PyHeapTypeObject. */ + assert(!p[1].name || p->offset <= p[1].offset); + + p->name_strobj = PyUnicode_InternFromString(p->name); + if (!p->name_strobj || !PyUnicode_CHECK_INTERNED(p->name_strobj)) + Py_FatalError("Out of memory interning slotdef names"); + } + slotdefs_initialized = 1; + } + + /* Second slotdefs initialization: only if core runtime is available */ + if (_PyRuntime.core_initialized == 1) { + /* Build mapping of __dunder__ names to slotdef[] incides. */ + assert(dunder_to_slotdef_index == NULL); + dunder_to_slotdef_index = PyDict_New(); + if (dunder_to_slotdef_index == NULL) + goto error_indexing; + + for (p = slotdefs; p->name; p++) { + /* Slot name must be dunder, index it */ + assert(is_dunder(p->name_strobj)); + + PyObject *index = PyLong_FromSsize_t(p - slotdefs); + if (index == NULL) + goto error_indexing; + PyObject *lst = PyDict_GetItemWithError(dunder_to_slotdef_index, + p->name_strobj); + if (lst == NULL) { + if (PyErr_Occurred()) + goto error_indexing; + lst = PyList_New(0); + if (lst == NULL) + goto error_indexing; + if (PyDict_SetItem(dunder_to_slotdef_index, p->name_strobj, lst)) + goto error_indexing; + Py_DECREF(lst); + } + if (PyList_Append(lst, index)) + goto error_indexing; + Py_DECREF(index); + } + slotdefs_initialized = 2; + } + return; + +error_indexing: + Py_FatalError("Out of memory indexing slotdef names"); +} + +/* Undo init_slotdefs, releasing the interned strings. */ +static void clear_slotdefs(void) +{ + slotdef *p; + for (p = slotdefs; p->name; p++) { + Py_CLEAR(p->name_strobj); + } + Py_CLEAR(dunder_to_slotdef_index); + slotdefs_initialized = 0; +} + +/* Return a tuple of slots present in *type*'s dict. + * Each tuple element is a (value, indices) tuple where *indices* + * is a list of slot indices in *slotdefs* that should be initialized + * with the *value*. + */ +static PyObject * +get_tp_defined_slots(PyTypeObject *type) +{ + PyObject *lst, *res; + + if (type->tp_defined_slots != NULL) { + res = type->tp_defined_slots; + assert(PyTuple_CheckExact(res)); + Py_INCREF(res); + return type->tp_defined_slots; + } + + lst = PyList_New(0); + if (lst == NULL) { + return NULL; + } + + PyObject *dict, *key, *value; + Py_ssize_t pos = 0; + dict = type->tp_dict; + assert(dict && PyDict_Check(dict)); + + while (PyDict_Next(dict, &pos, &key, &value)) { + if (!PyUnicode_Check(key) || !is_dunder(key)) { + /* Not a __dunder__ */ + continue; + } + PyObject *indices = PyDict_GetItem(dunder_to_slotdef_index, key); + if (indices == NULL) { + /* Not a slot method */ + continue; + } + PyObject *tup = PyTuple_Pack(2, value, indices); + if (PyList_Append(lst, tup)) { + Py_DECREF(tup); + goto error; + } + Py_DECREF(tup); + } + res = PyList_AsTuple(lst); + Py_INCREF(res); + Py_DECREF(lst); + type->tp_defined_slots = res; + return res; + +error: + Py_DECREF(lst); + return NULL; +} + + +/* Look up all possible slots in type's MRO. This aggressively caches + * the list of slot methods on each MRO class, so that other subclasses + * can then simply iterate over the cached values. + * + * The `out` array (N_SLOTDEFS elements) is populated with a borrowed + * descriptor value for each slotdef, if found. + * + * Returns 0 if ok, -1 if either an error occurred or not enough of + * the runtime is initialized to use this optimization. + */ +static int +lookup_slotdefs_in_mro(PyTypeObject *type, PyObject **out) +{ + PyObject *mro, *tuples = NULL; + Py_ssize_t i, n; + int res = 0; + + init_slotdefs(); + + if (dunder_to_slotdef_index == NULL) { + /* Not fully initialized yet */ + return -1; + } + mro = type->tp_mro; + if (mro == NULL) { + /* Don't bother with non-ready type (XXX: can happen?) */ + return -1; + } + assert(PyTuple_Check(mro)); + /* Keep a strong reference to mro because type->tp_mro can be replaced + during dict lookup, e.g. when comparing to non-string keys. */ + Py_INCREF(mro); + + memset(out, 0, sizeof(PyObject *) * N_SLOTDEFS); + + n = PyTuple_GET_SIZE(mro); + for (i = 0; i < n; i++) { + PyObject *base, *tuples; + + base = PyTuple_GET_ITEM(mro, i); + assert(PyType_Check(base)); + tuples = get_tp_defined_slots((PyTypeObject *) base); + if (tuples == NULL) { + res = (PyErr_Occurred() != NULL); + goto done; + } + + /* Examine all slot descriptors defined in this base's dict */ + Py_ssize_t ti, tn; + tn = PyTuple_GET_SIZE(tuples); + for (ti = 0; ti < tn; ti++) { + PyObject *tup = PyTuple_GET_ITEM(tuples, ti); + assert(PyTuple_CheckExact(tup)); + assert(PyTuple_GET_SIZE(tup) == 2); + + /* `value` is the descriptor value, `indices` is a list of + * indices into slotdefs[]. + */ + PyObject *value = PyTuple_GET_ITEM(tup, 0); + PyObject *indices = PyTuple_GET_ITEM(tup, 1); + assert(PyList_CheckExact(indices)); + /* Walk list of indices and populate `out` */ + Py_ssize_t li, ln; + ln = PyList_GET_SIZE(indices); + for (li = 0; li < ln; li++) { + PyObject *indexobj = PyList_GET_ITEM(indices, li); + assert(PyLong_CheckExact(indexobj)); + Py_ssize_t index = PyLong_AsSsize_t(indexobj); + if (index == -1 && PyErr_Occurred()) /* theoretically impossible? */ + goto error; + assert(index >= 0 && index < (Py_ssize_t) N_SLOTDEFS); + /* Set slot if not encountered earlier in the MRO */ + if (out[index] == NULL) { + out[index] = value; + } + } + } + Py_CLEAR(tuples); + } + res = 0; + goto done; + +error: + res = -1; + +done: + Py_CLEAR(tuples); + Py_DECREF(mro); + return res; +} + /* Given a type pointer and an offset gotten from a slotdef entry, return a pointer to the actual slot. This is not quite the same as simply adding the offset to the type pointer, since it takes care to indirect through the @@ -7041,30 +7279,39 @@ resolve_slotdups(PyTypeObject *type, PyObject *name) does some incredibly complex thinking and then sticks something into the slot. (It sees if the adjacent slotdefs for the same slot have conflicting interests, and then stores a generic wrapper or a specific function into - the slot.) Return a pointer to the next slotdef with a different offset, - because that's convenient for fixup_slot_dispatchers(). */ -static slotdef * -update_one_slot(PyTypeObject *type, slotdef *p) + the slot.) Return an index to the next slotdef with a different offset, + because that's convenient for fixup_slot_dispatchers(). */ +static Py_ssize_t +update_one_slot(PyTypeObject *type, Py_ssize_t i, slotdef *pp, PyObject **descrs) { + slotdef *p; PyObject *descr; PyWrapperDescrObject *d; void *generic = NULL, *specific = NULL; int use_generic = 0; - int offset = p->offset; + int offset = pp[i].offset; int error; void **ptr = slotptr(type, offset); if (ptr == NULL) { do { - ++p; - } while (p->offset == offset); - return p; + ++i; + } while (pp[i].offset == offset); } /* We may end up clearing live exceptions below, so make sure it's ours. */ assert(!PyErr_Occurred()); do { - /* Use faster uncached lookup as we won't get any cache hits during type setup. */ - descr = find_name_in_mro(type, p->name_strobj, &error); + assert(i >= 0 && i < (Py_ssize_t) N_SLOTDEFS); + p = &pp[i]; + if (descrs != NULL) { + /* Fastest method: use the descriptors array filled by lookup_slotdefs_in_mro() */ + error = 0; + descr = descrs[i]; + } + else { + /* Use uncached lookup as we won't get any cache hits during type setup. */ + descr = find_name_in_mro(type, p->name_strobj, &error); + } if (descr == NULL) { if (error == -1) { /* It is unlikely by not impossible that there has been an exception @@ -7126,12 +7373,12 @@ update_one_slot(PyTypeObject *type, slotdef *p) use_generic = 1; generic = p->function; } - } while ((++p)->offset == offset); + } while (pp[++i].offset == offset); if (specific && !use_generic) *ptr = specific; else *ptr = generic; - return p; + return i; } /* In the type, update the slots whose slotdefs are gathered in the pp array. @@ -7142,40 +7389,10 @@ update_slots_callback(PyTypeObject *type, void *data) slotdef **pp = (slotdef **)data; for (; *pp; pp++) - update_one_slot(type, *pp); + update_one_slot(type, 0, *pp, NULL); return 0; } -static int slotdefs_initialized = 0; -/* Initialize the slotdefs table by adding interned string objects for the - names. */ -static void -init_slotdefs(void) -{ - slotdef *p; - - if (slotdefs_initialized) - return; - for (p = slotdefs; p->name; p++) { - /* Slots must be ordered by their offset in the PyHeapTypeObject. */ - assert(!p[1].name || p->offset <= p[1].offset); - p->name_strobj = PyUnicode_InternFromString(p->name); - if (!p->name_strobj || !PyUnicode_CHECK_INTERNED(p->name_strobj)) - Py_FatalError("Out of memory interning slotdef names"); - } - slotdefs_initialized = 1; -} - -/* Undo init_slotdefs, releasing the interned strings. */ -static void clear_slotdefs(void) -{ - slotdef *p; - for (p = slotdefs; p->name; p++) { - Py_CLEAR(p->name_strobj); - } - slotdefs_initialized = 0; -} - /* Update the slots after assignment to a class (type) attribute. */ static int update_slot(PyTypeObject *type, PyObject *name) @@ -7221,11 +7438,27 @@ update_slot(PyTypeObject *type, PyObject *name) static void fixup_slot_dispatchers(PyTypeObject *type) { - slotdef *p; + slotdef *p = slotdefs; + Py_ssize_t i; + PyObject *stack_descrs[N_SLOTDEFS]; + PyObject **descrs = NULL; init_slotdefs(); - for (p = slotdefs; p->name; ) - p = update_one_slot(type, p); + + /* Try fast descriptor lookup to speed up update_one_slot() */ + if (lookup_slotdefs_in_mro(type, stack_descrs)) { + if (PyErr_Occurred()) { + /* XXX none of the functions here are able to return an error */ + PyErr_WriteUnraisable((PyObject *) type); + } + descrs = NULL; + } + else { + descrs = stack_descrs; + } + + for (i = 0; p[i].name; ) + i = update_one_slot(type, i, p, descrs); } static void @@ -7350,6 +7583,35 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *name, return 0; } + +/* Add a method descriptor to the type's tp_dict for the given + * slot definition and function pointer. + * See add_operators() below for more explanation. + */ +static int +add_one_operator(PyTypeObject *type, slotdef *p, void *ptr) +{ + PyObject *dict = type->tp_dict; + if (ptr == (void *)PyObject_HashNotImplemented) { + /* Classes may prevent the inheritance of the tp_hash + slot by storing PyObject_HashNotImplemented in it. Make it + visible as a None value for the __hash__ attribute. */ + if (PyDict_SetItem(dict, p->name_strobj, Py_None) < 0) + return -1; + } + else { + PyObject *descr = PyDescr_NewWrapper(type, p, ptr); + if (descr == NULL) + return -1; + if (PyDict_SetItem(dict, p->name_strobj, descr) < 0) { + Py_DECREF(descr); + return -1; + } + Py_DECREF(descr); + } + return 0; +} + /* This function is called by PyType_Ready() to populate the type's dictionary with method descriptors for function slots. For each function slot (like tp_repr) that's defined in the type, one or more @@ -7385,36 +7647,49 @@ add_operators(PyTypeObject *type) { PyObject *dict = type->tp_dict; slotdef *p; - PyObject *descr; void **ptr; + PyObject *stack_descrs[N_SLOTDEFS]; init_slotdefs(); - for (p = slotdefs; p->name; p++) { - if (p->wrapper == NULL) - continue; - ptr = slotptr(type, p->offset); - if (!ptr || !*ptr) - continue; - if (PyDict_GetItem(dict, p->name_strobj)) - continue; - if (*ptr == (void *)PyObject_HashNotImplemented) { - /* Classes may prevent the inheritance of the tp_hash - slot by storing PyObject_HashNotImplemented in it. Make it - visible as a None value for the __hash__ attribute. */ - if (PyDict_SetItem(dict, p->name_strobj, Py_None) < 0) + + if (!lookup_slotdefs_in_mro(type, stack_descrs)) { + /* Faster implementation avoiding a dict lookup for every possible + * slot wrapper. + */ + Py_ssize_t i; + for (i = 0; i < (Py_ssize_t) N_SLOTDEFS; i++) { + /* Does entry already exist in tp_dict? */ + if (stack_descrs[i]) { + continue; + } + p = &slotdefs[i]; + ptr = slotptr(type, p->offset); + if (!ptr || !*ptr) + continue; + if (add_one_operator(type, p, *ptr)) return -1; } - else { - descr = PyDescr_NewWrapper(type, p, *ptr); - if (descr == NULL) - return -1; - if (PyDict_SetItem(dict, p->name_strobj, descr) < 0) { - Py_DECREF(descr); + } + else { + /* Fallback in case the runtime is not fully initialized. */ + if (PyErr_Occurred()) { + return -1; + } + for (p = slotdefs; p->name; p++) { + if (p->wrapper == NULL) + continue; + ptr = slotptr(type, p->offset); + if (!ptr || !*ptr) + continue; + /* Does entry already exist in tp_dict? */ + if (PyDict_GetItem(dict, p->name_strobj)) + continue; + /* If not, add method descriptor */ + if (add_one_operator(type, p, *ptr)) return -1; - } - Py_DECREF(descr); } } + if (type->tp_new != NULL) { if (add_tp_new_wrapper(type) < 0) return -1;