Commits

Armin Rigo committed b0c70fd

Add the requirement that W_XxxObject classes that are different
implementations of the same app-level type should inherit from a
common base class more precise than W_Object. This is actually
easy, just by adding some empty W_AbstractXxxObject classes here
and there.

This property allows us to build the _interplevel_classes
for-speed-only dictionary in a way that doesn't depend on
dictionary order. Previously it would randomly pick a class if
there are several ones, which might be (if you're unluckly) not
the most commonly used one.

  • Participants
  • Parent commits 73b76d7

Comments (0)

Files changed (19)

pypy/objspace/std/intobject.py

 something CPython does not do anymore.
 """
 
-class W_IntObject(W_Object):
+class W_AbstractIntObject(W_Object):
+    pass
+
+class W_IntObject(W_AbstractIntObject):
     __slots__ = 'intval'
     _immutable_fields_ = ['intval']
 

pypy/objspace/std/iterobject.py

 from pypy.objspace.std.register_all import register_all
 
 
-class W_AbstractSeqIterObject(W_Object):
+class W_AbstractIterObject(W_Object):
+    pass
+
+class W_AbstractSeqIterObject(W_AbstractIterObject):
     from pypy.objspace.std.itertype import iter_typedef as typedef
 
     def __init__(w_self, w_seq, index=0):

pypy/objspace/std/listobject.py

 from pypy.rlib.listsort import make_timsort_class
 from pypy.interpreter.argument import Signature
 
-class W_ListObject(W_Object):
+class W_AbstractListObject(W_Object):
+    pass
+
+class W_ListObject(W_AbstractListObject):
     from pypy.objspace.std.listtype import list_typedef as typedef
 
     def __init__(w_self, wrappeditems):

pypy/objspace/std/longobject.py

 from pypy.objspace.std.noneobject import W_NoneObject
 from pypy.rlib.rbigint import rbigint, SHIFT
 
-class W_LongObject(W_Object):
+class W_AbstractLongObject(W_Object):
+    pass
+
+class W_LongObject(W_AbstractLongObject):
     """This is a wrapper of rbigint."""
     from pypy.objspace.std.longtype import long_typedef as typedef
     _immutable_fields_ = ['num']

pypy/objspace/std/objspace.py

         if self.config.objspace.std.withtproxy:
             transparent.setup(self)
 
-        interplevel_classes = {}
-        for type, classes in self.model.typeorder.iteritems():
-            if len(classes) >= 3: # XXX what does this 3 mean??!
-                # W_Root, AnyXxx and actual object
-                interplevel_classes[self.gettypefor(type)] = classes[0][0]
-        self._interplevel_classes = interplevel_classes
+        self.setup_isinstance_cache()
 
     def get_builtin_types(self):
         return self.builtin_types
     def isinstance_w(space, w_inst, w_type):
         return space._type_isinstance(w_inst, w_type)
 
+    def setup_isinstance_cache(self):
+        # This assumes that all classes in the stdobjspace implementing a
+        # particular app-level type are distinguished by a common base class.
+        # Alternatively, you can turn off the cache on specific classes,
+        # like e.g. proxyobject.  It is just a bit less performant but
+        # should not have any bad effect.
+        from pypy.objspace.std.model import W_Root, W_Object
+        #
+        # Build a dict {class: w_typeobject-or-None}.  The value None is used
+        # on classes that are known to be abstract base classes.
+        class2type = {}
+        class2type[W_Root] = None
+        class2type[W_Object] = None
+        for cls in self.model.typeorder.keys():
+            if getattr(cls, 'typedef', None) is None:
+                continue
+            if getattr(cls, 'ignore_for_isinstance_cache', False):
+                continue
+            w_type = self.gettypefor(cls)
+            w_oldtype = class2type.setdefault(cls, w_type)
+            assert w_oldtype is w_type
+        #
+        # Build the real dict {w_typeobject: class-or-base-class}.  For every
+        # w_typeobject we look for the most precise common base class of all
+        # the registered classes.  If no such class is found, we will find
+        # W_Object or W_Root, and complain.  Then you must either add an
+        # artificial common base class, or disable caching on one of the
+        # two classes with ignore_for_isinstance_cache.
+        def getmro(cls):
+            while True:
+                yield cls
+                if cls is W_Root:
+                    break
+                cls = cls.__bases__[0]
+        self._interplevel_classes = {}
+        for cls, w_type in class2type.items():
+            if w_type is None:
+                continue
+            if w_type not in self._interplevel_classes:
+                self._interplevel_classes[w_type] = cls
+            else:
+                cls1 = self._interplevel_classes[w_type]
+                mro1 = list(getmro(cls1))
+                for base in getmro(cls):
+                    if base in mro1:
+                        break
+                if base in class2type and class2type[base] is not w_type:
+                    if class2type.get(base) is None:
+                        msg = ("cannot find a common interp-level base class"
+                               " between %r and %r" % (cls1, cls))
+                    else:
+                        msg = ("%s is a base class of both %r and %r" % (
+                            class2type[base], cls1, cls))
+                    raise AssertionError("%r: %s" % (w_type, msg))
+                class2type[base] = w_type
+                self._interplevel_classes[w_type] = base
+
     @specialize.memo()
     def _get_interplevel_cls(self, w_type):
         if not hasattr(self, "_interplevel_classes"):

pypy/objspace/std/proxyobject.py

 def transparent_class(name, BaseCls):
 
     class W_Transparent(BaseCls):
+        ignore_for_isinstance_cache = True
+
         def __init__(self, space, w_type, w_controller):
             self.w_type = w_type
             self.w_controller = w_controller

pypy/objspace/std/rangeobject.py

 from pypy.objspace.std.noneobject import W_NoneObject
 from pypy.objspace.std.inttype import wrapint
 from pypy.objspace.std.sliceobject import W_SliceObject, normalize_simple_slice
-from pypy.objspace.std.listobject import W_ListObject
+from pypy.objspace.std.listobject import W_AbstractListObject, W_ListObject
 from pypy.objspace.std import listtype, iterobject, slicetype
 from pypy.interpreter import gateway, baseobjspace
 
         return (start - stop - step  - 1)/-step
 
 
-class W_RangeListObject(W_Object):
+class W_RangeListObject(W_AbstractListObject):
     typedef = listtype.list_typedef
 
     def __init__(w_self, start, step, length):

pypy/objspace/std/ropeobject.py

 from pypy.rlib.objectmodel import we_are_translated
 from pypy.objspace.std.inttype import wrapint
 from pypy.objspace.std.sliceobject import W_SliceObject, normalize_simple_slice
-from pypy.objspace.std import slicetype
+from pypy.objspace.std import stringobject, slicetype, iterobject
 from pypy.objspace.std.listobject import W_ListObject
 from pypy.objspace.std.noneobject import W_NoneObject
 from pypy.objspace.std.tupleobject import W_TupleObject
     str_format__String as str_format__Rope,
     _upper, _lower, DEFAULT_NOOP_TABLE)
 
-class W_RopeObject(W_Object):
+class W_RopeObject(stringobject.W_AbstractStringObject):
     from pypy.objspace.std.stringtype import str_typedef as typedef
     _immutable_fields_ = ['_node']
 
 
 registerimplementation(W_RopeObject)
 
-class W_RopeIterObject(W_Object):
+class W_RopeIterObject(iterobject.W_AbstractIterObject):
     from pypy.objspace.std.itertype import iter_typedef as typedef
 
     def __init__(w_self, w_rope, index=0):

pypy/objspace/std/ropeunicodeobject.py

 from pypy.objspace.std.noneobject import W_NoneObject
 from pypy.rlib import rope
 from pypy.objspace.std.sliceobject import W_SliceObject, normalize_simple_slice
-from pypy.objspace.std import slicetype
+from pypy.objspace.std import unicodeobject, slicetype, iterobject
 from pypy.objspace.std.tupleobject import W_TupleObject
 from pypy.rlib.rarithmetic import intmask, ovfcheck
 from pypy.module.unicodedata import unicodedb
     return encode_object(space, w_unistr, encoding, errors)
 
 
-class W_RopeUnicodeObject(W_Object):
+class W_RopeUnicodeObject(unicodeobject.W_AbstractUnicodeObject):
     from pypy.objspace.std.unicodetype import unicode_typedef as typedef
     _immutable_fields_ = ['_node']
 
     return rope.LiteralUnicodeNode(space.unicode_w(w_str))
 
 
-class W_RopeUnicodeIterObject(W_Object):
+class W_RopeUnicodeIterObject(iterobject.W_AbstractIterObject):
     from pypy.objspace.std.itertype import iter_typedef as typedef
 
     def __init__(w_self, w_rope, index=0):

pypy/objspace/std/smallintobject.py

 from pypy.objspace.std.model import registerimplementation, W_Object
 from pypy.objspace.std.register_all import register_all
 from pypy.objspace.std.noneobject import W_NoneObject
-from pypy.objspace.std.intobject import W_IntObject
+from pypy.objspace.std.intobject import W_AbstractIntObject, W_IntObject
 from pypy.interpreter.error import OperationError
 from pypy.rlib.objectmodel import UnboxedValue
 from pypy.rlib.rbigint import rbigint
 from pypy.tool.sourcetools import func_with_new_name
 from pypy.objspace.std.inttype import wrapint
 
-class W_SmallIntObject(W_Object, UnboxedValue):
+class W_SmallIntObject(W_AbstractIntObject, UnboxedValue):
     __slots__ = 'intval'
     from pypy.objspace.std.inttype import int_typedef as typedef
 

pypy/objspace/std/smalllongobject.py

 from pypy.rlib.rarithmetic import r_longlong, r_int, r_uint
 from pypy.rlib.rarithmetic import intmask, LONGLONG_BIT
 from pypy.rlib.rbigint import rbigint
-from pypy.objspace.std.longobject import W_LongObject
+from pypy.objspace.std.longobject import W_AbstractLongObject, W_LongObject
 from pypy.objspace.std.intobject import W_IntObject
 from pypy.objspace.std.noneobject import W_NoneObject
 from pypy.interpreter.error import OperationError
 LONGLONG_MIN = r_longlong((-1) << (LONGLONG_BIT-1))
 
 
-class W_SmallLongObject(W_Object):
+class W_SmallLongObject(W_AbstractLongObject):
     from pypy.objspace.std.longtype import long_typedef as typedef
     _immutable_fields_ = ['longlong']
 

pypy/objspace/std/smalltupleobject.py

 from pypy.interpreter import gateway
 from pypy.rlib.debug import make_sure_not_resized
 from pypy.rlib.unroll import unrolling_iterable
-from pypy.objspace.std.tupleobject import W_TupleObject
+from pypy.objspace.std.tupleobject import W_AbstractTupleObject, W_TupleObject
 
-class W_SmallTupleObject(W_Object):
+class W_SmallTupleObject(W_AbstractTupleObject):
     from pypy.objspace.std.tupletype import tuple_typedef as typedef
 
     def tolist(self):

pypy/objspace/std/strbufobject.py

 from pypy.objspace.std.model import registerimplementation, W_Object
 from pypy.objspace.std.register_all import register_all
+from pypy.objspace.std.stringobject import W_AbstractStringObject
 from pypy.objspace.std.stringobject import W_StringObject
 from pypy.objspace.std.unicodeobject import delegate_String2Unicode
 from pypy.rlib.rstring import StringBuilder
 from pypy.interpreter.buffer import Buffer
 
-class W_StringBufferObject(W_Object):
+class W_StringBufferObject(W_AbstractStringObject):
     from pypy.objspace.std.stringtype import str_typedef as typedef
 
     w_str = None

pypy/objspace/std/stringobject.py

 
 from pypy.objspace.std.formatting import mod_format
 
-class W_StringObject(W_Object):
+class W_AbstractStringObject(W_Object):
+    pass
+
+class W_StringObject(W_AbstractStringObject):
     from pypy.objspace.std.stringtype import str_typedef as typedef
     _immutable_fields_ = ['_value']
 

pypy/objspace/std/strjoinobject.py

 from pypy.objspace.std.model import registerimplementation, W_Object
 from pypy.objspace.std.register_all import register_all
+from pypy.objspace.std.stringobject import W_AbstractStringObject
 from pypy.objspace.std.stringobject import W_StringObject
 from pypy.objspace.std.unicodeobject import delegate_String2Unicode
 
 from pypy.objspace.std.stringtype import wrapstr
 
-class W_StringJoinObject(W_Object):
+class W_StringJoinObject(W_AbstractStringObject):
     from pypy.objspace.std.stringtype import str_typedef as typedef
 
     def __init__(w_self, joined_strs, until=-1):

pypy/objspace/std/strsliceobject.py

 from pypy.interpreter.error import OperationError
 from pypy.objspace.std.model import registerimplementation, W_Object
 from pypy.objspace.std.register_all import register_all
+from pypy.objspace.std.stringobject import W_AbstractStringObject
 from pypy.objspace.std.stringobject import W_StringObject
 from pypy.objspace.std.unicodeobject import delegate_String2Unicode
 from pypy.objspace.std.sliceobject import W_SliceObject, normalize_simple_slice
      stringendswith, stringstartswith
 
 
-class W_StringSliceObject(W_Object):
+class W_StringSliceObject(W_AbstractStringObject):
     from pypy.objspace.std.stringtype import str_typedef as typedef
 
     def __init__(w_self, str, start, stop):

pypy/objspace/std/test/test_stdobjspace.py

     def test_fastpath_isinstance(self):
         from pypy.objspace.std.stringobject import W_StringObject
         from pypy.objspace.std.intobject import W_IntObject
+        from pypy.objspace.std.iterobject import W_AbstractSeqIterObject
+        from pypy.objspace.std.iterobject import W_SeqIterObject
 
         space = self.space
         assert space._get_interplevel_cls(space.w_str) is W_StringObject
 
         assert space.isinstance_w(X(), space.w_str)
 
+        w_sequenceiterator = space.gettypefor(W_SeqIterObject)
+        cls = space._get_interplevel_cls(w_sequenceiterator)
+        assert cls is W_AbstractSeqIterObject
+
     def test_withstrbuf_fastpath_isinstance(self):
-        from pypy.objspace.std.stringobject import W_StringObject
+        from pypy.objspace.std.stringobject import W_AbstractStringObject
 
-        space = gettestobjspace(withstrbuf=True) 
-        assert space._get_interplevel_cls(space.w_str) is W_StringObject
-        
+        space = gettestobjspace(withstrbuf=True)
+        cls = space._get_interplevel_cls(space.w_str)
+        assert cls is W_AbstractStringObject

pypy/objspace/std/tupleobject.py

 from pypy.interpreter import gateway
 from pypy.rlib.debug import make_sure_not_resized
 
-class W_TupleObject(W_Object):
+class W_AbstractTupleObject(W_Object):
+    pass
+
+class W_TupleObject(W_AbstractTupleObject):
     from pypy.objspace.std.tupletype import tuple_typedef as typedef
     _immutable_fields_ = ['wrappeditems[*]']
 

pypy/objspace/std/unicodeobject.py

 from pypy.objspace.std.formatting import mod_format
 from pypy.objspace.std.stringtype import stringstartswith, stringendswith
 
-class W_UnicodeObject(W_Object):
+class W_AbstractUnicodeObject(W_Object):
+    pass
+
+class W_UnicodeObject(W_AbstractUnicodeObject):
     from pypy.objspace.std.unicodetype import unicode_typedef as typedef
     _immutable_fields_ = ['_value']