[pypy-svn] rev 956 - in pypy/trunk/src/pypy: interpreter objspace/ann objspace/ann/test

gvanrossum at codespeak.net gvanrossum at codespeak.net
Sun Jun 22 17:45:32 CEST 2003


Author: gvanrossum
Date: Sun Jun 22 17:45:31 2003
New Revision: 956

Modified:
   pypy/trunk/src/pypy/interpreter/pyframe.py
   pypy/trunk/src/pypy/objspace/ann/cloningcontext.py
   pypy/trunk/src/pypy/objspace/ann/objspace.py
   pypy/trunk/src/pypy/objspace/ann/test/test_objspace.py
   pypy/trunk/src/pypy/objspace/ann/wrapper.py
Log:
We now handle loops!!!

Modified: pypy/trunk/src/pypy/interpreter/pyframe.py
==============================================================================
--- pypy/trunk/src/pypy/interpreter/pyframe.py	(original)
+++ pypy/trunk/src/pypy/interpreter/pyframe.py	Sun Jun 22 17:45:31 2003
@@ -313,7 +313,7 @@
 
 class ExitFrame(Exception):
     """Signals the end of the frame execution.
-    The argument is the returned or yielded value."""
+    The argument is the returned or yielded value, already wrapped."""
 
 class BytecodeCorruption(ValueError):
     """Detected bytecode corruption.  Never caught; it's an error."""

Modified: pypy/trunk/src/pypy/objspace/ann/cloningcontext.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/ann/cloningcontext.py	(original)
+++ pypy/trunk/src/pypy/objspace/ann/cloningcontext.py	Sun Jun 22 17:45:31 2003
@@ -1,6 +1,7 @@
 from pypy.interpreter.executioncontext import ExecutionContext
-from pypy.interpreter.pyframe import ControlFlowException
-from pypy.objspace.ann.wrapper import union
+from pypy.interpreter.pyframe import ControlFlowException, ExitFrame
+from pypy.objspace.ann.wrapper \
+     import union, compatible_frames, unite_frames, W_Anything, W_Constant
 
 class IndeterminateCondition(ControlFlowException):
 
@@ -9,33 +10,53 @@
         self.w_obj = w_obj
 
     def action(self, frame, last_instr):
-        frame.next_instr = last_instr
-        f2 = frame.clone()
-        clones = frame.clones
-        clones.append(f2)
-        f2.clones = clones # Share the joy
-        f2.force_w_obj = self.w_obj
-        self.w_obj.force = True
+        frame.next_instr = last_instr # Restart failed opcode (!)
+        frame.restarting = (self.w_obj, True) # For bytecode_trace() below
 
 class CloningExecutionContext(ExecutionContext):
 
+    def __init__(self, space):
+        ExecutionContext.__init__(self, space)
+        self.knownframes = {}
+        # {(bytecode, w_globals): (result, clones, {next_instr: [frame, ...], ...}), ...}
+
+    def bytecode_trace(self, frame):
+        assert isinstance(frame.w_globals, W_Constant)
+        key = (frame.bytecode, id(frame.w_globals.value))
+        result, clones, subdict = self.knownframes[key]
+
+        if frame.restarting is not None:
+            w_obj, flag = frame.restarting
+            frame.restarting = None
+            w_obj.force = flag
+            if flag:
+                f2 = frame.clone()
+                f2.restarting = (w_obj, False)
+                clones.append(f2)
+            return
+
+        frames = subdict.setdefault(frame.next_instr, [])
+        assert len(frames) <= 1 # We think this is true
+        for f in frames:
+            if compatible_frames(frame, f):
+                c1, c2 = unite_frames(frame, f)
+                if not c2:
+                    # A fixpoint
+                    raise ExitFrame(None)
+                return
+        frames.append(frame.clone())
 
     def eval_frame(self, frame):
-        from pypy.objspace.ann.objspace import W_Anything
         assert not hasattr(frame, "clones")
-        space = frame.space
-        clones = [frame]
-        frame.clones = clones
-        frame.force_w_obj = None
-        result = None # W_Impossible
+        assert self.space == frame.space
+        frame.restarting = None
+        key = (frame.bytecode, id(frame.w_globals.value))
+        result, clones, subdict = self.knownframes.setdefault(key, (None, [], {}))
+        clones.append(frame)
         while clones:
             f = clones.pop()
-            w_obj = f.force_w_obj
-            if w_obj is not None:
-                assert w_obj.force == True
-                w_obj.force = False
             r = ExecutionContext.eval_frame(self, f)
+            result, clones, subdict = self.knownframes[key]
             result = union(result, r)
-            if isinstance(result, W_Anything):
-                break
+            self.knownframes[key] = result, clones, subdict
         return result

Modified: pypy/trunk/src/pypy/objspace/ann/objspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/ann/objspace.py	(original)
+++ pypy/trunk/src/pypy/objspace/ann/objspace.py	Sun Jun 22 17:45:31 2003
@@ -21,6 +21,7 @@
 class AnnotationObjSpace(ObjSpace):
 
     def initialize(self):
+        self.bytecodecache = {}
         self.w_None = self.wrap(None)
         self.w_True = self.wrap(True)
         self.w_False = self.wrap(False)
@@ -115,11 +116,40 @@
         else:
             return W_Anything()
 
+    def sub(self, w_left, w_right):
+        try:
+            left = self.unwrap(w_left)
+            right = self.unwrap(w_right)
+        except UnwrapException:
+            pass
+        else:
+            return self.wrap(left - right)
+        if is_int(w_left) and is_int(w_right):
+            return W_Integer()
+        else:
+            return W_Anything()
+
+    def mul(self, w_left, w_right):
+        try:
+            left = self.unwrap(w_left)
+            right = self.unwrap(w_right)
+        except UnwrapException:
+            pass
+        else:
+            return self.wrap(left * right)
+        if is_int(w_left) and is_int(w_right):
+            return W_Integer()
+        else:
+            return W_Anything()
+
     def call(self, w_func, w_args, w_kwds):
         func = self.unwrap(w_func) # Would be bad it it was W_Anything
         code = func.func_code
-        bytecode = PyByteCode()
-        bytecode._from_code(code)
+        bytecode = self.bytecodecache.get(code)
+        if bytecode is None:
+            bytecode = PyByteCode()
+            bytecode._from_code(code)
+            self.bytecodecache[code] = bytecode
         w_locals = bytecode.build_arguments(self,
                                             w_args,
                                             w_kwds,

Modified: pypy/trunk/src/pypy/objspace/ann/test/test_objspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/ann/test/test_objspace.py	(original)
+++ pypy/trunk/src/pypy/objspace/ann/test/test_objspace.py	Sun Jun 22 17:45:31 2003
@@ -41,6 +41,12 @@
                           'f', [self.space.wrap(5), W_Anything()])
         self.assertEquals(self.space.unwrap(x), 6)
 
+    def test_constplusstr2any(self):
+        x = self.codetest("def f(i, j):\n"
+                          "    return i+j\n",
+                          'f', [W_Integer(), self.space.wrap(3.5)])
+        self.assertEquals(type(x), W_Anything)
+
     def test_int2int(self):
         x = self.codetest("def f(i):\n"
                           "    return i+1\n",
@@ -73,7 +79,7 @@
                           'f', [W_Integer()])
         self.assertEquals(self.space.unwrap(x), 0)
 
-    def dont_test_while(self):
+    def test_while(self):
         x = self.codetest("def f(i):\n"
                           "    while i > 0:\n"
                           "        i = i-1\n"
@@ -81,8 +87,16 @@
                           'f', [W_Integer()])
         self.assertEquals(type(x), W_Integer)
 
+    def test_factorial(self):
+        x = self.codetest("def f(i):\n"
+                          "    if i > 1:\n"
+                          "        return i*f(i-1)\n"
+                          "    return 1\n",
+                          'f', [self.space.wrap(5)])
+        self.assertEquals(type(x), W_Integer)
+
     def dont_test_global(self):
-        # XXX This doesn't work because we don't handle mutating globals
+        # XXX This will never work, because we don't handle mutating globals
         x = self.codetest("def f(i, j):\n"
                           "    global x\n"
                           "    x = i\n"

Modified: pypy/trunk/src/pypy/objspace/ann/wrapper.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/ann/wrapper.py	(original)
+++ pypy/trunk/src/pypy/objspace/ann/wrapper.py	Sun Jun 22 17:45:31 2003
@@ -9,23 +9,50 @@
 """
 
 class W_Object(object):
+    """Abstract base class.  do not instantiate."""
+    def __new__(cls, *args, **kwd):
+        assert cls is not W_Object
+        return object.__new__(cls)
+    def __init__(self):
+        pass
+    def __repr__(self):
+        return "%s(%s)" % (self.__class__.__name__, self.argsrepr())
+    def argsrepr(self):
+        return ""
+    def __eq__(self, other):
+        return type(other) is type(self)
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+class W_Undefined(W_Object):
+    """May be undefined.  This is the most contagious type."""
     pass
 
 class W_Anything(W_Object):
+    """Any (defined) value.   This is the next most contagious type."""
     pass
 
 class W_Integer(W_Object):
+    """An integer value (int or long)."""
     pass
 
 class W_Constant(W_Object):
+    """A specific constant value."""
     def __init__(self, value):
         self.value = value
-    def __repr__(self):
-        return '<constant %r>' % self.value
+    def argsrepr(self):
+        return repr(self.value)[:50]
+    def __eq__(self, other):
+        return type(other) is type(self) and self.value == other.value
 
 class W_KnownKeysContainer(W_Object):
+    """A dict with constant set of keys or a tuple with known length."""
     def __init__(self, args_w):
         self.args_w = args_w
+    def argsrepr(self):
+        return repr(self.args_w)
+    def __eq__(self, other):
+        return type(other) is type(self) and self.args_w == other.args_w
     def __len__(self):
         return len(self.args_w)
     def __getitem__(self, i):
@@ -37,14 +64,99 @@
         # XXX Recurse down the values?
         return W_KnownKeysContainer(args_w)
 
+def unite_frames(f1, f2):
+    """Given two compatible frames, make them the same.
+
+    This changes both f1 and f2 in-place to change all the values into
+    their union.  It returns two booleans, indicating whether the
+    frames were changed.
+
+    This requires that the frames are compatible.
+
+    """
+    assert compatible_frames(f1, f2)
+
+    # Compare value stacks
+    # XXX uses stack internals
+    s1 = f1.valuestack.items
+    s2 = f2.valuestack.items
+    c1 = c2 = False # changed flags
+    n = len(s1)
+    assert n == len(s2)
+    for i in range(n):
+        v1 = s1[i]
+        v2 = s2[i]
+        if v1 != v2:
+            u = union(v1, v2)
+            if v1 != u:
+                c1 = True
+                s1[i] = u
+            if v2 != u:
+                c2 = True
+                s2[i] = u
+
+    # Compare locals
+    # XXX uses W_KnownKeysContainer internals
+    assert isinstance(f1.w_locals, W_KnownKeysContainer)
+    assert isinstance(f2.w_locals, W_KnownKeysContainer)
+    l1 = f1.w_locals.args_w
+    l2 = f2.w_locals.args_w
+    keydict = {} # compute set of keys
+    for key in l1.iterkeys():
+        keydict[key] = 1
+    for key in l2.iterkeys():
+        keydict[key] = 1
+    for key in keydict.iterkeys():
+        v1 = l1.get(key, W_Undefined())
+        v2 = l2.get(key, W_Undefined())
+        u = union(v1, v2)
+        if v1 != u:
+            c1 = True
+            l1[key] = u
+        if v2 != u:
+            c2 = True
+            l2[key] = u
+    return c1, c2
+
+def compatible_frames(f1, f2):
+    """Return whether two frames are compatible.
+
+    Informally, this means that they represent different states
+    at the same point in the program.
+
+    """
+    if f1 is f2:
+        return True
+    return (f1.next_instr == f2.next_instr and
+            f1.space is f2.space and
+            f2.bytecode is f2.bytecode and
+            f1.valuestack.depth() == f2.valuestack.depth() and
+            equivalent(f1.w_globals, f2.w_globals) and
+            equivalent(f1.w_builtins, f2.w_builtins) and
+            f1.blockstack.items == f2.blockstack.items)
+
+def equivalent(w1, w2):
+    """Return whether two wrappers are equivalent
+
+    (Helper for compatible_frames.)
+
+    They must be constant wrappers for the same object.
+
+    """
+    return (isinstance(w1, W_Constant) and
+            isinstance(w2, W_Constant) and
+            w1.value is w2.value)
+
 def union(r1, r2):
-    # Unite two results
+    """Return the union of two wrappers."""
     if r1 is r2:
         return r1
     if r1 is None:
         return r2
     if r2 is None:
         return r1
+    if isinstance(r1, W_Undefined) or isinstance(r2, W_Undefined):
+        return W_Undefined()
     if isinstance(r1, W_Anything) or isinstance(r2, W_Anything):
         return W_Anything()
     if (isinstance(r1, W_Constant) and isinstance(r2, W_Constant) and
@@ -61,5 +173,10 @@
     return W_Anything()
 
 def is_int(w_obj):
+    """Return whether a wrapped object is an integer.
+
+    It could either be W_Integer or an constant integer.
+    """
     return (isinstance(w_obj, W_Integer) or
-            isinstance(w_obj, W_Constant) and isinstance(w_obj.value, int))
+            (isinstance(w_obj, W_Constant) and
+             isinstance(w_obj.value, (int, long))))


More information about the Pypy-commit mailing list