Author: Remi Meier <[email protected]>
Branch: stmgc-c8-gcc
Changeset: r79548:7d1f31843f97
Date: 2015-09-08 17:52 +0200
http://bitbucket.org/pypy/pypy/changeset/7d1f31843f97/

Log:    wip: fix handling of weird __eq__

diff --git a/pypy/module/pypystm/stmdict.py b/pypy/module/pypystm/stmdict.py
--- a/pypy/module/pypystm/stmdict.py
+++ b/pypy/module/pypystm/stmdict.py
@@ -16,7 +16,10 @@
 PARRAY = lltype.Ptr(ARRAY)
 
 
+
 def find_equal_item(space, array, w_key):
+    # result by this function is based on 'array'. If the entry
+    # changes, the result is stale.
     w_item = cast_gcref_to_instance(W_Root, array[0])
     if space.eq_w(w_key, w_item):
         return 0
@@ -27,13 +30,12 @@
 @jit.dont_look_inside
 def _run_next_iterations(space, array, w_key):
     i = 2
-    limit = len(array)
     while True:
         w_item = cast_gcref_to_instance(W_Root, array[i])
-        if space.eq_w(w_key, w_item):
+        if space.eq_w(w_key, w_item): # array may change here
             return i
         i += 2
-        if i >= limit:
+        if i >= len(array):
             return -1
 
 def ll_arraycopy(source, dest, source_start, dest_start, length):
@@ -43,24 +45,37 @@
         for i in range(length):
             dest[dest_start + i] = source[source_start + i]
 
-def pop_from_entry(h, entry, space, w_key):
+def pop_from_entry(h, space, w_key):
+    hkey = space.hash_w(w_key)
+    entry = h.lookup(hkey)
     array = lltype.cast_opaque_ptr(PARRAY, entry.object)
-    if not array:
-        return None
-    i = find_equal_item(space, array, w_key)
-    if i < 0:
-        return None
-    # found
-    w_value = cast_gcref_to_instance(W_Root, array[i + 1])
-    L = len(array) - 2
-    if L == 0:
-        narray = lltype.nullptr(ARRAY)
-    else:
-        narray = lltype.malloc(ARRAY, L)
-        ll_arraycopy(array, narray, 0, 0, i)
-        ll_arraycopy(array, narray, i + 2, i, L - i)
-    h.writeobj(entry, lltype.cast_opaque_ptr(llmemory.GCREF, narray))
-    return w_value
+    while True:
+        if not array:
+            return None
+
+        i = find_equal_item(space, array, w_key)
+        if not space.type(w_key).compares_by_identity():
+            entry2 = h.lookup(hkey)
+            array2 = lltype.cast_opaque_ptr(PARRAY, entry2.object)
+            if array2 != array:
+                entry = entry2
+                array = array2
+                continue # not utopia yet
+
+        if i < 0:
+            return None
+        # found
+        w_value = cast_gcref_to_instance(W_Root, array[i + 1])
+        L = len(array) - 2
+        if L == 0:
+            narray = lltype.nullptr(ARRAY)
+        else:
+            narray = lltype.malloc(ARRAY, L)
+            ll_arraycopy(array, narray, 0, 0, i)
+            ll_arraycopy(array, narray, i + 2, i, L - i)
+        h.writeobj(entry, lltype.cast_opaque_ptr(llmemory.GCREF, narray))
+        return w_value
+
 
 
 class W_STMDict(W_Root):
@@ -70,63 +85,95 @@
 
     def getitem_w(self, space, w_key):
         hkey = space.hash_w(w_key)
-        gcref = self.h.get(hkey)
-        array = lltype.cast_opaque_ptr(PARRAY, gcref)
-        if array:
-            i = find_equal_item(space, array, w_key)
-            if i >= 0:
-                return cast_gcref_to_instance(W_Root, array[i + 1])
-        space.raise_key_error(w_key)
+        entry = self.h.lookup(hkey)
+        array = lltype.cast_opaque_ptr(PARRAY, entry.object)
+        while True:
+            if array:
+                i = find_equal_item(space, array, w_key)
+
+                if not space.type(w_key).compares_by_identity():
+                    # the world may have changed:
+                    # if entry has changed, we are lost
+                    # if array has changed, the result we got from 
find_equal_item
+                    #    is not trustworthy; should also imply entry!=entry2
+                    entry2 = self.h.lookup(hkey)
+                    array2 = lltype.cast_opaque_ptr(PARRAY, entry2.object)
+                    if array2 != array:
+                        entry = entry2
+                        array = array2
+                        continue # not utopia yet
+
+                if i >= 0:
+                    return cast_gcref_to_instance(W_Root, array[i + 1])
+            space.raise_key_error(w_key)
 
     def setitem_w(self, space, w_key, w_value):
         hkey = space.hash_w(w_key)
         entry = self.h.lookup(hkey)
         array = lltype.cast_opaque_ptr(PARRAY, entry.object)
-        if array:
-            i = find_equal_item(space, array, w_key)
-            if i >= 0:
-                # already there, update the value
-                array[i + 1] = cast_instance_to_gcref(w_value)
-                return
-            L = len(array)
-            narray = lltype.malloc(ARRAY, L + 2)
-            ll_arraycopy(array, narray, 0, 0, L)
-        else:
-            narray = lltype.malloc(ARRAY, 2)
-            L = 0
-        narray[L] = cast_instance_to_gcref(w_key)
-        narray[L + 1] = cast_instance_to_gcref(w_value)
-        self.h.writeobj(entry, lltype.cast_opaque_ptr(llmemory.GCREF, narray))
+        while True:
+            if array:
+                i = find_equal_item(space, array, w_key)
+                if not space.type(w_key).compares_by_identity():
+                    entry2 = self.h.lookup(hkey)
+                    array2 = lltype.cast_opaque_ptr(PARRAY, entry2.object)
+                    if array2 != array:
+                        entry = entry2
+                        array = array2
+                        continue
+
+                if i >= 0:
+                    # already there, update the value
+                    array[i + 1] = cast_instance_to_gcref(w_value)
+                    return
+                L = len(array)
+                narray = lltype.malloc(ARRAY, L + 2)
+                ll_arraycopy(array, narray, 0, 0, L)
+            else:
+                narray = lltype.malloc(ARRAY, 2)
+                L = 0
+            narray[L] = cast_instance_to_gcref(w_key)
+            narray[L + 1] = cast_instance_to_gcref(w_value)
+            self.h.writeobj(entry, lltype.cast_opaque_ptr(llmemory.GCREF, 
narray))
+            return
 
     def delitem_w(self, space, w_key):
-        hkey = space.hash_w(w_key)
-        entry = self.h.lookup(hkey)
-        if pop_from_entry(self.h, entry, space, w_key) is None:
+        if pop_from_entry(self.h, space, w_key) is None:
             space.raise_key_error(w_key)
 
     def contains_w(self, space, w_key):
         hkey = space.hash_w(w_key)
         gcref = self.h.get(hkey)
         array = lltype.cast_opaque_ptr(PARRAY, gcref)
-        if array and find_equal_item(space, array, w_key) >= 0:
-            return space.w_True
-        return space.w_False
+        while True:
+            if array and find_equal_item(space, array, w_key) >= 0:
+                if not space.type(w_key).compares_by_identity():
+                    array2 = lltype.cast_opaque_ptr(PARRAY, self.h.get(hkey))
+                    if array2 != array:
+                        array = array2
+                        continue
+                return space.w_True
+            return space.w_False
 
     @unwrap_spec(w_default=WrappedDefault(None))
     def get_w(self, space, w_key, w_default):
         hkey = space.hash_w(w_key)
         gcref = self.h.get(hkey)
         array = lltype.cast_opaque_ptr(PARRAY, gcref)
-        if array:
-            i = find_equal_item(space, array, w_key)
-            if i >= 0:
-                return cast_gcref_to_instance(W_Root, array[i + 1])
-        return w_default
+        while True:
+            if array:
+                i = find_equal_item(space, array, w_key)
+                if not space.type(w_key).compares_by_identity():
+                    array2 = lltype.cast_opaque_ptr(PARRAY, self.h.get(hkey))
+                    if array2 != array:
+                        array = array2
+                        continue
+                if i >= 0:
+                    return cast_gcref_to_instance(W_Root, array[i + 1])
+            return w_default
 
     def pop_w(self, space, w_key, w_default=None):
-        hkey = space.hash_w(w_key)
-        entry = self.h.lookup(hkey)
-        w_value = pop_from_entry(self.h, entry, space, w_key)
+        w_value = pop_from_entry(self.h, space, w_key)
         if w_value is not None:
             return w_value
         elif w_default is not None:
@@ -139,21 +186,29 @@
         hkey = space.hash_w(w_key)
         entry = self.h.lookup(hkey)
         array = lltype.cast_opaque_ptr(PARRAY, entry.object)
-        if array:
-            i = find_equal_item(space, array, w_key)
-            if i >= 0:
-                # already there, return the existing value
-                return cast_gcref_to_instance(W_Root, array[i + 1])
-            L = len(array)
-            narray = lltype.malloc(ARRAY, L + 2)
-            ll_arraycopy(array, narray, 0, 0, L)
-        else:
-            narray = lltype.malloc(ARRAY, 2)
-            L = 0
-        narray[L] = cast_instance_to_gcref(w_key)
-        narray[L + 1] = cast_instance_to_gcref(w_default)
-        self.h.writeobj(entry, lltype.cast_opaque_ptr(llmemory.GCREF, narray))
-        return w_default
+        while True:
+            if array:
+                i = find_equal_item(space, array, w_key)
+                if not space.type(w_key).compares_by_identity():
+                    entry2 = self.h.lookup(hkey)
+                    array2 = lltype.cast_opaque_ptr(PARRAY, entry2.object)
+                    if array2 != array:
+                        entry = entry2
+                        array = array2
+                        continue
+                if i >= 0:
+                    # already there, return the existing value
+                    return cast_gcref_to_instance(W_Root, array[i + 1])
+                L = len(array)
+                narray = lltype.malloc(ARRAY, L + 2)
+                ll_arraycopy(array, narray, 0, 0, L)
+            else:
+                narray = lltype.malloc(ARRAY, 2)
+                L = 0
+            narray[L] = cast_instance_to_gcref(w_key)
+            narray[L + 1] = cast_instance_to_gcref(w_default)
+            self.h.writeobj(entry, lltype.cast_opaque_ptr(llmemory.GCREF, 
narray))
+            return w_default
 
     def get_length(self):
         array, count = self.h.list()
diff --git a/pypy/module/pypystm/test/test_stmdict.py 
b/pypy/module/pypystm/test/test_stmdict.py
--- a/pypy/module/pypystm/test/test_stmdict.py
+++ b/pypy/module/pypystm/test/test_stmdict.py
@@ -116,3 +116,47 @@
         res = d.pop(42.0, "foo")
         assert res == "bar"
         raises(KeyError, "d[42.0]")
+
+
+    def test_custom_evil_eq(self):
+        import pypystm
+
+        class A(object):
+            depth = []
+            def __hash__(self):
+                return 1
+            def __eq__(self, other):
+                if not self.depth:
+                    self.depth.append(1)
+                    del d[a]
+                    print "del a"
+                return self is other
+        d = pypystm.stmdict()
+        a = A()
+        b = A()
+        d[a] = "a"
+        d[b] = "b" # dels a
+        assert a not in d
+        assert b in d
+
+    def test_custom_evil_eq2(self):
+        import pypystm
+
+        class A(object):
+            depth = []
+            def __hash__(self):
+                return 1
+            def __eq__(self, other):
+                if not self.depth:
+                    self.depth.append(1)
+                    del d[a]
+                    print "del a"
+                return self is other
+        d = pypystm.stmdict()
+        a = A()
+        b = A()
+        d[a] = "a"
+        assert d.get(b) is None
+        assert a not in d
+        assert b not in d
+        assert d.keys() == []
diff --git a/rpython/rlib/rstm.py b/rpython/rlib/rstm.py
--- a/rpython/rlib/rstm.py
+++ b/rpython/rlib/rstm.py
@@ -298,20 +298,23 @@
 
 class HashtableForTest(object):
     def __init__(self):
-        self._content = {}      # dict {integer: GCREF}
+        self._content = {}      # dict {integer: Entry(obj=GCREF)}
 
     def _cleanup_(self):
         raise Exception("cannot translate a prebuilt rstm.Hashtable object")
 
     def get(self, key):
         assert type(key) is int
-        return self._content.get(key, NULL_GCREF)
+        return self.lookup(key).object
+        # return self._content.get(key, NULL_GCREF)
 
     def set(self, key, value):
         assert type(key) is int
         assert lltype.typeOf(value) == llmemory.GCREF
         if value:
-            self._content[key] = value
+            entry = self.lookup(key)
+            entry._obj = value
+            # self._content[key] = value
         else:
             try:
                 del self._content[key]
@@ -319,10 +322,11 @@
                 pass
 
     def len(self):
-        return len(self._content)
+        items = [self.lookup(key) for key, v in self._content.items() if 
v.object != NULL_GCREF]
+        return len(items)
 
     def list(self):
-        items = [self.lookup(key) for key in self._content]
+        items = [self.lookup(key) for key, v in self._content.items() if 
v.object != NULL_GCREF]
         count = len(items)
         for i in range(3):
             items.append("additional garbage for testing")
@@ -330,7 +334,7 @@
 
     def lookup(self, key):
         assert type(key) is int
-        return EntryObjectForTest(self, key)
+        return self._content.setdefault(key, EntryObjectForTest(self, key))
 
     def writeobj(self, entry, nvalue):
         assert isinstance(entry, EntryObjectForTest)
@@ -341,9 +345,10 @@
         self.hashtable = hashtable
         self.key = key
         self.index = r_uint(key)
+        self._obj = NULL_GCREF
 
     def _getobj(self):
-        return self.hashtable.get(self.key)
+        return self._obj
     def _setobj(self, nvalue):
         raise Exception("can't assign to the 'object' attribute:"
                         " use h.writeobj() instead")
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to