Author: Carl Friedrich Bolz <[email protected]>
Branch: guard-compatible
Changeset: r83139:76268c65c9d5
Date: 2016-03-18 13:57 +0100
http://bitbucket.org/pypy/pypy/changeset/76268c65c9d5/
Log: make every map of a class have a version that is updated when the
class is changed
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -26,8 +26,11 @@
# we want to propagate knowledge that the result cannot be negative
+class Version(object):
+ pass
+
class AbstractAttribute(object):
- _immutable_fields_ = ['terminator']
+ _immutable_fields_ = ['terminator', 'version?']
cache_attrs = None
_size_estimate = 0
@@ -35,6 +38,12 @@
self.space = space
assert isinstance(terminator, Terminator)
self.terminator = terminator
+ # the maps have their own versions, if the terminator version is not
+ # None
+ if terminator.version is not None:
+ self.version = Version()
+ else:
+ self.version = None
@jit.elidable_compatible()
def getclass_from_terminator(self):
@@ -159,6 +168,9 @@
attr = cache.get((name, index), None)
if attr is None:
attr = PlainAttribute(name, index, self)
+ if self.terminator.all_children is None:
+ self.terminator.all_children = []
+ self.terminator.all_children.append(attr)
cache[name, index] = attr
return attr
@@ -295,11 +307,28 @@
class Terminator(AbstractAttribute):
- _immutable_fields_ = ['w_cls']
+ _immutable_fields_ = ['w_cls', 'version?']
def __init__(self, space, w_cls):
+ if w_cls._version_tag is None:
+ self.version = None
+ else:
+ self.version = Version()
AbstractAttribute.__init__(self, space, self)
self.w_cls = w_cls
+ self.all_children = None
+
+ def mutated_w_cls_version(self, version):
+ if version is None:
+ self.version = None
+ else:
+ self.version = Version()
+ if self.all_children is not None:
+ for map in self.all_children:
+ if version is None:
+ map.version = None
+ else:
+ map.version = Version()
def _read_terminator(self, obj, name, index):
return None
@@ -335,6 +364,10 @@
Terminator.__init__(self, space, w_cls)
self.devolved_dict_terminator = DevolvedDictTerminator(space, w_cls)
+ def mutated_w_cls_version(self, version):
+ self.devolved_dict_terminator.mutated_w_cls_version(version)
+ Terminator.mutated_w_cls_version(self, version)
+
def materialize_r_dict(self, space, obj, dict_w):
result = Object()
result.space = space
diff --git a/pypy/objspace/std/test/test_mapdict.py
b/pypy/objspace/std/test/test_mapdict.py
--- a/pypy/objspace/std/test/test_mapdict.py
+++ b/pypy/objspace/std/test/test_mapdict.py
@@ -17,6 +17,7 @@
class Class(object):
def __init__(self, hasdict=True):
self.hasdict = True
+ self._version_tag = None
if hasdict:
self.terminator = DictTerminator(space, self)
else:
@@ -34,7 +35,7 @@
hasdict = False
def test_plain_attribute():
- w_cls = "class"
+ w_cls = Class()
aa = PlainAttribute("b", DICT,
PlainAttribute("a", DICT,
Terminator(space, w_cls)))
@@ -62,14 +63,14 @@
assert aa.get_terminator() is aa.back.back
def test_huge_chain():
- current = Terminator(space, "cls")
+ current = Terminator(space, Class())
for i in range(20000):
current = PlainAttribute(str(i), DICT, current)
assert current.find_map_attr("0", DICT).storageindex == 0
def test_search():
- aa = PlainAttribute("b", DICT, PlainAttribute("a", DICT, Terminator(None,
None)))
+ aa = PlainAttribute("b", DICT, PlainAttribute("a", DICT, Terminator(None,
Class())))
assert aa.search(DICT) is aa
assert aa.search(SLOTS_STARTING_FROM) is None
assert aa.search(SPECIAL) is None
@@ -1209,6 +1210,7 @@
got = x.a
assert got == 'd'
+
class AppTestGlobalCaching(AppTestWithMapDict):
spaceconfig = {"objspace.std.withmethodcachecounter": True,
"objspace.std.withmapdict": True}
diff --git a/pypy/objspace/std/test/test_versionedtype.py
b/pypy/objspace/std/test/test_versionedtype.py
--- a/pypy/objspace/std/test/test_versionedtype.py
+++ b/pypy/objspace/std/test/test_versionedtype.py
@@ -260,6 +260,68 @@
assert space.float_w(cell.w_value) == 2.2
+class TestVersionedTypeMapDict(test_typeobject.TestTypeObject):
+ spaceconfig = {"objspace.std.withtypeversion": True,
+ "objspace.std.withmapdict": True}
+
+ def get_three_classes_and_instances(self):
+ space = self.space
+ w_types = space.appexec([], """():
+ class A(object):
+ def f(self): pass
+ class B(A):
+ pass
+ class X:
+ pass
+ class Y(object):
+ pass
+ class C(Y, X):
+ pass
+ a = A()
+ a.x = 1
+ b = B()
+ b.x = 1
+ c = C()
+ c.x = 1
+ c.y = 2
+ return A, B, C, a, b, c
+ """)
+ return space.unpackiterable(w_types)
+
+ def test_update_map_version_too(self):
+ space = self.space
+ w_A, w_B, w_C, a, b, c = self.get_three_classes_and_instances()
+ def get_versions(cls, *maps):
+ result = [cls.version_tag(), cls.terminator.version,
+ cls.terminator.devolved_dict_terminator.version]
+ result += [m.version for m in maps]
+ return result
+ def all_different(v1s, v2s):
+ for v1, v2 in zip(v1s, v2s):
+ assert v1 is not v2
+ aversions = get_versions(w_A, a.map)
+ bversions = get_versions(w_B, b.map)
+
+ assert w_C.version_tag() is None
+ assert w_C.terminator.version is None
+ assert c.map.version is None
+ # all versions are different
+ assert len(set(aversions)) == len(aversions)
+ assert len(set(bversions)) == len(bversions)
+
+ space.setattr(w_B, space.wrap("a"), space.wrap(1))
+ assert get_versions(w_A, a.map) == aversions
+ all_different(get_versions(w_B, b.map), bversions)
+ bversions = get_versions(w_B, b.map)
+
+ space.setattr(w_A, space.wrap("f"), space.wrap(5))
+ all_different(get_versions(w_A, a.map), aversions)
+ all_different(get_versions(w_B, b.map), bversions)
+
+ space.delattr(w_A, space.wrap("f"))
+ all_different(get_versions(w_A, a.map), aversions)
+ all_different(get_versions(w_B, b.map), bversions)
+
class AppTestVersionedType(test_typeobject.AppTestTypeObject):
spaceconfig = {"objspace.std.withtypeversion": True}
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -217,7 +217,7 @@
if (space.config.objspace.std.withtypeversion
and w_self._version_tag is not None):
- w_self._version_tag = VersionTag()
+ w_self._set_version_tag(VersionTag())
subclasses_w = w_self.get_subclasses()
for w_subclass in subclasses_w:
@@ -234,6 +234,11 @@
def _pure_version_tag(w_self):
return w_self._version_tag
+ def _set_version_tag(self, version_tag):
+ self._version_tag = version_tag
+ if self.space.config.objspace.std.withmapdict:
+ self.terminator.mutated_w_cls_version(version_tag)
+
def getattribute_if_not_from_object(w_self):
""" this method returns the applevel __getattribute__ if that is not
the one from object, in which case it returns None """
@@ -837,10 +842,10 @@
w_type.version_tag() is not None and
not is_mro_purely_of_types(w_type.mro_w)):
# Disable method cache if the hierarchy isn't pure.
- w_type._version_tag = None
+ w_type._set_version_tag(None)
for w_subclass in w_type.get_subclasses():
if isinstance(w_subclass, W_TypeObject):
- w_subclass._version_tag = None
+ w_subclass._set_version_tag(None)
def descr__base(space, w_type):
w_type = _check(space, w_type)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit