From 02832616b916decc8e2347a14fe90f3b54c59a72 Mon Sep 17 00:00:00 2001 From: Carl Friedrich Bolz-Tereick Date: Thu, 31 Dec 2020 14:38:07 +0100 Subject: implement immutability of UnwrappedPlainAttributes --- pypy/objspace/std/mapdict.py | 13 ++++++++++--- pypy/objspace/std/test/test_mapdict.py | 35 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) (limited to 'pypy') diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py index 74c153145c..9bde356768 100644 --- a/pypy/objspace/std/mapdict.py +++ b/pypy/objspace/std/mapdict.py @@ -526,13 +526,20 @@ class UnboxedPlainAttribute(PlainAttribute): return self._box(unerase_unboxed(obj._mapdict_read_storage(self.storageindex))[self.listindex]) def _pure_direct_read(self, obj): - XXX # difficult! - return self._direct_read(obj) + # somewhat tricky! note that _direct_read isn't really elidable (it has + # potential side effects, and the boxes aren't always the same) + # but _pure_unboxed_read is elidable, and we can let the jit see the + # boxing + return self._box(self._pure_unboxed_read(obj)) + + @jit.elidable + def _pure_unboxed_read(self, obj): + return unerase_unboxed(obj._mapdict_read_storage(self.storageindex))[self.listindex] def _direct_write(self, obj, w_value): if type(w_value) is self.typ: val = self._unbox(w_value) - unboxed = erase_unboxed(obj._mapdict_read_storage(self.storageindex)) + unboxed = unerase_unboxed(obj._mapdict_read_storage(self.storageindex)) unboxed[self.listindex] = val return # type change not supposed to happen. according to the principle diff --git a/pypy/objspace/std/test/test_mapdict.py b/pypy/objspace/std/test/test_mapdict.py index 45ed1615c1..05bed07164 100644 --- a/pypy/objspace/std/test/test_mapdict.py +++ b/pypy/objspace/std/test/test_mapdict.py @@ -641,6 +641,41 @@ def test_unboxed_type_change_other_object(): # now it's switched assert type(w_obj2.map) is PlainAttribute +def test_unboxed_attr_immutability(monkeypatch): + cls = Class(allow_unboxing=True) + obj = cls.instantiate() + obj.setdictvalue(space, "a", 10) + obj.setdictvalue(space, "b", 20) + obj.setdictvalue(space, "b", 30) + assert obj.map.ever_mutated == True + assert obj.map.back.ever_mutated == False + + indices = [] + + def _pure_unboxed_read(obj): + indices.append(0) + return longlong2float(10) + + obj.map.back._pure_unboxed_read = _pure_unboxed_read + monkeypatch.setattr(jit, "isconstant", lambda c: True) + + assert obj.getdictvalue(space, "a") == 10 + assert obj.getdictvalue(space, "b") == 30 + assert obj.getdictvalue(space, "a") == 10 + assert indices == [0, 0] + + obj2 = cls.instantiate() + obj2.setdictvalue(space, "a", 15) + obj2.setdictvalue(space, "b", 25) + assert obj2.map is obj.map + assert obj2.map.ever_mutated == True + assert obj2.map.back.ever_mutated == False + + # mutating obj2 changes the map + obj2.setdictvalue(space, "a", 50) + assert obj2.map.back.ever_mutated == True + assert obj2.map is obj.map + # ___________________________________________________________ # dict tests -- cgit v1.2.3-65-gdbad