Skip to content

Commit 7a6779d

Browse files
committed
skip NO_VALUE keys for PythohnObject dict storage
1 parent 3b3da41 commit 7a6779d

File tree

2 files changed

+124
-6
lines changed

2 files changed

+124
-6
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_class-set-attrib.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ def test_deepcopy_attribute_removal():
138138
from copy import deepcopy
139139

140140
class A:
141-
a = "a"
141+
def __init__(self):
142+
self.a = "a"
142143

143144
def add_rem_attr(self):
144145
self.b = "b"

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/DynamicObjectStorage.java

Lines changed: 122 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@
4141
package com.oracle.graal.python.builtins.objects.common;
4242

4343
import java.util.ArrayList;
44+
import java.util.Iterator;
45+
import java.util.NoSuchElementException;
46+
import java.util.function.Predicate;
4447

48+
import com.oracle.graal.python.builtins.objects.PNone;
4549
import com.oracle.graal.python.runtime.sequence.storage.MroSequenceStorage;
4650
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
4751
import com.oracle.truffle.api.object.DynamicObject;
@@ -105,17 +109,20 @@ public boolean remove(Object key, Equivalence eq) {
105109
return result;
106110
}
107111

112+
protected Iterable<Object> getKeysIterable() {
113+
return store.getShape().getKeys();
114+
}
115+
108116
@Override
109117
public Iterable<Object> keys() {
110-
return wrapJavaIterable(store.getShape().getKeys());
118+
return wrapJavaIterable(getKeysIterable());
111119
}
112120

113121
@Override
114122
@TruffleBoundary
115123
public Iterable<Object> values() {
116124
ArrayList<Object> entries = new ArrayList<>(store.size());
117-
Shape shape = store.getShape();
118-
for (Object key : shape.getKeys()) {
125+
for (Object key : getKeysIterable()) {
119126
entries.add(store.get(key));
120127
}
121128
return wrapJavaIterable(entries);
@@ -125,8 +132,7 @@ public Iterable<Object> values() {
125132
@TruffleBoundary
126133
public Iterable<DictEntry> entries() {
127134
ArrayList<DictEntry> entries = new ArrayList<>(store.size());
128-
Shape shape = store.getShape();
129-
for (Object key : shape.getKeys()) {
135+
for (Object key : getKeysIterable()) {
130136
entries.add(new DictEntry(key, store.get(key)));
131137
}
132138
return wrapJavaIterable(entries);
@@ -143,6 +149,67 @@ public DynamicObject getStore() {
143149
return store;
144150
}
145151

152+
153+
private static class FilterIterator<T> implements Iterator<T> {
154+
private final Iterator<T> iterator;
155+
private final Predicate<T> predicate;
156+
private T value;
157+
private boolean valueIsSet = false;
158+
159+
private FilterIterator(Iterator<T> iterator, Predicate<T> predicate) {
160+
this.iterator = iterator;
161+
this.predicate = predicate;
162+
}
163+
164+
@Override
165+
public boolean hasNext() {
166+
return valueIsSet || consumeUntilNext();
167+
}
168+
169+
@Override
170+
public T next() {
171+
if (valueIsSet || consumeUntilNext()) {
172+
valueIsSet = false;
173+
return value;
174+
}
175+
// no more vals matching were found
176+
throw new NoSuchElementException();
177+
}
178+
179+
private boolean consumeUntilNext() {
180+
while (iterator.hasNext()) {
181+
T next = iterator.next();
182+
if (predicate.test(next)) {
183+
value = next;
184+
valueIsSet = true;
185+
return true;
186+
}
187+
}
188+
return false;
189+
}
190+
}
191+
192+
private static class NoValuePredicate<T> implements Predicate<T> {
193+
194+
private final DynamicObject store;
195+
196+
private NoValuePredicate(DynamicObject store) {
197+
this.store = store;
198+
}
199+
200+
@Override
201+
public boolean test(T t) {
202+
return store.get(t) != PNone.NO_VALUE;
203+
}
204+
}
205+
206+
private static class NoValueFilterIterator extends FilterIterator<Object> {
207+
208+
private NoValueFilterIterator(DynamicObject store) {
209+
super(store.getShape().getKeys().iterator(), new NoValuePredicate<>(store));
210+
}
211+
}
212+
146213
public static final class FastDictStorage extends DynamicObjectStorage {
147214
public FastDictStorage() {
148215
}
@@ -159,10 +226,60 @@ public HashingStorage copy(Equivalence eq) {
159226
}
160227

161228
public static final class PythonObjectDictStorage extends DynamicObjectStorage {
229+
private int size = -1;
230+
162231
public PythonObjectDictStorage(DynamicObject store) {
163232
super(store);
164233
}
165234

235+
@Override
236+
protected Iterable<Object> getKeysIterable() {
237+
return new Iterable<Object>() {
238+
@Override
239+
public Iterator<Object> iterator() {
240+
return new NoValueFilterIterator(getStore());
241+
}
242+
};
243+
}
244+
245+
@Override
246+
public int length() {
247+
if (size == -1) {
248+
size = 0;
249+
for (Object ignored : getKeysIterable()) {
250+
size += 1;
251+
}
252+
}
253+
return size;
254+
}
255+
256+
@Override
257+
public void setItem(Object key, Object value, Equivalence eq) {
258+
super.setItem(key, value, eq);
259+
if (value != PNone.NO_VALUE) {
260+
size += 1;
261+
}
262+
}
263+
264+
@Override
265+
public boolean remove(Object key, Equivalence eq) {
266+
if (getStore().get(key) != PNone.NO_VALUE) {
267+
size -= 1;
268+
}
269+
return super.remove(key, eq);
270+
}
271+
272+
@Override
273+
public void clear() {
274+
super.clear();
275+
size = 0;
276+
}
277+
278+
@Override
279+
public boolean hasKey(Object key, Equivalence eq) {
280+
return super.hasKey(key, eq) && getStore().get(key, PNone.NO_VALUE) != PNone.NO_VALUE;
281+
}
282+
166283
@Override
167284
@TruffleBoundary
168285
public HashingStorage copy(Equivalence eq) {

0 commit comments

Comments
 (0)