Skip to content

Commit f73062a

Browse files
GH-137759: Limit _PyObject_HashFast to dict keys
1 parent 47b7dc7 commit f73062a

5 files changed

Lines changed: 33 additions & 28 deletions

File tree

Include/internal/pycore_object.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -830,9 +830,14 @@ _PyObject_IS_GC(PyObject *obj)
830830
&& (type->tp_is_gc == NULL || type->tp_is_gc(obj)));
831831
}
832832

833-
// Fast inlined version of PyObject_Hash()
834-
static inline Py_hash_t
835-
_PyObject_HashFast(PyObject *op)
833+
// Fast inlined version of PyObject_Hash(). Dictionaries are very
834+
// likely to include string keys (class and instance attributes,
835+
// json, ...) so we include a fast path for strings.
836+
// This function should not be used in a collection if str is not
837+
// very likely, since it is slower than PyObject_Hash() on types
838+
// other than str. See gh-137759.
839+
static inline Py_ALWAYS_INLINE Py_hash_t
840+
_PyObject_HashDictKey(PyObject *op)
836841
{
837842
if (PyUnicode_CheckExact(op)) {
838843
Py_hash_t hash = PyUnstable_Unicode_GET_CACHED_HASH(op);

Modules/_collectionsmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2590,7 +2590,7 @@ _collections__count_elements_impl(PyObject *module, PyObject *mapping,
25902590
if (key == NULL)
25912591
break;
25922592

2593-
hash = _PyObject_HashFast(key);
2593+
hash = _PyObject_HashDictKey(key);
25942594
if (hash == -1) {
25952595
goto done;
25962596
}

Objects/dictobject.c

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2384,7 +2384,7 @@ dict_getitem(PyObject *op, PyObject *key, const char *warnmsg)
23842384
}
23852385
PyDictObject *mp = (PyDictObject *)op;
23862386

2387-
Py_hash_t hash = _PyObject_HashFast(key);
2387+
Py_hash_t hash = _PyObject_HashDictKey(key);
23882388
if (hash == -1) {
23892389
PyErr_FormatUnraisable(warnmsg);
23902390
return NULL;
@@ -2456,7 +2456,7 @@ _PyDict_LookupIndexAndValue(PyDictObject *mp, PyObject *key, PyObject **value)
24562456
assert(PyDict_CheckExact((PyObject*)mp));
24572457
assert(PyUnicode_CheckExact(key));
24582458

2459-
Py_hash_t hash = _PyObject_HashFast(key);
2459+
Py_hash_t hash = _PyObject_HashDictKey(key);
24602460
if (hash == -1) {
24612461
dict_unhashable_type((PyObject*)mp, key);
24622462
return -1;
@@ -2560,7 +2560,7 @@ PyDict_GetItemRef(PyObject *op, PyObject *key, PyObject **result)
25602560
return -1;
25612561
}
25622562

2563-
Py_hash_t hash = _PyObject_HashFast(key);
2563+
Py_hash_t hash = _PyObject_HashDictKey(key);
25642564
if (hash == -1) {
25652565
dict_unhashable_type(op, key);
25662566
*result = NULL;
@@ -2576,7 +2576,7 @@ _PyDict_GetItemRef_Unicode_LockHeld(PyDictObject *op, PyObject *key, PyObject **
25762576
ASSERT_DICT_LOCKED(op);
25772577
assert(PyUnicode_CheckExact(key));
25782578

2579-
Py_hash_t hash = _PyObject_HashFast(key);
2579+
Py_hash_t hash = _PyObject_HashDictKey(key);
25802580
if (hash == -1) {
25812581
dict_unhashable_type((PyObject*)op, key);
25822582
*result = NULL;
@@ -2614,7 +2614,7 @@ PyDict_GetItemWithError(PyObject *op, PyObject *key)
26142614
PyErr_BadInternalCall();
26152615
return NULL;
26162616
}
2617-
hash = _PyObject_HashFast(key);
2617+
hash = _PyObject_HashDictKey(key);
26182618
if (hash == -1) {
26192619
dict_unhashable_type(op, key);
26202620
return NULL;
@@ -2673,7 +2673,7 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
26732673
Py_hash_t hash;
26742674
PyObject *value;
26752675

2676-
hash = _PyObject_HashFast(key);
2676+
hash = _PyObject_HashDictKey(key);
26772677
if (hash == -1) {
26782678
return NULL;
26792679
}
@@ -2697,7 +2697,7 @@ _PyDict_LoadGlobalStackRef(PyDictObject *globals, PyDictObject *builtins, PyObje
26972697
Py_ssize_t ix;
26982698
Py_hash_t hash;
26992699

2700-
hash = _PyObject_HashFast(key);
2700+
hash = _PyObject_HashDictKey(key);
27012701
if (hash == -1) {
27022702
*res = PyStackRef_NULL;
27032703
return;
@@ -2774,7 +2774,7 @@ setitem_take2_lock_held_known_hash(PyDictObject *mp, PyObject *key, PyObject *va
27742774
static int
27752775
setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value)
27762776
{
2777-
Py_hash_t hash = _PyObject_HashFast(key);
2777+
Py_hash_t hash = _PyObject_HashDictKey(key);
27782778
if (hash == -1) {
27792779
dict_unhashable_type((PyObject*)mp, key);
27802780
Py_DECREF(key);
@@ -2952,7 +2952,7 @@ int
29522952
PyDict_DelItem(PyObject *op, PyObject *key)
29532953
{
29542954
assert(key);
2955-
Py_hash_t hash = _PyObject_HashFast(key);
2955+
Py_hash_t hash = _PyObject_HashDictKey(key);
29562956
if (hash == -1) {
29572957
dict_unhashable_type(op, key);
29582958
return -1;
@@ -3296,7 +3296,7 @@ pop_lock_held(PyObject *op, PyObject *key, PyObject **result)
32963296
return 0;
32973297
}
32983298

3299-
Py_hash_t hash = _PyObject_HashFast(key);
3299+
Py_hash_t hash = _PyObject_HashDictKey(key);
33003300
if (hash == -1) {
33013301
dict_unhashable_type(op, key);
33023302
if (result) {
@@ -3734,7 +3734,7 @@ _PyDict_SubscriptKnownHash(PyObject *self, PyObject *key, Py_hash_t hash)
37343734
PyObject *
37353735
_PyDict_Subscript(PyObject *self, PyObject *key)
37363736
{
3737-
Py_hash_t hash = _PyObject_HashFast(key);
3737+
Py_hash_t hash = _PyObject_HashDictKey(key);
37383738
if (hash == -1) {
37393739
dict_unhashable_type(self, key);
37403740
return NULL;
@@ -4686,7 +4686,7 @@ dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value)
46864686
Py_hash_t hash;
46874687
Py_ssize_t ix;
46884688

4689-
hash = _PyObject_HashFast(key);
4689+
hash = _PyObject_HashDictKey(key);
46904690
if (hash == -1) {
46914691
dict_unhashable_type((PyObject*)self, key);
46924692
return NULL;
@@ -4723,7 +4723,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
47234723
Py_hash_t hash;
47244724
Py_ssize_t ix;
47254725

4726-
hash = _PyObject_HashFast(key);
4726+
hash = _PyObject_HashDictKey(key);
47274727
if (hash == -1) {
47284728
dict_unhashable_type(d, key);
47294729
if (result) {
@@ -5165,7 +5165,7 @@ static PyMethodDef mapp_methods[] = {
51655165
static int
51665166
dict_contains(PyObject *op, PyObject *key)
51675167
{
5168-
Py_hash_t hash = _PyObject_HashFast(key);
5168+
Py_hash_t hash = _PyObject_HashDictKey(key);
51695169
if (hash == -1) {
51705170
dict_unhashable_type(op, key);
51715171
return -1;
@@ -7298,7 +7298,7 @@ _PyDict_SetItem_LockHeld(PyDictObject *dict, PyObject *name, PyObject *value)
72987298
}
72997299

73007300
if (value == NULL) {
7301-
Py_hash_t hash = _PyObject_HashFast(name);
7301+
Py_hash_t hash = _PyObject_HashDictKey(name);
73027302
if (hash == -1) {
73037303
dict_unhashable_type((PyObject*)dict, name);
73047304
return -1;

Objects/setobject.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ set_unhashable_type(PyObject *key)
361361
int
362362
_PySet_AddTakeRef(PySetObject *so, PyObject *key)
363363
{
364-
Py_hash_t hash = _PyObject_HashFast(key);
364+
Py_hash_t hash = PyObject_Hash(key);
365365
if (hash == -1) {
366366
set_unhashable_type(key);
367367
Py_DECREF(key);
@@ -600,7 +600,7 @@ set_discard_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
600600
static int
601601
set_add_key(PySetObject *so, PyObject *key)
602602
{
603-
Py_hash_t hash = _PyObject_HashFast(key);
603+
Py_hash_t hash = PyObject_Hash(key);
604604
if (hash == -1) {
605605
set_unhashable_type(key);
606606
return -1;
@@ -611,7 +611,7 @@ set_add_key(PySetObject *so, PyObject *key)
611611
static int
612612
set_contains_key(PySetObject *so, PyObject *key)
613613
{
614-
Py_hash_t hash = _PyObject_HashFast(key);
614+
Py_hash_t hash = PyObject_Hash(key);
615615
if (hash == -1) {
616616
set_unhashable_type(key);
617617
return -1;
@@ -622,7 +622,7 @@ set_contains_key(PySetObject *so, PyObject *key)
622622
static int
623623
set_discard_key(PySetObject *so, PyObject *key)
624624
{
625-
Py_hash_t hash = _PyObject_HashFast(key);
625+
Py_hash_t hash = PyObject_Hash(key);
626626
if (hash == -1) {
627627
set_unhashable_type(key);
628628
return -1;
@@ -2514,7 +2514,7 @@ _PySet_Contains(PySetObject *so, PyObject *key)
25142514
{
25152515
assert(so);
25162516

2517-
Py_hash_t hash = _PyObject_HashFast(key);
2517+
Py_hash_t hash = PyObject_Hash(key);
25182518
if (hash == -1) {
25192519
if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) {
25202520
set_unhashable_type(key);
@@ -2574,7 +2574,7 @@ static PyObject *
25742574
frozenset___contains___impl(PySetObject *so, PyObject *key)
25752575
/*[clinic end generated code: output=2301ed91bc3a6dd5 input=2f04922a98d8bab7]*/
25762576
{
2577-
Py_hash_t hash = _PyObject_HashFast(key);
2577+
Py_hash_t hash = PyObject_Hash(key);
25782578
if (hash == -1) {
25792579
if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) {
25802580
set_unhashable_type(key);
@@ -3039,7 +3039,7 @@ PySet_Contains(PyObject *anyset, PyObject *key)
30393039
}
30403040

30413041
PySetObject *so = (PySetObject *)anyset;
3042-
Py_hash_t hash = _PyObject_HashFast(key);
3042+
Py_hash_t hash = PyObject_Hash(key);
30433043
if (hash == -1) {
30443044
set_unhashable_type(key);
30453045
return -1;

Objects/typeobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3793,7 +3793,7 @@ solid_base(PyTypeObject *type)
37933793
// or when __bases__ is re-assigned. Since the slots are read without atomic
37943794
// operations and without locking, we can only safely update them while the
37953795
// world is stopped. However, with the world stopped, we are very limited on
3796-
// which APIs can be safely used. For example, calling _PyObject_HashFast()
3796+
// which APIs can be safely used. For example, calling _PyObject_HashDictKey()
37973797
// or _PyDict_GetItemRef_KnownHash() are not safe and can potentially cause
37983798
// deadlocks. Hashing can be re-entrant and _PyDict_GetItemRef_KnownHash can
37993799
// acquire a lock if the dictionary is not owned by the current thread, to
@@ -6134,7 +6134,7 @@ PyObject_GetItemData(PyObject *obj)
61346134
static int
61356135
find_name_in_mro(PyTypeObject *type, PyObject *name, _PyStackRef *out)
61366136
{
6137-
Py_hash_t hash = _PyObject_HashFast(name);
6137+
Py_hash_t hash = _PyObject_HashDictKey(name);
61386138
if (hash == -1) {
61396139
PyErr_Clear();
61406140
return -1;

0 commit comments

Comments
 (0)