Skip to content

Commit c7de0a6

Browse files
committed
check for "other"-s storage change in dict.update
1 parent 33401cb commit c7de0a6

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

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

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SETITEM__;
4343
import static com.oracle.graal.python.runtime.exception.PythonErrorType.KeyError;
4444
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
45+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.RuntimeError;
4546

4647
import java.util.List;
4748

@@ -51,13 +52,15 @@
5152
import com.oracle.graal.python.builtins.PythonBuiltins;
5253
import com.oracle.graal.python.builtins.objects.PNone;
5354
import com.oracle.graal.python.builtins.objects.PNotImplemented;
55+
import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage;
5456
import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes;
5557
import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes.GetDictStorageNode;
5658
import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes.SetDictStorageNode;
5759
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
5860
import com.oracle.graal.python.builtins.objects.common.HashingStorage.DictEntry;
5961
import com.oracle.graal.python.builtins.objects.common.HashingStorage.StorageSupplier;
6062
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary;
63+
import com.oracle.graal.python.builtins.objects.common.HashingStorageLibrary.HashingStorageIterator;
6164
import com.oracle.graal.python.builtins.objects.common.SequenceNodes;
6265
import com.oracle.graal.python.builtins.objects.function.PArguments;
6366
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
@@ -452,8 +455,10 @@ public abstract static class ClearNode extends PythonUnaryBuiltinNode {
452455

453456
@Specialization(limit = "3")
454457
public PDict clear(PDict dict,
455-
@CachedLibrary("dict.getDictStorage()") HashingStorageLibrary lib) {
456-
lib.clear(dict.getDictStorage());
458+
@CachedLibrary("dict.getDictStorage()") HashingStorageLibrary lib,
459+
@Cached SetDictStorageNode setStorage) {
460+
HashingStorage newStorage = lib.clear(dict.getDictStorage());
461+
setStorage.execute(dict, newStorage);
457462
return dict;
458463
}
459464
}
@@ -498,7 +503,7 @@ public Object update(VirtualFrame frame, PDict self, @SuppressWarnings("unused")
498503
return PNone.NONE;
499504
}
500505

501-
@Specialization(guards = {"isDict(args)", "kwargs.length == 0"})
506+
@Specialization(guards = {"isDictButNotEconomicMap(args, getStorage)", "kwargs.length == 0"})
502507
public Object updateDict(PDict self, Object[] args, @SuppressWarnings("unused") PKeyword[] kwargs,
503508
@CachedLibrary(limit = "1") HashingStorageLibrary lib,
504509
@Cached GetDictStorageNode getStorage,
@@ -508,7 +513,7 @@ public Object updateDict(PDict self, Object[] args, @SuppressWarnings("unused")
508513
return PNone.NONE;
509514
}
510515

511-
@Specialization(guards = {"isDict(args)", "kwargs.length > 0"})
516+
@Specialization(guards = {"isDictButNotEconomicMap(args, getStorage)", "kwargs.length > 0"})
512517
public Object updateDict(VirtualFrame frame, PDict self, Object[] args, PKeyword[] kwargs,
513518
@CachedLibrary(limit = "1") HashingStorageLibrary lib,
514519
@Cached HashingStorage.InitNode initNode,
@@ -520,6 +525,45 @@ public Object updateDict(VirtualFrame frame, PDict self, Object[] args, PKeyword
520525
return PNone.NONE;
521526
}
522527

528+
@Specialization(guards = {"isDictEconomicMap(args, getStorage)", "kwargs.length == 0"}, limit = "1")
529+
public Object updateDict(PDict self, Object[] args, @SuppressWarnings("unused") PKeyword[] kwargs,
530+
@Cached GetDictStorageNode getStorage,
531+
@Cached SetDictStorageNode setStorage,
532+
@CachedLibrary("getStorage.execute(self)") HashingStorageLibrary libSelf,
533+
@CachedLibrary(limit = "1") HashingStorageLibrary libOther) {
534+
HashingStorage newStorage = addAll(self, (PDict) args[0], getStorage, libSelf, libOther);
535+
setStorage.execute(self, newStorage);
536+
return PNone.NONE;
537+
}
538+
539+
@Specialization(guards = {"isDictEconomicMap(args, getStorage)", "kwargs.length > 0"}, limit = "1")
540+
public Object updateDict(VirtualFrame frame, PDict self, Object[] args, PKeyword[] kwargs,
541+
@Cached GetDictStorageNode getStorage,
542+
@Cached SetDictStorageNode setStorage,
543+
@CachedLibrary("getStorage.execute(self)") HashingStorageLibrary libSelf,
544+
@CachedLibrary(limit = "1") HashingStorageLibrary libOther,
545+
@Cached HashingStorage.InitNode initNode) {
546+
HashingStorage newStorage = addAll(self, (PDict) args[0], getStorage, libSelf, libOther);
547+
newStorage = libOther.addAllToOther(initNode.execute(frame, PNone.NO_VALUE, kwargs), newStorage);
548+
setStorage.execute(self, newStorage);
549+
return PNone.NONE;
550+
}
551+
552+
private HashingStorage addAll(PDict self, PDict other, GetDictStorageNode getStorage, HashingStorageLibrary libSelf, HashingStorageLibrary libOther) throws PException {
553+
HashingStorage selfStorage = getStorage.execute(self);
554+
HashingStorage otherStorage = getStorage.execute(other);
555+
HashingStorageIterator<DictEntry> itOther = libOther.entries(otherStorage).iterator();
556+
HashingStorage newStorage = selfStorage;
557+
while (itOther.hasNext()) {
558+
DictEntry next = itOther.next();
559+
newStorage = libSelf.setItem(selfStorage, next.key, next.value);
560+
if (otherStorage != getStorage.execute(other)) {
561+
throw raise(RuntimeError, ErrorMessages.MUTATED_DURING_UPDATE, "dict");
562+
}
563+
}
564+
return newStorage;
565+
}
566+
523567
@Specialization(guards = {"args.length == 1", "!isDict(args)", "hasKeysAttr(args, libArg)"})
524568
public Object updateMapping(VirtualFrame frame, PDict self, Object[] args, PKeyword[] kwargs,
525569
@SuppressWarnings("unused") @CachedLibrary(limit = "1") PythonObjectLibrary libArg,
@@ -570,6 +614,14 @@ protected boolean isDict(Object[] args) {
570614
return args.length == 1 && args[0] instanceof PDict;
571615
}
572616

617+
protected boolean isDictEconomicMap(Object[] args, GetDictStorageNode getStorage) {
618+
return args.length == 1 && args[0] instanceof PDict && getStorage.execute((PDict) args[0]) instanceof EconomicMapStorage;
619+
}
620+
621+
protected boolean isDictButNotEconomicMap(Object[] args, GetDictStorageNode getStorage) {
622+
return args.length == 1 && args[0] instanceof PDict && !(getStorage.execute((PDict) args[0]) instanceof EconomicMapStorage);
623+
}
624+
573625
protected boolean isSeq(Object[] args) {
574626
return args.length == 1 && args[0] instanceof PSequence;
575627
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ public abstract class ErrorMessages {
322322
public static final String MUST_BE_STRINGS = "%s must be strings";
323323
public static final String MUST_BE_STRINGS_NOT_P = "%s must be strings, not %p";
324324
public static final String MUST_SPECIFY_FILTERS = "Must specify filters for FORMAT_RAW";
325+
public static final String MUTATED_DURING_UPDATE = "%s mutated during update";
325326
public static final String NAME_IS_ASSIGNED_BEFORE_NONLOCAL = "name '%s' is assigned to before nonlocal declaration";
326327
public static final String NAME_NOT_DEFINED = "name '%s' is not defined";
327328
public static final String NEED_BYTELIKE_OBJ = "decoding to str: need a bytes-like object, %p found";

0 commit comments

Comments
 (0)