summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorStefan Behnel <stefan_ml@behnel.de>2019-02-20 17:29:24 (GMT)
committerRaymond Hettinger <rhettinger@users.noreply.github.com>2019-02-20 17:29:24 (GMT)
commitd8b9e1fc2e45d2bc3f4a9737c375f2adb8a8c7de (patch)
tree670cea6dd725af6f1676cb8440d1f5e7d305f3bf /Objects
parentb5409dacc4885146a27d06482b346e55fa12d2ec (diff)
downloadcpython-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.c51
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 */