Skip to content

Commit 39e97ce

Browse files
committed
throw RuntimeError if dict/set size changes while iterating
1 parent 8e1672a commit 39e97ce

File tree

14 files changed

+112
-38
lines changed

14 files changed

+112
-38
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictBuiltins.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,8 @@ public abstract static class IterNode extends PythonUnaryBuiltinNode {
377377
Object run(PDict self,
378378
@Cached HashingCollectionNodes.GetDictStorageNode getStore,
379379
@CachedLibrary("getStore.execute(self)") HashingStorageLibrary lib) {
380-
return factory().createDictKeyIterator(lib.keys(getStore.execute(self)).iterator());
380+
HashingStorage storage = getStore.execute(self);
381+
return factory().createDictKeyIterator(lib.keys(storage).iterator(), storage, lib.length(storage));
381382
}
382383
}
383384

@@ -388,7 +389,8 @@ public abstract static class ReversedNode extends PythonUnaryBuiltinNode {
388389
Object run(PDict self,
389390
@Cached HashingCollectionNodes.GetDictStorageNode getStore,
390391
@CachedLibrary("getStore.execute(self)") HashingStorageLibrary lib) {
391-
return factory().createDictKeyIterator(lib.reverseKeys(getStore.execute(self)).iterator());
392+
HashingStorage storage = getStore.execute(self);
393+
return factory().createDictKeyIterator(lib.reverseKeys(storage).iterator(), storage, lib.length(storage));
392394
}
393395
}
394396

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictItemsIteratorBuiltins.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,13 @@
3333
import com.oracle.graal.python.builtins.Builtin;
3434
import com.oracle.graal.python.builtins.CoreFunctions;
3535
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
36+
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.RuntimeError;
3637
import com.oracle.graal.python.builtins.PythonBuiltins;
3738
import com.oracle.graal.python.builtins.objects.common.HashingStorage.DictEntry;
39+
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary;
3840
import com.oracle.graal.python.builtins.objects.dict.PDictView.PDictItemIterator;
3941
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
42+
import com.oracle.graal.python.nodes.ErrorMessages;
4043
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
4144
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
4245
import com.oracle.graal.python.runtime.exception.PythonErrorType;
@@ -45,6 +48,7 @@
4548
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
4649
import com.oracle.truffle.api.dsl.NodeFactory;
4750
import com.oracle.truffle.api.dsl.Specialization;
51+
import com.oracle.truffle.api.library.CachedLibrary;
4852
import com.oracle.truffle.api.profiles.ConditionProfile;
4953

5054
@CoreFunctions(extendClasses = {PythonBuiltinClassType.PDictItemIterator, PythonBuiltinClassType.PDictReverseItemIterator})
@@ -60,8 +64,13 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
6064
public abstract static class NextNode extends PythonUnaryBuiltinNode {
6165
@Specialization
6266
Object run(PDictItemIterator self,
63-
@Cached("createBinaryProfile()") ConditionProfile profile) {
67+
@Cached("createBinaryProfile()") ConditionProfile profile,
68+
@Cached("createBinaryProfile()") ConditionProfile sizeChanged,
69+
@CachedLibrary(limit = "1") HashingStorageLibrary storageLibrary) {
6470
if (profile.profile(hasNext(self))) {
71+
if (sizeChanged.profile(self.checkSizeChanged(storageLibrary))) {
72+
throw raise(RuntimeError, ErrorMessages.CHANGED_SIZE_DURING_ITERATION, "dictionary");
73+
}
6574
DictEntry value = next(self);
6675
return factory().createTuple(new Object[]{value.getKey(), value.getValue()});
6776
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictKeysIteratorBuiltins.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,20 @@
3333
import com.oracle.graal.python.builtins.Builtin;
3434
import com.oracle.graal.python.builtins.CoreFunctions;
3535
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
36+
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.RuntimeError;
3637
import com.oracle.graal.python.builtins.PythonBuiltins;
38+
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary;
3739
import com.oracle.graal.python.builtins.objects.dict.PDictView.PDictKeyIterator;
3840
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
41+
import com.oracle.graal.python.nodes.ErrorMessages;
3942
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
4043
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
4144
import com.oracle.graal.python.runtime.exception.PythonErrorType;
4245
import com.oracle.truffle.api.dsl.Cached;
4346
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
4447
import com.oracle.truffle.api.dsl.NodeFactory;
4548
import com.oracle.truffle.api.dsl.Specialization;
49+
import com.oracle.truffle.api.library.CachedLibrary;
4650
import com.oracle.truffle.api.profiles.ConditionProfile;
4751

4852
@CoreFunctions(extendClasses = {PythonBuiltinClassType.PDictKeyIterator, PythonBuiltinClassType.PDictReverseKeyIterator})
@@ -58,8 +62,13 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
5862
public abstract static class NextNode extends PythonUnaryBuiltinNode {
5963
@Specialization
6064
Object run(PDictKeyIterator self,
61-
@Cached("createBinaryProfile()") ConditionProfile profile) {
62-
if (profile.profile(self.getIterator().hasNext())) {
65+
@Cached("createBinaryProfile()") ConditionProfile hasNext,
66+
@Cached("createBinaryProfile()") ConditionProfile sizeChanged,
67+
@CachedLibrary(limit = "1") HashingStorageLibrary storageLibrary) {
68+
if (hasNext.profile(self.getIterator().hasNext())) {
69+
if (sizeChanged.profile(self.checkSizeChanged(storageLibrary))) {
70+
throw raise(RuntimeError, ErrorMessages.CHANGED_SIZE_DURING_ITERATION, "dictionary");
71+
}
6372
return self.getIterator().next();
6473
}
6574
throw raise(PythonErrorType.StopIteration);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictValuesBuiltins.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes;
4040
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
4141
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary;
42+
import com.oracle.graal.python.builtins.objects.common.PHashingCollection;
4243
import com.oracle.graal.python.builtins.objects.dict.PDictView.PDictValuesView;
4344
import static com.oracle.graal.python.nodes.SpecialMethodNames.__REVERSED__;
4445
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
@@ -77,7 +78,9 @@ public abstract static class IterNode extends PythonUnaryBuiltinNode {
7778
Object doPDictValuesView(PDictValuesView self,
7879
@Cached HashingCollectionNodes.GetDictStorageNode getStore,
7980
@CachedLibrary("getStore.execute(self.getWrappedDict())") HashingStorageLibrary lib) {
80-
return factory().createDictValueIterator(lib.values(getStore.execute(self.getWrappedDict())).iterator());
81+
PHashingCollection dict = self.getWrappedDict();
82+
HashingStorage storage = getStore.execute(dict);
83+
return factory().createDictValueIterator(lib.values(storage).iterator(), storage, lib.length(storage));
8184
}
8285
}
8386

@@ -88,7 +91,9 @@ public abstract static class ReversedNode extends PythonUnaryBuiltinNode {
8891
Object doPDictValuesView(PDictValuesView self,
8992
@Cached HashingCollectionNodes.GetDictStorageNode getStore,
9093
@CachedLibrary("getStore.execute(self.getWrappedDict())") HashingStorageLibrary lib) {
91-
return factory().createDictReverseValueIterator(lib.reverseValues(getStore.execute(self.getWrappedDict())).iterator());
94+
PHashingCollection dict = self.getWrappedDict();
95+
HashingStorage storage = getStore.execute(dict);
96+
return factory().createDictReverseValueIterator(lib.reverseValues(storage).iterator(), storage, lib.length(storage));
9297
}
9398
}
9499

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictValuesIteratorBuiltins.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,20 @@
3333
import com.oracle.graal.python.builtins.Builtin;
3434
import com.oracle.graal.python.builtins.CoreFunctions;
3535
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
36+
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.RuntimeError;
3637
import com.oracle.graal.python.builtins.PythonBuiltins;
38+
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary;
3739
import com.oracle.graal.python.builtins.objects.dict.PDictView.PDictValueIterator;
3840
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
41+
import com.oracle.graal.python.nodes.ErrorMessages;
3942
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
4043
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
4144
import com.oracle.graal.python.runtime.exception.PythonErrorType;
4245
import com.oracle.truffle.api.dsl.Cached;
4346
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
4447
import com.oracle.truffle.api.dsl.NodeFactory;
4548
import com.oracle.truffle.api.dsl.Specialization;
49+
import com.oracle.truffle.api.library.CachedLibrary;
4650
import com.oracle.truffle.api.profiles.ConditionProfile;
4751

4852
@CoreFunctions(extendClasses = {PythonBuiltinClassType.PDictValueIterator, PythonBuiltinClassType.PDictReverseValueIterator})
@@ -58,8 +62,13 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
5862
public abstract static class NextNode extends PythonUnaryBuiltinNode {
5963
@Specialization
6064
Object run(PDictValueIterator self,
61-
@Cached("createBinaryProfile()") ConditionProfile profile) {
65+
@Cached("createBinaryProfile()") ConditionProfile profile,
66+
@Cached("createBinaryProfile()") ConditionProfile sizeChanged,
67+
@CachedLibrary(limit = "1") HashingStorageLibrary storageLibrary) {
6268
if (profile.profile(self.getIterator().hasNext())) {
69+
if (sizeChanged.profile(self.checkSizeChanged(storageLibrary))) {
70+
throw raise(RuntimeError, ErrorMessages.CHANGED_SIZE_DURING_ITERATION, "dictionary");
71+
}
6372
return self.getIterator().next();
6473
}
6574
throw raise(PythonErrorType.StopIteration);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictViewBuiltins.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes;
6666
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
6767
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary;
68+
import com.oracle.graal.python.builtins.objects.common.PHashingCollection;
6869
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
6970
import com.oracle.graal.python.builtins.objects.dict.PDictView.PDictItemsView;
7071
import com.oracle.graal.python.builtins.objects.dict.PDictView.PDictKeysView;
@@ -123,14 +124,18 @@ public abstract static class IterNode extends PythonUnaryBuiltinNode {
123124
Object getKeysViewIter(PDictKeysView self,
124125
@Cached HashingCollectionNodes.GetDictStorageNode getStore,
125126
@CachedLibrary("getStore.execute(self.getWrappedDict())") HashingStorageLibrary lib) {
126-
return factory().createDictKeyIterator(lib.keys(getStore.execute(self.getWrappedDict())).iterator());
127+
PHashingCollection dict = self.getWrappedDict();
128+
HashingStorage storage = getStore.execute(dict);
129+
return factory().createDictKeyIterator(lib.keys(storage).iterator(), storage, lib.length(storage));
127130
}
128131

129132
@Specialization(limit = "getCallSiteInlineCacheMaxDepth()")
130133
Object getItemsViewIter(PDictItemsView self,
131134
@Cached HashingCollectionNodes.GetDictStorageNode getStore,
132135
@CachedLibrary("getStore.execute(self.getWrappedDict())") HashingStorageLibrary lib) {
133-
return factory().createDictItemIterator(lib.entries(getStore.execute(self.getWrappedDict())).iterator());
136+
PHashingCollection dict = self.getWrappedDict();
137+
HashingStorage storage = getStore.execute(dict);
138+
return factory().createDictItemIterator(lib.entries(storage).iterator(), storage, lib.length(storage));
134139
}
135140
}
136141

@@ -141,14 +146,18 @@ public abstract static class ReversedNode extends PythonUnaryBuiltinNode {
141146
Object getReversedKeysViewIter(PDictKeysView self,
142147
@Cached HashingCollectionNodes.GetDictStorageNode getStore,
143148
@CachedLibrary("getStore.execute(self.getWrappedDict())") HashingStorageLibrary lib) {
144-
return factory().createDictReverseKeyIterator(lib.reverseKeys(getStore.execute(self.getWrappedDict())).iterator());
149+
PHashingCollection dict = self.getWrappedDict();
150+
HashingStorage storage = getStore.execute(dict);
151+
return factory().createDictReverseKeyIterator(lib.reverseKeys(storage).iterator(), storage, lib.length(storage));
145152
}
146153

147154
@Specialization(limit = "getCallSiteInlineCacheMaxDepth()")
148155
Object getReversedItemsViewIter(PDictItemsView self,
149156
@Cached HashingCollectionNodes.GetDictStorageNode getStore,
150157
@CachedLibrary("getStore.execute(self.getWrappedDict())") HashingStorageLibrary lib) {
151-
return factory().createDictReverseItemIterator(lib.reverseEntries(getStore.execute(self.getWrappedDict())).iterator());
158+
PHashingCollection dict = self.getWrappedDict();
159+
HashingStorage storage = getStore.execute(dict);
160+
return factory().createDictReverseItemIterator(lib.reverseEntries(storage).iterator(), storage, lib.length(storage));
152161
}
153162
}
154163

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/PDictView.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
*/
4141
package com.oracle.graal.python.builtins.objects.dict;
4242

43+
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
4344
import com.oracle.graal.python.builtins.objects.common.HashingStorage.DictEntry;
4445
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary.HashingStorageIterator;
4546
import com.oracle.graal.python.builtins.objects.common.PHashingCollection;
@@ -71,8 +72,8 @@ public int size() {
7172
//
7273
// -----------------------------------------------------------------------------------------------------------------
7374
public static final class PDictKeyIterator extends PHashingStorageIterator<Object> {
74-
public PDictKeyIterator(Object clazz, DynamicObject storage, HashingStorageIterator<Object> iterator) {
75-
super(clazz, storage, iterator);
75+
public PDictKeyIterator(Object clazz, DynamicObject storage, HashingStorageIterator<Object> iterator, HashingStorage hashingStorage, int initialSize) {
76+
super(clazz, storage, iterator, hashingStorage, initialSize);
7677
}
7778
}
7879

@@ -89,8 +90,8 @@ public PDictKeysView(Object clazz, DynamicObject storage, PHashingCollection dic
8990
//
9091
// -----------------------------------------------------------------------------------------------------------------
9192
public static final class PDictValueIterator extends PHashingStorageIterator<Object> {
92-
public PDictValueIterator(Object clazz, DynamicObject storage, HashingStorageIterator<Object> iterator) {
93-
super(clazz, storage, iterator);
93+
public PDictValueIterator(Object clazz, DynamicObject storage, HashingStorageIterator<Object> iterator, HashingStorage hashingStorage, int initialSize) {
94+
super(clazz, storage, iterator, hashingStorage, initialSize);
9495
}
9596
}
9697

@@ -107,8 +108,8 @@ public PDictValuesView(Object clazz, DynamicObject storage, PHashingCollection d
107108
//
108109
// -----------------------------------------------------------------------------------------------------------------
109110
public static final class PDictItemIterator extends PHashingStorageIterator<DictEntry> {
110-
public PDictItemIterator(Object clazz, DynamicObject storage, HashingStorageIterator<DictEntry> iterator) {
111-
super(clazz, storage, iterator);
111+
public PDictItemIterator(Object clazz, DynamicObject storage, HashingStorageIterator<DictEntry> iterator, HashingStorage hashingStorage, int initialSize) {
112+
super(clazz, storage, iterator, hashingStorage, initialSize);
112113
}
113114
}
114115

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/PHashingStorageIterator.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,25 @@
4040
*/
4141
package com.oracle.graal.python.builtins.objects.dict;
4242

43+
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
44+
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary;
4345
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary.HashingStorageIterator;
4446
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
4547
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
4648
import com.oracle.truffle.api.object.DynamicObject;
4749

4850
public abstract class PHashingStorageIterator<T> extends PythonBuiltinObject {
4951

52+
private final HashingStorage hashingStorage;
53+
private final int size;
5054
private final HashingStorageIterator<T> iterator;
5155
private int index;
5256

53-
public PHashingStorageIterator(Object clazz, DynamicObject storage, HashingStorageIterator<T> iterator) {
57+
public PHashingStorageIterator(Object clazz, DynamicObject storage, HashingStorageIterator<T> iterator, HashingStorage hashingStorage, int size) {
5458
super(clazz, storage);
5559
this.iterator = iterator;
60+
this.hashingStorage = hashingStorage;
61+
this.size = size;
5662
}
5763

5864
public HashingStorageIterator<T> getIterator() {
@@ -74,4 +80,8 @@ public final boolean hasNext() {
7480
public int getIndex() {
7581
return index;
7682
}
83+
84+
public final boolean checkSizeChanged(HashingStorageLibrary lib) {
85+
return lib.length(hashingStorage) != size;
86+
}
7787
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/iterator/IteratorBuiltins.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,15 @@
3535
import com.oracle.graal.python.builtins.Builtin;
3636
import com.oracle.graal.python.builtins.CoreFunctions;
3737
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
38+
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.RuntimeError;
3839
import com.oracle.graal.python.builtins.PythonBuiltins;
40+
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary;
3941
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary.HashingStorageIterator;
4042
import com.oracle.graal.python.builtins.objects.common.SequenceNodes;
4143
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
4244
import com.oracle.graal.python.builtins.objects.iterator.PRangeIterator.PRangeReverseIterator;
4345
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
46+
import com.oracle.graal.python.nodes.ErrorMessages;
4447
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
4548
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
4649
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
@@ -53,6 +56,8 @@
5356
import com.oracle.truffle.api.dsl.NodeFactory;
5457
import com.oracle.truffle.api.dsl.Specialization;
5558
import com.oracle.truffle.api.frame.VirtualFrame;
59+
import com.oracle.truffle.api.library.CachedLibrary;
60+
import com.oracle.truffle.api.profiles.ConditionProfile;
5661
import com.oracle.truffle.api.profiles.ValueProfile;
5762

5863
@CoreFunctions(extendClasses = {PythonBuiltinClassType.PIterator, PythonBuiltinClassType.PArrayIterator})
@@ -134,9 +139,14 @@ long next(PLongSequenceIterator self) {
134139
}
135140

136141
@Specialization
137-
public Object next(PBaseSetIterator self) {
142+
public Object next(PBaseSetIterator self,
143+
@Cached("createBinaryProfile()") ConditionProfile sizeChanged,
144+
@CachedLibrary(limit = "1") HashingStorageLibrary storageLibrary) {
138145
HashingStorageIterator<Object> iterator = self.getIterator();
139146
if (iterator.hasNext()) {
147+
if (sizeChanged.profile(self.checkSizeChanged(storageLibrary))) {
148+
throw raise(RuntimeError, ErrorMessages.CHANGED_SIZE_DURING_ITERATION, "Set");
149+
}
140150
return iterator.next();
141151
}
142152
throw raise(StopIteration);

0 commit comments

Comments
 (0)