bpo-38525: Fix a segmentation fault when using reverse iterators of empty dict (GH-16846)

The reverse iterator for empty dictionaries was not handling correctly shared-key dictionaries.
This commit is contained in:
Dong-hee Na 2019-10-20 05:01:08 +09:00 committed by Pablo Galindo
parent 88eeda6311
commit 24dc2f8c56
3 changed files with 34 additions and 2 deletions

View File

@ -1312,6 +1312,31 @@ class DictTest(unittest.TestCase):
self.assertEqual(list(r), list('dcba'))
self.assertRaises(StopIteration, next, r)
def test_reverse_iterator_for_empty_dict(self):
# bpo-38525: revered iterator should work properly
# empty dict is directly used for reference count test
self.assertEqual(list(reversed({})), [])
self.assertEqual(list(reversed({}.items())), [])
self.assertEqual(list(reversed({}.values())), [])
self.assertEqual(list(reversed({}.keys())), [])
# dict() and {} don't trigger the same code path
self.assertEqual(list(reversed(dict())), [])
self.assertEqual(list(reversed(dict().items())), [])
self.assertEqual(list(reversed(dict().values())), [])
self.assertEqual(list(reversed(dict().keys())), [])
def test_reverse_iterator_for_shared_shared_dicts(self):
class A:
def __init__(self, x, y):
if x: self.x = x
if y: self.y = y
self.assertEqual(list(reversed(A(1, 2).__dict__)), ['y', 'x'])
self.assertEqual(list(reversed(A(1, 0).__dict__)), ['x'])
self.assertEqual(list(reversed(A(0, 1).__dict__)), ['y'])
def test_dict_copy_order(self):
# bpo-34320
od = collections.OrderedDict([('a', 1), ('b', 2)])

View File

@ -0,0 +1,2 @@
Fix a segmentation fault when using reverse iterators of empty ``dict`` objects.
Patch by Dong-hee Na and Inada Naoki.

View File

@ -3452,10 +3452,15 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
di->di_dict = dict;
di->di_used = dict->ma_used;
di->len = dict->ma_used;
if ((itertype == &PyDictRevIterKey_Type ||
if (itertype == &PyDictRevIterKey_Type ||
itertype == &PyDictRevIterItem_Type ||
itertype == &PyDictRevIterValue_Type) && dict->ma_used) {
itertype == &PyDictRevIterValue_Type) {
if (dict->ma_values) {
di->di_pos = dict->ma_used - 1;
}
else {
di->di_pos = dict->ma_keys->dk_nentries - 1;
}
}
else {
di->di_pos = 0;