[pypy-svn] r19612 - in pypy/branch/somepbc-refactoring/pypy/annotation: . test

arigo at codespeak.net arigo at codespeak.net
Mon Nov 7 18:42:56 CET 2005


Author: arigo
Date: Mon Nov  7 18:42:54 2005
New Revision: 19612

Added:
   pypy/branch/somepbc-refactoring/pypy/annotation/desc.py
Modified:
   pypy/branch/somepbc-refactoring/pypy/annotation/binaryop.py
   pypy/branch/somepbc-refactoring/pypy/annotation/bookkeeper.py
   pypy/branch/somepbc-refactoring/pypy/annotation/model.py
   pypy/branch/somepbc-refactoring/pypy/annotation/test/test_model.py
   pypy/branch/somepbc-refactoring/pypy/annotation/unaryop.py
Log:
first steps.

the tests in annotation even pass!


Modified: pypy/branch/somepbc-refactoring/pypy/annotation/binaryop.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/binaryop.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/binaryop.py	Mon Nov  7 18:42:54 2005
@@ -7,9 +7,9 @@
 from pypy.annotation.model import SomeObject, SomeInteger, SomeBool
 from pypy.annotation.model import SomeString, SomeChar, SomeList, SomeDict
 from pypy.annotation.model import SomeUnicodeCodePoint
-from pypy.annotation.model import SomeTuple, SomeImpossibleValue
+from pypy.annotation.model import SomeTuple, SomeImpossibleValue, s_ImpossibleValue
 from pypy.annotation.model import SomeInstance, SomeBuiltin, SomeIterator
-from pypy.annotation.model import SomePBC, SomeSlice, SomeFloat
+from pypy.annotation.model import SomePBC, SomeSlice, SomeFloat, s_None
 from pypy.annotation.model import SomeExternalObject
 from pypy.annotation.model import SomeAddress, SomeTypedAddressAccess
 from pypy.annotation.model import unionof, UnionError, set, missing_operation, TLS
@@ -85,8 +85,7 @@
     def inplace_floordiv((obj1, obj2)): return pair(obj1, obj2).floordiv()
     def inplace_div((obj1, obj2)):      return pair(obj1, obj2).div()
     def inplace_mod((obj1, obj2)):      return pair(obj1, obj2).mod()
-    def inplace_pow((obj1, obj2)):      return pair(obj1, obj2).pow(
-                                                      SomePBC({None: True}))
+    def inplace_pow((obj1, obj2)):      return pair(obj1, obj2).pow(s_None)
     def inplace_lshift((obj1, obj2)):   return pair(obj1, obj2).lshift()
     def inplace_rshift((obj1, obj2)):   return pair(obj1, obj2).rshift()
     def inplace_and((obj1, obj2)):      return pair(obj1, obj2).and_()
@@ -523,9 +522,9 @@
                 resdef = ins1.classdef
             else:
                 if ins1.can_be_None and ins2.can_be_None:
-                    return SomePBC({None: True})
+                    return s_None
                 else:
-                    return SomeImpossibleValue()
+                    return s_ImpossibleValue
         res = SomeInstance(resdef, can_be_None=ins1.can_be_None and ins2.can_be_None)
         if ins1.contains(res) and ins2.contains(res):
             return res    # fine
@@ -556,27 +555,9 @@
 
 class __extend__(pairtype(SomePBC, SomePBC)):
     def union((pbc1, pbc2)):       
-        if len(pbc2.prebuiltinstances) > len(pbc1.prebuiltinstances):
-            pbc1, pbc2 = pbc2, pbc1
         d = pbc1.prebuiltinstances.copy()
-        for x, classdef in pbc2.prebuiltinstances.items():
-            if x in d:
-                if bool(isclassdef(classdef)) ^ bool(isclassdef(d[x])):
-                    raise UnionError(
-                        "union failed for %r with classdefs %r and %r" % 
-                        (x, classdef, d[x]))
-                if isclassdef(classdef):
-                    classdef2 = classdef
-                    if classdef != d[x]:
-                        classdef = classdef.commonbase(d[x])
-                        if classdef is None:
-                            raise UnionError(
-                                "confused pbc union trying unwarranted"
-                                "moving up of method %s from pair %s %s" %
-                                (x, d[x], classdef2))
-            d[x] = classdef
-        result =  SomePBC(d)
-        return result
+        d.update(pbc2.prebuiltinstances)
+        return SomePBC(d, can_be_None = pbc1.can_be_None or pbc2.can_be_None)
 
 class __extend__(pairtype(SomeImpossibleValue, SomeObject)):
     def union((imp1, obj2)):
@@ -590,6 +571,7 @@
     def union((ins, pbc)):
         if pbc.isNone():
             return SomeInstance(classdef=ins.classdef, can_be_None = True)
+        # XXX is the following still useful?
         classdef = ins.classdef.superdef_containing(pbc.knowntype)
         if classdef is None:
             # print warning?

Modified: pypy/branch/somepbc-refactoring/pypy/annotation/bookkeeper.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/bookkeeper.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/bookkeeper.py	Mon Nov  7 18:42:54 2005
@@ -9,7 +9,7 @@
 from pypy.annotation.model import SomeString, SomeChar, SomeFloat, \
      SomePtr, unionof, SomeInstance, SomeDict, SomeBuiltin, SomePBC, \
      SomeInteger, SomeExternalObject, SomeOOInstance, TLS, SomeAddress, \
-     new_or_old_class, SomeUnicodeCodePoint, SomeOOStaticMeth, \
+     SomeUnicodeCodePoint, SomeOOStaticMeth, s_None, \
      SomeLLADTMeth, SomeBool, SomeTuple, SomeOOClass, SomeImpossibleValue, \
      SomeList, SomeObject
 from pypy.annotation.classdef import ClassDef, isclassdef
@@ -178,10 +178,10 @@
 
     def __init__(self, annotator):
         self.annotator = annotator
-        self.userclasses = {}    # map classes to ClassDefs
-        self.userclasseslist = []# userclasses.keys() in creation order
+        self.descs = {}          # map Python objects to their XxxDesc wrappers
+        self.methoddescs = {}    # map (funcdesc, classdef) to the MethodDesc
+        self.classdefs = []      # list of all ClassDefs
         self.cachespecializations = {}
-        self.pbccache = {}
         self.pbctypes = {}
         self.seen_mutable = {}
         self.listdefs = {}       # map position_keys to ListDefs
@@ -247,21 +247,14 @@
                     del self.needs_hash_support[cls]
                     break
 
-    def getclassdef(self, cls):
-        """Get the ClassDef associated with the given user cls."""
+    def getuniqueclassdef(self, cls):
+        """Get the ClassDef associated with the given user cls.
+        Avoid using this!  It breaks for classes that must be specialized.
+        """
         if cls is object:
             return None
-        try:
-            return self.userclasses[cls]
-        except KeyError:
-            if cls in self.pbctypes:
-                self.warning("%r gets a ClassDef, but is the type of some PBC"
-                             % (cls,))
-            cdef = ClassDef(cls, self)
-            self.userclasses[cls] = cdef
-            self.userclasseslist.append(cdef)
-            cdef.setup()
-            return cdef
+        desc = self.getdesc(cls)
+        return desc.getuniqueclassdef()
 
     def getlistdef(self, **flags):
         """Get the ListDef associated with the current position."""
@@ -366,17 +359,14 @@
         elif isinstance(x, ootype._instance):
             result = SomeOOInstance(ootype.typeOf(x))
         elif callable(x) or isinstance(x, staticmethod): # XXX
-            # maybe 'x' is a method bound to a not-yet-frozen cache?
-            # fun fun fun.
-            if hasattr(x, 'im_self') and hasattr(x.im_self, '_freeze_'):
-                x.im_self._freeze_()
             if hasattr(x, '__self__') and x.__self__ is not None:
+                # for cases like 'l.append' where 'l' is a global constant list
                 s_self = self.immutablevalue(x.__self__)
                 result = s_self.find_method(x.__name__)
                 if result is None:
                     result = SomeObject()
             else:
-                return self.getpbc(x)
+                result = SomePBC([self.getdesc(x)])
         elif hasattr(x, '__class__') \
                  and x.__class__.__module__ != '__builtin__':
             # user-defined classes can define a method _freeze_(), which
@@ -385,7 +375,7 @@
             # a SomePBC().  Otherwise it's just SomeInstance().
             frozen = hasattr(x, '_freeze_') and x._freeze_()
             if frozen:
-                return self.getpbc(x)
+                result = SomePBC([self.getdesc(x)])
             else:
                 clsdef = self.getclassdef(x.__class__)
                 if x.__class__.__dict__.get('_annspecialcase_', '').endswith('ctr_location'):
@@ -398,29 +388,58 @@
                         clsdef.add_source_for_attribute(attr, x) # can trigger reflowing
                 result = SomeInstance(clsdef)
         elif x is None:
-            return self.getpbc(None)
+            return s_None
         else:
             result = SomeObject()
         result.const = x
         return result
 
-    def getpbc(self, x):
+    def getdesc(self, pyobj):
+        # get the XxxDesc wrapper for the given Python object, which must be
+        # one of:
+        #  * a user-defined Python function
+        #  * a Python type or class (but not a built-in one like 'int')
+        #  * a user-defined bound or unbound method object
+        #  * a frozen pre-built constant (with _freeze_() == True)
+        #  * a bound method of a frozen pre-built constant
+        try:
+            return self.descs[pyobj]
+        except KeyError:
+            if isinstance(pyobj, types.FunctionType):
+                result = FunctionDesc(pyobj)
+            elif isintance(pyobj, (type, types.ClassType)):
+                result = ClassDesc(pyobj)
+            elif isinstance(pyobj, types.MethodType):
+                if pyobj.im_self is None:   # unbound
+                    result = FunctionDesc(pyobj.im_func)
+                elif (hasattr(pyobj.im_self, '_freeze_') and
+                      pyobj.im_self._freeze_()):  # method of frozen
+                    result = MethodOfFrozenDesc(
+                        self.getdesc(pyobj.im_func),            # funcdesc
+                        self.getdesc(pyobj.im_self))            # frozendesc
+                else: # regular method
+                    result = self.getmethoddesc(
+                        self.getdesc(pyobj.im_func),            # funcdesc
+                        self.getuniqueclassdef(pyobj.im_class)) # classdef
+            else:
+                # must be a frozen pre-built constant, but let's check
+                assert pyobj._freeze_()
+                result = FrozenDesc(pyobj)
+                cls = result.knowntype
+                if cls not in self.pbctypes:
+                    self.pbctypes[cls] = True
+                    if cls in self.userclasses:
+                        self.warning("making some PBC of type %r, which has "
+                                     "already got a ClassDef" % (cls,))
+            self.descs[pyobj] = result
+            return result
+
+    def getmethoddesc(self, funcdesc, classdef):
         try:
-            # this is not just an optimization, but needed to avoid
-            # infinitely repeated calls to add_source_for_attribute()
-            return self.pbccache[x]
+            return self.methoddescs[funcdesc, classdef]
         except KeyError:
-            result = SomePBC({x: True}) # pre-built inst
-            #clsdef = self.getclassdef(new_or_old_class(x))
-            #for attr in getattr(x, '__dict__', {}):
-            #    clsdef.add_source_for_attribute(attr, x)
-            self.pbccache[x] = result
-            cls = new_or_old_class(x)
-            if cls not in self.pbctypes:
-                self.pbctypes[cls] = True
-                if cls in self.userclasses:
-                    self.warning("making some PBC of type %r, which has "
-                                 "already got a ClassDef" % (cls,))
+            result = MethodDesc(funcdesc, classdef)
+            self.methoddescs[funcdesc, classdef] = result
             return result
 
     def valueoftype(self, t):
@@ -443,7 +462,7 @@
             return SomeDict(MOST_GENERAL_DICTDEF)
         # can't do tuple
         elif t is NoneType:
-            return self.getpbc(None)
+            return s_None
         elif t in EXTERNAL_TYPE_ANALYZERS:
             return SomeExternalObject(t)
         elif t.__module__ != '__builtin__' and t not in self.pbctypes:
@@ -460,28 +479,22 @@
         attr = s_attr.const
 
         access_sets = self.pbc_maximal_access_sets
-        objects = pbc.prebuiltinstances.keys()
-
-        for obj in objects:
-            if obj is not None:
-                first = obj
-                break
-        else:
+        descs = pbc.prebuiltinstances.keys()
+        if not descs:
             return SomeImpossibleValue()
+        first = descs[0]
 
         change, rep, access = access_sets.find(first)
-        for obj in objects:
-            if obj is not None:
-                change1, rep, access = access_sets.union(rep, obj)
-                change = change or change1
+        for desc in descs:
+            change1, rep, access = access_sets.union(rep, desc)
+            change = change or change1
 
         position = self.position_key
         access.read_locations[position] = True
 
         actuals = []
-        for c in access.objects:
-            if hasattr(c, attr):
-                actuals.append(self.immutablevalue(getattr(c, attr)))
+        for desc in access.objects:
+            actuals.append(desc.s_read_attribute(attr))
         s_result = unionof(*actuals)
 
         access.attrs[attr] = s_result

Added: pypy/branch/somepbc-refactoring/pypy/annotation/desc.py
==============================================================================
--- (empty file)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/desc.py	Mon Nov  7 18:42:54 2005
@@ -0,0 +1,95 @@
+import types
+from pypy.interpreter.pycode import cpython_code_signature
+from pypy.annotation.classdef import ClassDef
+from pypy.annotation import model as annmodel
+
+
+class Desc(object):
+    pyobj = None   # non-None if there is an associated underlying Python obj
+
+    def __repr__(self):
+        pyobj = self.pyobj
+        if pyobj is None:
+            return object.__repr__(self)
+        return '<%s for %r>' % (self.__class__.__name__, pyobj)
+
+
+class FunctionDesc(Desc):
+    knowntype = types.FunctionType
+    
+    def __init__(self, pyobj=None, signature=None):
+        self.pyobj = pyobj
+        if signature is None:
+            signature = cpython_code_signature(pyfunc.func_code)
+        self.signature = signature
+
+
+class ClassDesc(Desc):
+    knowntype = type
+
+    def __init__(self, bookkeeper, pyobj, specialize=None):
+        self.bookkeeper = bookkeeper
+        self.pyobj = pyobj
+        self.name = pyobj.__module__ + '.' + pyobj.__name__
+        if specialize is None:
+            tag = pyobj.__dict__.get('_annspecialcase_', '')
+            assert not tag  # XXX later
+        self.specialize = specialize
+        self._classdef = None
+
+    def getuniqueclassdef(self):
+        if self.specialize:
+            raise Exception("not supported on class %r because it needs "
+                            "specialization" % (self.name,))
+        if self._classdef is None:
+            classdef = ClassDef(self.pyobj, self.bookkeeper)
+            self.bookkeeper.classdefs.append(classdef)
+            self._classdef = classdef
+            classdef.setup()
+        return self._classdef
+
+
+class MethodDesc(Desc):
+    knowntype = types.MethodType
+
+    def __init__(self, funcdesc, classdef):
+        self.funcdesc = funcdesc
+        self.classdef = classdef
+
+    def __repr__(self):
+        return '<MethodDesc %r of %r>' % (self.funcdesc,
+                                          self.classdef)
+
+
+def new_or_old_class(c):
+    if hasattr(c, '__class__'):
+        return c.__class__
+    else:
+        return type(c)
+
+
+class FrozenDesc(Desc):
+
+    def __init__(self, bookkeeper, pyobj):
+        self.bookkeeper = bookkeeper
+        self.pyobj = pyobj
+        self.attributes = self.pyobj.__dict__.copy()
+        self.knowntype = new_or_old_class(pyobj)
+
+    def s_read_attribute(self, attr):
+        if attr in self.attributes:
+            return self.bookkeeper.immutablevalue(self.attributes[attr])
+        else:
+            return annmodel.SomeImpossibleValue()
+
+
+class MethodOfFrozenDesc(Desc):
+    knowntype = types.MethodType
+
+    def __init__(self, funcdesc, frozendesc):
+        self.funcdesc = funcdesc
+        self.frozendesc = frozendesc
+
+    def __repr__(self):
+        return '<MethodOfFrozenDesc %r of %r>' % (self.funcdesc,
+                                                  self.frozendesc)

Modified: pypy/branch/somepbc-refactoring/pypy/annotation/model.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/model.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/model.py	Mon Nov  7 18:42:54 2005
@@ -294,63 +294,54 @@
         return SomeInstance(self.classdef, can_be_None=False)
 
 
-def new_or_old_class(c):
-    if hasattr(c, '__class__'):
-        return c.__class__
-    else:
-        return type(c)
-
-
 class SomePBC(SomeObject):
     """Stands for a global user instance, built prior to the analysis,
     or a set of such instances."""
-    def __init__(self, prebuiltinstances):
-        # prebuiltinstances is a dictionary containing concrete python
-        # objects as keys.
-        # if the key is a function, the value can be a classdef to
-        # indicate that it is really a method.
-        prebuiltinstances = prebuiltinstances.copy()
+    def __init__(self, prebuiltinstances, can_be_None=False):
+        # prebuiltinstances is a set of Desc instances.
+        prebuiltinstances = dict.fromkeys(prebuiltinstances)
         self.prebuiltinstances = prebuiltinstances
-        self.simplify()
+        self.can_be_None = can_be_None
+        self.check()
         if self.isNone():
             self.knowntype = type(None)
+            self.const = None
         else:
             knowntype = reduce(commonbase,
-                               [new_or_old_class(x)
-                                for x in prebuiltinstances
-                                if x is not None])
+                               [x.knowntype for x in prebuiltinstances])
             if knowntype == type(Exception):
                 knowntype = type
             if knowntype != object:
                 self.knowntype = knowntype
-        if prebuiltinstances.values() == [True]:
-            # hack for the convenience of direct callers to SomePBC():
-            # only if there is a single object in prebuiltinstances and
-            # it doesn't have an associated ClassDef
-            self.const, = prebuiltinstances
-    def simplify(self):
-        # We check that the dictionary does not contain at the same time
-        # a function bound to a classdef, and constant bound method objects
-        # on that class.
-        for x, ignored in self.prebuiltinstances.items():
-            if isinstance(x, MethodType) and x.im_func in self.prebuiltinstances:
-                classdef = self.prebuiltinstances[x.im_func]
-                if isinstance(x.im_self, classdef.cls):
-                    del self.prebuiltinstances[x]
+            if len(prebuiltinstances) == 1:
+                desc, = prebuiltinstances
+                if desc.pyobj is not None:
+                    # hack for the convenience of direct callers to SomePBC():
+                    # only if there is a single object in prebuiltinstances
+                    self.const, = desc.pyobj
+
+    def check(self):
+        # We check that the set only contains a single kind of Desc instance
+        kinds = {}
+        for x in self.prebuiltinstances:
+            assert type(x).__name__.endswith('Desc')  # avoid import nightmares
+            kinds[x.__class__] = True
+        assert len(kinds) <= 1, (
+            "mixing several kinds of PBCs: %r" % (kinds.keys(),))
+        assert self.prebuiltinstances or self.can_be_None, (
+            "use s_ImpossibleValue")
 
     def isNone(self):
-        return self.prebuiltinstances == {None:True}
+        return len(self.prebuiltinstances) == 0
 
     def can_be_none(self):
-        return None in self.prebuiltinstances
+        return self.can_be_None
 
     def nonnoneify(self):
-        prebuiltinstances = self.prebuiltinstances.copy()
-        del prebuiltinstances[None]
-        if not prebuiltinstances:
-            return SomeImpossibleValue()
+        if self.isNone():
+            return s_ImpossibleValue
         else:
-            return SomePBC(prebuiltinstances)
+            return SomePBC(self.prebuiltinstances, can_be_None=False)
 
     def fmt_prebuiltinstances(self, pbis):
         if hasattr(self, 'const'):
@@ -396,6 +387,10 @@
     def can_be_none(self):
         return False
 
+
+s_None = SomePBC([], can_be_None=True)
+s_ImpossibleValue = SomeImpossibleValue()
+
 # ____________________________________________________________
 # memory addresses
 
@@ -459,7 +454,7 @@
 from pypy.rpython.ootypesystem import ootype
 
 annotation_to_ll_map = [
-    (SomePBC({None: True}), lltype.Void),   # also matches SomeImpossibleValue()
+    (s_None, lltype.Void),   # also matches SomeImpossibleValue()
     (SomeBool(), lltype.Bool),
     (SomeInteger(), lltype.Signed),
     (SomeInteger(nonneg=True, unsigned=True), lltype.Unsigned),    
@@ -519,7 +514,7 @@
     try:
         s1, s2 = somevalues
     except ValueError:
-        s1 = SomeImpossibleValue()
+        s1 = s_ImpossibleValue
         for s2 in somevalues:
             if s1 != s2:
                 s1 = pair(s1, s2).union()
@@ -587,7 +582,7 @@
                 return  SomeObject()
         bookkeeper = pypy.annotation.bookkeeper.getbookkeeper()
         bookkeeper.warning("no precise annotation supplied for %s%r" % (name, args))
-        return SomeImpossibleValue()
+        return s_ImpossibleValue
     setattr(cls, name, default_op)
 
 #

Modified: pypy/branch/somepbc-refactoring/pypy/annotation/test/test_model.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/test/test_model.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/test/test_model.py	Mon Nov  7 18:42:54 2005
@@ -26,18 +26,17 @@
 
 si0 = SomeInstance(DummyClassDef(), True)
 si1 = SomeInstance(DummyClassDef())
-sNone = SomePBC({None: True})
 sTrue = SomeBool()
 sTrue.const = True
 sFalse = SomeBool()
 sFalse.const = False
 
 def test_is_None():
-    assert pair(sNone, sNone).is_() == sTrue
-    assert pair(si1, sNone).is_() == sFalse
-    assert pair(si0, sNone).is_() != sTrue
-    assert pair(si0, sNone).is_() != sFalse
-    assert pair(si0, sNone).is_() == SomeBool()
+    assert pair(s_None, s_None).is_() == sTrue
+    assert pair(si1, s_None).is_() == sFalse
+    assert pair(si0, s_None).is_() != sTrue
+    assert pair(si0, s_None).is_() != sFalse
+    assert pair(si0, s_None).is_() == SomeBool()
 
 def test_equality():
     assert s1 != s2 != s3 != s4 != s5 != s6

Modified: pypy/branch/somepbc-refactoring/pypy/annotation/unaryop.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/unaryop.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/unaryop.py	Mon Nov  7 18:42:54 2005
@@ -36,7 +36,7 @@
         if moreargs:
             raise Exception, 'type() called with more than one argument'
         if obj.is_constant():
-            r = SomePBC({obj.knowntype: True})
+            r = immutablevalue(obj.knowntype)
         else:
             r = SomeObject()
             r.knowntype = type



More information about the Pypy-commit mailing list