Skip to content

Commit 911ae6f

Browse files
committed
[GR-64010] Storing NO_VALUE in attribute removes the Key Value pair
PullRequest: graalpython/3753
2 parents 11194f0 + 054c3eb commit 911ae6f

File tree

2 files changed

+46
-4
lines changed

2 files changed

+46
-4
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_dict.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1267,11 +1267,22 @@ def test_dict_values_eq():
12671267
d1 = {1: 1, 2: 2, 4: 4}
12681268
assert d1.values() != d1.values()
12691269

1270-
12711270
def test_missing_and_not_implemented():
12721271
class MyDict(dict):
12731272
def __missing__(self, key):
12741273
return NotImplemented
12751274

12761275
d = MyDict()
12771276
assert d['bogus_key'] == NotImplemented
1277+
1278+
def test_removing_attr_from_economic_map():
1279+
class Test:
1280+
pass
1281+
1282+
o = Test()
1283+
o.foo = 1
1284+
o.__dict__[42] = 10
1285+
del o.foo
1286+
1287+
assert "foo" not in o.__dict__
1288+

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
5151
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
5252
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
53+
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
5354
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageSetItem;
5455
import com.oracle.graal.python.builtins.objects.dict.PDict;
5556
import com.oracle.graal.python.builtins.objects.object.PythonObject;
@@ -188,7 +189,7 @@ private static boolean writeToDynamicStorageManagedClass(PythonManagedClass klas
188189
}
189190

190191
// write to the dict: the basic specialization for non-classes
191-
@Specialization(guards = {"dict != null", "!isManagedClass(object)"})
192+
@Specialization(guards = {"dict != null", "!isManagedClass(object)", "!isNoValue(value)"})
192193
static boolean writeToDictNoType(@SuppressWarnings("unused") PythonObject object, TruffleString key, Object value,
193194
@Bind("this") Node inliningTarget,
194195
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
@@ -199,7 +200,7 @@ static boolean writeToDictNoType(@SuppressWarnings("unused") PythonObject object
199200
}
200201

201202
// write to the dict & PythonManagedClass -> requires calling onAttributeUpdate
202-
@Specialization(guards = {"dict != null"})
203+
@Specialization(guards = {"dict != null", "!isNoValue(value)"})
203204
boolean writeToDictBuiltinType(PythonBuiltinClass klass, TruffleString key, Object value,
204205
@Bind("this") Node inliningTarget,
205206
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
@@ -216,7 +217,7 @@ boolean writeToDictBuiltinType(PythonBuiltinClass klass, TruffleString key, Obje
216217
}
217218
}
218219

219-
@Specialization(guards = {"dict != null"})
220+
@Specialization(guards = {"dict != null", "!isNoValue(value)"})
220221
static boolean writeToDictClass(PythonClass klass, TruffleString key, Object value,
221222
@Bind("this") Node inliningTarget,
222223
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
@@ -229,6 +230,36 @@ static boolean writeToDictClass(PythonClass klass, TruffleString key, Object val
229230
return writeToDictManagedClass(klass, dict, key, value, inliningTarget, callAttrUpdate, updateStorage, setHashingStorageItem, codePointLengthNode, codePointAtIndexNode);
230231
}
231232

233+
@Specialization(guards = {"dict != null", "isNoValue(value)", "!isPythonBuiltinClass(obj)"})
234+
static boolean deleteFromPythonObject(PythonObject obj, TruffleString key, Object value,
235+
@Bind("this") Node inliningTarget,
236+
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
237+
@Bind("getDict.execute(obj)") PDict dict,
238+
@Shared("callAttrUpdate") @Cached InlinedBranchProfile callAttrUpdate,
239+
@Cached HashingStorageNodes.HashingStorageDelItem hashingStorageDelItem,
240+
@Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode,
241+
@Shared("cpAtIndex") @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode) {
242+
try {
243+
HashingStorage dictStorage = dict.getDictStorage();
244+
return hashingStorageDelItem.execute(inliningTarget, dictStorage, key, dict);
245+
} finally {
246+
if (obj instanceof PythonManagedClass klass) {
247+
if (!klass.canSkipOnAttributeUpdate(key, value, codePointLengthNode, codePointAtIndexNode)) {
248+
callAttrUpdate.enter(inliningTarget);
249+
klass.onAttributeUpdate(key, value);
250+
}
251+
}
252+
}
253+
}
254+
255+
@Specialization(guards = {"dict != null", "isNoValue(value)"})
256+
static boolean deleteFromPythonBuiltinClass(PythonBuiltinClass klass, TruffleString key, Object value,
257+
@Bind("this") Node inliningTarget,
258+
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
259+
@Bind("getDict.execute(klass)") PDict dict) {
260+
throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.CANT_SET_ATTRIBUTE_R_OF_IMMUTABLE_TYPE_N, key, klass);
261+
}
262+
232263
private static boolean writeToDictManagedClass(PythonManagedClass klass, PDict dict, TruffleString key, Object value, Node inliningTarget,
233264
InlinedBranchProfile callAttrUpdate, InlinedBranchProfile updateStorage, HashingStorageSetItem setHashingStorageItem, TruffleString.CodePointLengthNode codePointLengthNode,
234265
TruffleString.CodePointAtIndexNode codePointAtIndexNode) {

0 commit comments

Comments
 (0)