diff options
author | Stefan Behnel <stefan_ml@behnel.de> | 2019-02-20 17:29:24 (GMT) |
---|---|---|
committer | Raymond Hettinger <rhettinger@users.noreply.github.com> | 2019-02-20 17:29:24 (GMT) |
commit | d8b9e1fc2e45d2bc3f4a9737c375f2adb8a8c7de (patch) | |
tree | 670cea6dd725af6f1676cb8440d1f5e7d305f3bf /Objects | |
parent | b5409dacc4885146a27d06482b346e55fa12d2ec (diff) | |
download | cpython-d8b9e1fc2e45d2bc3f4a9737c375f2adb8a8c7de.zip cpython-d8b9e1fc2e45d2bc3f4a9737c375f2adb8a8c7de.tar.gz cpython-d8b9e1fc2e45d2bc3f4a9737c375f2adb8a8c7de.tar.bz2 |
bpo-36012: Avoid linear slot search for non-dunder methods (GH-11907)
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/typeobject.c | 51 |
1 files changed, 38 insertions, 13 deletions
diff --git a/Objects/typeobject.c b/Objects/typeobject.c index e6cf4fb..4234726 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3164,6 +3164,24 @@ _PyType_LookupId(PyTypeObject *type, struct _Py_Identifier *name) return _PyType_Lookup(type, oname); } +/* Check if the "readied" PyUnicode name + is a double-underscore special name. */ +static int +is_dunder_name(PyObject *name) +{ + Py_ssize_t length = PyUnicode_GET_LENGTH(name); + int kind = PyUnicode_KIND(name); + /* Special names contain at least "__x__" and are always ASCII. */ + if (length > 4 && kind == PyUnicode_1BYTE_KIND) { + Py_UCS1 *characters = PyUnicode_1BYTE_DATA(name); + return ( + ((characters[length-2] == '_') && (characters[length-1] == '_')) && + ((characters[0] == '_') && (characters[1] == '_')) + ); + } + return 0; +} + /* This is similar to PyObject_GenericGetAttr(), but uses _PyType_Lookup() instead of just looking in type->tp_dict. */ static PyObject * @@ -3275,12 +3293,14 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value) if (name == NULL) return -1; } - PyUnicode_InternInPlace(&name); if (!PyUnicode_CHECK_INTERNED(name)) { - PyErr_SetString(PyExc_MemoryError, - "Out of memory interning an attribute name"); - Py_DECREF(name); - return -1; + PyUnicode_InternInPlace(&name); + if (!PyUnicode_CHECK_INTERNED(name)) { + PyErr_SetString(PyExc_MemoryError, + "Out of memory interning an attribute name"); + Py_DECREF(name); + return -1; + } } } else { @@ -3289,7 +3309,16 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value) } res = _PyObject_GenericSetAttrWithDict((PyObject *)type, name, value, NULL); if (res == 0) { - res = update_slot(type, name); + /* Clear the VALID_VERSION flag of 'type' and all its + subclasses. This could possibly be unified with the + update_subclasses() recursion in update_slot(), but carefully: + they each have their own conditions on which to stop + recursing into subclasses. */ + PyType_Modified(type); + + if (is_dunder_name(name)) { + res = update_slot(type, name); + } assert(_PyType_CheckConsistency(type)); } Py_DECREF(name); @@ -7236,13 +7265,6 @@ update_slot(PyTypeObject *type, PyObject *name) assert(PyUnicode_CheckExact(name)); assert(PyUnicode_CHECK_INTERNED(name)); - /* Clear the VALID_VERSION flag of 'type' and all its - subclasses. This could possibly be unified with the - update_subclasses() recursion below, but carefully: - they each have their own conditions on which to stop - recursing into subclasses. */ - PyType_Modified(type); - init_slotdefs(); pp = ptrs; for (p = slotdefs; p->name; p++) { @@ -7281,6 +7303,9 @@ update_all_slots(PyTypeObject* type) { slotdef *p; + /* Clear the VALID_VERSION flag of 'type' and all its subclasses. */ + PyType_Modified(type); + init_slotdefs(); for (p = slotdefs; p->name; p++) { /* update_slot returns int but can't actually fail */ |