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