[pypy-svn] r69289 - in pypy/trunk/pypy: module/pypyjit/test objspace/std objspace/std/test

cfbolz at codespeak.net cfbolz at codespeak.net
Sat Nov 14 15:56:33 CET 2009


Author: cfbolz
Date: Sat Nov 14 15:56:31 2009
New Revision: 69289

Modified:
   pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py
   pypy/trunk/pypy/objspace/std/callmethod.py
   pypy/trunk/pypy/objspace/std/dictmultiobject.py
   pypy/trunk/pypy/objspace/std/objspace.py
   pypy/trunk/pypy/objspace/std/test/test_versionedtype.py
   pypy/trunk/pypy/objspace/std/typeobject.py
   pypy/trunk/pypy/objspace/std/typetype.py
Log:
clean up some code in typeobject to make sure that the version_tag of builtin
objects cannot change. Get rid of a few guards here and there.


Modified: pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py
==============================================================================
--- pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py	(original)
+++ pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py	Sat Nov 14 15:56:31 2009
@@ -187,7 +187,7 @@
         assert len(ops) == 2
         assert not ops[0].get_opnames("call")
         assert not ops[0].get_opnames("new")
-        assert len(ops[0].get_opnames("guard")) <= 8
+        assert len(ops[0].get_opnames("guard")) <= 7
         assert not ops[1] # second LOOKUP_METHOD folded away
 
         ops = self.get_by_bytecode("CALL_METHOD")
@@ -233,6 +233,7 @@
                 while i < n:
                     a = A()
                     assert isinstance(a, A)
+                    assert not isinstance(a, int)
                     a.x = 2
                     i = i + a.x
                 return i
@@ -240,13 +241,42 @@
                    ([20], 20),
                    ([31], 32))
 
-        callA, callisinstance = self.get_by_bytecode("CALL_FUNCTION")
+        callA, callisinstance1, callisinstance2 = (
+                self.get_by_bytecode("CALL_FUNCTION"))
         assert not callA.get_opnames("call")
         assert not callA.get_opnames("new")
-        assert len(callA.get_opnames("guard")) <= 9
-        assert not callisinstance.get_opnames("call")
-        assert not callisinstance.get_opnames("new")
-        assert len(callisinstance.get_opnames("guard")) <= 2
+        assert len(callA.get_opnames("guard")) <= 8
+        assert not callisinstance1.get_opnames("call")
+        assert not callisinstance1.get_opnames("new")
+        assert len(callisinstance1.get_opnames("guard")) <= 2
+        # calling isinstance on a builtin type gives zero guards
+        # because the version_tag of a builtin type is immutable
+        assert not len(callisinstance1.get_opnames("guard"))
+
+
+        bytecode, = self.get_by_bytecode("STORE_ATTR")
+        assert bytecode.get_opnames() == []
+
+    def test_load_attr(self):
+        self.run_source('''
+            class A(object):
+                pass
+            a = A()
+            a.x = 2
+            def main(n):
+                i = 0
+                while i < n:
+                    i = i + a.x
+                return i
+        ''',
+                   ([20], 20),
+                   ([31], 32))
+
+        load = self.get_by_bytecode("LOAD_ATTR")
+        # 1 guard_value for the class
+        # 1 guard_value for the version_tag
+        # 1 guard_value for the structure
+        assert len(load.get_opnames("guard")) <= 3
 
         bytecode, = self.get_by_bytecode("STORE_ATTR")
         assert bytecode.get_opnames() == []

Modified: pypy/trunk/pypy/objspace/std/callmethod.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/callmethod.py	(original)
+++ pypy/trunk/pypy/objspace/std/callmethod.py	Sat Nov 14 15:56:31 2009
@@ -33,22 +33,8 @@
     w_name = f.getname_w(nameindex)
     w_value = None
 
-    if space.config.objspace.std.getattributeshortcut:
-        w_type = space.type(w_obj)
-        fastpath = w_type.uses_object_getattribute
-        # conservatively, 'uses_object_getattribute' can be False
-        # even if __getattribute__ was not overridden.  In this
-        # case, the code below calls space.getattr(), which will
-        # set 'uses_object_getattribute' to True for the next time.
-    else:
-        w_getattribute = space.lookup(w_obj, '__getattribute__')
-        if w_getattribute is object_getattribute(space):
-            w_type = space.type(w_obj)
-            fastpath = True
-        else:
-            fastpath = False
-
-    if fastpath:
+    w_type = space.type(w_obj)
+    if w_type.has_object_getattribute():
         name = space.str_w(w_name)
         w_descr = w_type.lookup(name)
         if w_descr is None:
@@ -87,8 +73,8 @@
     """An optimized version of space.call_method()
     based on the same principle as above.
     """
-    w_getattribute = space.lookup(w_obj, '__getattribute__')
-    if w_getattribute is object_getattribute(space):
+    w_type = space.type(w_obj)
+    if w_type.has_object_getattribute():
         w_descr = space.lookup(w_obj, methname)
         typ = type(w_descr)
         if typ is function.Function or typ is function.FunctionWithFixedCode:

Modified: pypy/trunk/pypy/objspace/std/dictmultiobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/dictmultiobject.py	(original)
+++ pypy/trunk/pypy/objspace/std/dictmultiobject.py	Sat Nov 14 15:56:31 2009
@@ -393,7 +393,7 @@
     def __init__(self, space, w_type):
         StrDictImplementation.__init__(self, space)
         self.w_type = w_type
-        self.original_version_tag = w_type.version_tag
+        self.original_version_tag = w_type.version_tag()
         if self.original_version_tag is None:
             self._shadows_anything = True
         else:
@@ -419,7 +419,7 @@
 
     def impl_shadows_anything(self):
         return (self._shadows_anything or 
-                self.w_type.version_tag is not self.original_version_tag)
+                self.w_type.version_tag() is not self.original_version_tag)
 
     def impl_set_shadows_anything(self):
         self._shadows_anything = True

Modified: pypy/trunk/pypy/objspace/std/objspace.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/objspace.py	(original)
+++ pypy/trunk/pypy/objspace/std/objspace.py	Sat Nov 14 15:56:31 2009
@@ -692,13 +692,8 @@
         from pypy.objspace.descroperation import raiseattrerror
         from pypy.objspace.descroperation import object_getattribute
         w_type = self.type(w_obj)
-        if not w_type.uses_object_getattribute:
-            # slow path: look for a custom __getattribute__ on the class
-            w_descr = w_type.lookup('__getattribute__')
-            # if it was not actually overriden in the class, we remember this
-            # fact for the next time.
-            if w_descr is object_getattribute(self):
-                w_type.uses_object_getattribute = True
+        w_descr = w_type.getattribute_if_not_from_object()
+        if w_descr is not None:
             return self._handle_getattribute(w_descr, w_obj, w_name)
 
         # fast path: XXX this is duplicating most of the logic

Modified: pypy/trunk/pypy/objspace/std/test/test_versionedtype.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/test/test_versionedtype.py	(original)
+++ pypy/trunk/pypy/objspace/std/test/test_versionedtype.py	Sat Nov 14 15:56:31 2009
@@ -23,59 +23,100 @@
     def test_tag_changes(self):
         space = self.space
         w_A, w_B, w_C = self.get_three_classes()
-        atag = w_A.version_tag
-        btag = w_B.version_tag
+        atag = w_A.version_tag()
+        btag = w_B.version_tag()
         assert atag is not None
         assert btag is not None
-        assert w_C.version_tag is None
+        assert w_C.version_tag() is None
         assert atag is not btag
         w_types = space.appexec([w_A, w_B], """(A, B):
             B.g = lambda self: None
         """)
-        assert w_B.version_tag is not btag
-        assert w_A.version_tag is atag
-        btag = w_B.version_tag
+        assert w_B.version_tag() is not btag
+        assert w_A.version_tag() is atag
+        btag = w_B.version_tag()
         w_types = space.appexec([w_A, w_B], """(A, B):
             A.f = lambda self: None
         """)
-        assert w_B.version_tag is not btag
-        assert w_A.version_tag is not atag
-        atag = w_A.version_tag
-        btag = w_B.version_tag
+        assert w_B.version_tag() is not btag
+        assert w_A.version_tag() is not atag
+        atag = w_A.version_tag()
+        btag = w_B.version_tag()
         assert atag is not btag
         w_types = space.appexec([w_A, w_B], """(A, B):
             del A.f
         """)
-        assert w_B.version_tag is not btag
-        assert w_A.version_tag is not atag
-        atag = w_A.version_tag
-        btag = w_B.version_tag
+        assert w_B.version_tag() is not btag
+        assert w_A.version_tag() is not atag
+        atag = w_A.version_tag()
+        btag = w_B.version_tag()
         assert atag is not btag
 
     def test_tag_changes_when_bases_change(self):
         space = self.space
         w_A, w_B, w_C = self.get_three_classes()
-        atag = w_A.version_tag
-        btag = w_B.version_tag
+        atag = w_A.version_tag()
+        btag = w_B.version_tag()
         w_types = space.appexec([w_A, w_B, w_C], """(A, B, C):
             class D(object):
                 pass
             B.__bases__ = (D, )
         """)
-        assert w_B.version_tag is not btag
+        assert w_B.version_tag() is not btag
+
+    def test_tag_changes_only_when_dict_changes(self):
+        space = self.space
+        w_A, w_B, w_C = self.get_three_classes()
+        atag = w_A.version_tag()
+        # setting a descriptor does not change the version_tag
+        w_types = space.appexec([w_A, w_B, w_C], """(A, B, C):
+            A.__name__ = "hello"
+        """)
+
+        assert w_A.version_tag() is atag
+        # deleting via a descriptor does not change the version_tag
+        w_types = space.appexec([w_A, w_B, w_C], """(A, B, C):
+            try:
+                del A.__name__
+            except AttributeError:
+                pass
+        """)
+        assert w_A.version_tag() is atag
+
+        # deleting a non-existing key does not change the version_tag
+        w_types = space.appexec([w_A, w_B, w_C], """(A, B, C):
+            try:
+                del A.abc
+            except AttributeError:
+                pass
+        """)
+        assert w_A.version_tag() is atag
+
+    def test_tag_changes_When_module_changes(self):
+        space = self.space
+        w_A, w_B, w_C = self.get_three_classes()
+        atag = w_A.version_tag()
+        # setting a descriptor does not change the version_tag
+        w_types = space.appexec([w_A, w_B, w_C], """(A, B, C):
+            A.__module__ = "hello"
+        """)
+
+        assert w_A.version_tag() is not atag
+
+
 
     def test_version_tag_of_builtin_types(self):
         space = self.space
-        assert space.w_list.version_tag is not None
-        assert space.w_dict.version_tag is not None
-        assert space.type(space.sys).version_tag is None
-        assert space.w_type.version_tag is None
+        assert space.w_list.version_tag() is not None
+        assert space.w_dict.version_tag() is not None
+        assert space.type(space.sys).version_tag() is None
+        assert space.w_type.version_tag() is None
         w_function = space.appexec([], """():
             def f():
                 pass
             return type(f)
         """)
-        assert w_function.version_tag is None
+        assert w_function.version_tag() is None
 
     def test_version_tag_of_subclasses_of_builtin_types(self):
         space = self.space
@@ -95,11 +136,11 @@
         """)
         (w_LIST, w_DICT, w_TYPE, w_MODULE,
                  w_OBJECT) = space.unpackiterable(w_types)
-        assert w_LIST.version_tag is not None
-        assert w_DICT.version_tag is not None
-        assert w_TYPE.version_tag is None
-        assert w_MODULE.version_tag is None
-        assert w_OBJECT.version_tag is not None
+        assert w_LIST.version_tag() is not None
+        assert w_DICT.version_tag() is not None
+        assert w_TYPE.version_tag() is None
+        assert w_MODULE.version_tag() is None
+        assert w_OBJECT.version_tag() is not None
 
 class AppTestVersionedType(test_typeobject.AppTestTypeObject):
     def setup_class(cls):

Modified: pypy/trunk/pypy/objspace/std/typeobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/typeobject.py	(original)
+++ pypy/trunk/pypy/objspace/std/typeobject.py	Sat Nov 14 15:56:31 2009
@@ -48,7 +48,10 @@
     from pypy.objspace.std.typetype import type_typedef as typedef
 
     lazyloaders = {} # can be overridden by specific instances
-    version_tag = None
+
+    # the version_tag changes if the dict or the inheritance hierarchy changes
+    # other changes to the type (e.g. the name) leave it unchanged
+    _version_tag = None
 
     _immutable_fields_ = ["__flags__",
                           'needsdel',
@@ -92,10 +95,11 @@
             if w_self.instancetypedef.hasdict or custom_metaclass:
                 pass
             else:
-                w_self.version_tag = VersionTag()
+                w_self._version_tag = VersionTag()
 
     def mutated(w_self):
         space = w_self.space
+        assert w_self.is_heaptype() or not space.config.objspace.std.immutable_builtintypes
         if (not space.config.objspace.std.withtypeversion and
             not space.config.objspace.std.getattributeshortcut and
             not space.config.objspace.std.newshortcut):
@@ -109,14 +113,52 @@
             w_self.w_bltin_new = None
 
         if (space.config.objspace.std.withtypeversion
-            and w_self.version_tag is not None):
-            w_self.version_tag = VersionTag()
+            and w_self._version_tag is not None):
+            w_self._version_tag = VersionTag()
 
         subclasses_w = w_self.get_subclasses()
         for w_subclass in subclasses_w:
             assert isinstance(w_subclass, W_TypeObject)
             w_subclass.mutated()
 
+    def version_tag(w_self):
+        if (not we_are_jitted() or w_self.is_heaptype() or not
+            w_self.space.config.objspace.std.immutable_builtintypes):
+            return w_self._version_tag
+        # pure objects cannot get their version_tag changed
+        w_self = hint(w_self, promote=True)
+        return w_self._pure_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 """
+        from pypy.objspace.descroperation import object_getattribute
+        if not we_are_jitted():
+            shortcut = w_self.space.config.objspace.std.getattributeshortcut
+            if not shortcut or not w_self.uses_object_getattribute:
+                # slow path: look for a custom __getattribute__ on the class
+                w_descr = w_self.lookup('__getattribute__')
+                # if it was not actually overriden in the class, we remember this
+                # fact for the next time.
+                if w_descr is object_getattribute(w_self.space):
+                    if shortcut:
+                        w_self.uses_object_getattribute = True
+                else:
+                    return w_descr
+            return None
+        # in the JIT case, just use a lookup, because it is folded away
+        # correctly using the version_tag
+        w_descr = w_self.lookup('__getattribute__')
+        if w_descr is not object_getattribute(w_self.space):
+            return w_descr
+
+    def has_object_getattribute(w_self):
+        return w_self.getattribute_if_not_from_object() is None
+
+    @purefunction
+    def _pure_version_tag(w_self):
+        return w_self._version_tag
+
     def ready(w_self):
         for w_base in w_self.bases_w:
             if not isinstance(w_base, W_TypeObject):
@@ -195,21 +237,11 @@
                 return w_class, w_value
         return None, None
 
-    @purefunction
-    def _pure_lookup_where_builtin_type(w_self, name):
-        assert not w_self.is_heaptype()
-        return w_self._lookup_where(name)
-
     def lookup_where_with_method_cache(w_self, name):
         space = w_self.space
         w_self = hint(w_self, promote=True)
         assert space.config.objspace.std.withmethodcache
-        if (space.config.objspace.std.immutable_builtintypes and
-                we_are_jitted() and not w_self.is_heaptype()):
-            w_self = hint(w_self, promote=True)
-            name = hint(name, promote=True)
-            return w_self._pure_lookup_where_builtin_type(name)
-        version_tag = w_self.version_tag
+        version_tag = w_self.version_tag()
         version_tag = hint(version_tag, promote=True)
         if version_tag is None:
             tup = w_self._lookup_where(name)
@@ -594,7 +626,15 @@
         else:
             return space.type(w_obj)
     # invoke the __new__ of the type
-    w_bltin_new = w_type.w_bltin_new
+    if not we_are_jitted():
+        # note that the annotator will figure out that w_type.w_bltin_new can
+        # only be None if the newshortcut config option is not set
+        w_bltin_new = w_type.w_bltin_new
+    else:
+        # for the JIT it is better to take the slow path because normal lookup
+        # is nicely optimized, but the w_type.w_bltin_new attribute is not
+        # known to the JIT
+        w_bltin_new = None
     call_init = True
     if w_bltin_new is not None:
         w_newobject = space.call_obj_args(w_bltin_new, w_type, __args__)
@@ -602,6 +642,7 @@
         w_newtype, w_newdescr = w_type.lookup_where('__new__')
         w_newfunc = space.get(w_newdescr, w_type)
         if (space.config.objspace.std.newshortcut and
+            not we_are_jitted() and
             isinstance(w_newtype, W_TypeObject) and
             not w_newtype.is_heaptype() and
             not space.is_w(w_newtype, space.w_type)):
@@ -622,10 +663,6 @@
     return w_type2 in w_type1.mro_w
 
 @purefunction
-def _pure_issubtype_builtin(w_type1, w_type2):
-    return _issubtype(w_type1, w_type2)
-
- at purefunction
 def _pure_issubtype(w_type1, w_type2, version_tag1, version_tag2):
     return _issubtype(w_type1, w_type2)
 
@@ -633,19 +670,13 @@
     w_type1 = hint(w_type1, promote=True)
     w_type2 = hint(w_type2, promote=True)
     if space.config.objspace.std.withtypeversion and we_are_jitted():
-        if (space.config.objspace.std.immutable_builtintypes and
-            not w_type1.is_heaptype() and
-            not w_type2.is_heaptype()):
-            res = _pure_issubtype_builtin(w_type1, w_type2)
+        version_tag1 = w_type1.version_tag()
+        version_tag2 = w_type2.version_tag()
+        version_tag1 = hint(version_tag1, promote=True)
+        version_tag2 = hint(version_tag2, promote=True)
+        if version_tag1 is not None and version_tag2 is not None:
+            res = _pure_issubtype(w_type1, w_type2, version_tag1, version_tag2)
             return space.newbool(res)
-        else:
-            version_tag1 = w_type1.version_tag
-            version_tag2 = w_type2.version_tag
-            version_tag1 = hint(version_tag1, promote=True)
-            version_tag2 = hint(version_tag2, promote=True)
-            if version_tag1 is not None and version_tag2 is not None:
-                res = _pure_issubtype(w_type1, w_type2, version_tag1, version_tag2)
-                return space.newbool(res)
     res = _issubtype(w_type1, w_type2)
     return space.newbool(res)
 
@@ -684,7 +715,6 @@
     # Note. This is exactly the same thing as descroperation.descr__setattr__,
     # but it is needed at bootstrap to avoid a call to w_type.getdict() which
     # would un-lazify the whole type.
-    w_type.mutated()
     name = space.str_w(w_name)
     w_descr = space.lookup(w_type, name)
     if w_descr is not None:
@@ -699,10 +729,10 @@
     if name == "__del__" and name not in w_type.dict_w:
         msg = "a __del__ method added to an existing type will not be called"
         space.warn(msg, space.w_RuntimeWarning)
+    w_type.mutated()
     w_type.dict_w[name] = w_value
 
 def delattr__Type_ANY(space, w_type, w_name):
-    w_type.mutated()
     if w_type.lazyloaders:
         w_type._freeze_()    # force un-lazification
     name = space.str_w(w_name)
@@ -717,9 +747,11 @@
         raise OperationError(space.w_TypeError, space.wrap(msg))
     try:
         del w_type.dict_w[name]
-        return
     except KeyError:
         raise OperationError(space.w_AttributeError, w_name)
+    else:
+        w_type.mutated()
+        return
 
 
 # ____________________________________________________________

Modified: pypy/trunk/pypy/objspace/std/typetype.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/typetype.py	(original)
+++ pypy/trunk/pypy/objspace/std/typetype.py	Sat Nov 14 15:56:31 2009
@@ -194,6 +194,7 @@
         raise OperationError(space.w_TypeError, 
                              space.wrap("can't set %s.__module__" %
                                         w_type.name))
+    w_type.mutated()
     w_type.dict_w['__module__'] = w_value
 
 def descr___subclasses__(space, w_type):



More information about the Pypy-commit mailing list