[pypy-svn] r67466 - in pypy/branch/spine-of-frames/pypy/interpreter: . test

cfbolz at codespeak.net cfbolz at codespeak.net
Fri Sep 4 11:41:23 CEST 2009


Author: cfbolz
Date: Fri Sep  4 11:41:23 2009
New Revision: 67466

Modified:
   pypy/branch/spine-of-frames/pypy/interpreter/executioncontext.py
   pypy/branch/spine-of-frames/pypy/interpreter/pyframe.py
   pypy/branch/spine-of-frames/pypy/interpreter/test/test_executioncontext.py
Log:
(pedronis, cfbolz, arigo watching): write really explicit tests about the frame handling. Also refactor 
things so that all the obscure stuff is living in one place, in some methods on the execution context.


Modified: pypy/branch/spine-of-frames/pypy/interpreter/executioncontext.py
==============================================================================
--- pypy/branch/spine-of-frames/pypy/interpreter/executioncontext.py	(original)
+++ pypy/branch/spine-of-frames/pypy/interpreter/executioncontext.py	Fri Sep  4 11:41:23 2009
@@ -17,10 +17,7 @@
 
     def __init__(self, space):
         self.space = space
-        # 'some_frame' points to any frame from this thread's frame stack
-        # (although in general it should point to the top one).
-        self.some_frame = None
-        self.framestackdepth = 0
+        self._init_frame_chain()
         # tracing: space.frame_trace_action.fire() must be called to ensure
         # that tracing occurs whenever self.w_tracefunc or self.is_tracing
         # is modified.
@@ -54,29 +51,122 @@
         if self.framestackdepth > self.space.sys.recursionlimit:
             raise OperationError(self.space.w_RuntimeError,
                                  self.space.wrap("maximum recursion depth exceeded"))
+        self._chain(frame)
+
+    def leave(self, frame):
+        if self.profilefunc:
+            self._trace(frame, 'leaveframe', self.space.w_None)
+
+        self._unchain(frame)
+        
+        if self.w_tracefunc is not None and not frame.hide():
+            self.space.frame_trace_action.fire()
+
+    # ________________________________________________________________
+    # the methods below are used for chaining frames in JIT-friendly way
+    # part of that stuff is obscure
+
+    def _init_frame_chain(self):
+        # 'some_frame' points to any frame from this thread's frame stack
+        # (although in general it should point to the top one).
+        self.some_frame = None
+        self.framestackdepth = 0
+
+    @staticmethod
+    def _init_chaining_attributes(frame):
+        """
+        explanation of the f_back handling:
+        -----------------------------------
+
+        in the non-JIT case, the frames simply form a doubly linked list via the
+        attributes f_back_some and f_forward.
+
+        When the JIT is used, things become more complex, as functions can be
+        inlined into each other. In this case a frame chain can look like this:
+
+        +---------------+
+        | real_frame    |
+        +---------------+
+           |      ^
+           | f_back_some
+           |      |
+           |      | f_forward
+           |  +--------------+
+           |  | virtual frame|
+           |  +--------------+
+           |      ^ 
+           |      | f_forward
+           |  +--------------+
+           |  | virtual frame|
+           |  +--------------+
+           |      ^
+           |      |
+           v      | f_forward
+        +---------------+
+        | real_frame    |
+        +---------------+
+           |
+           |
+           v
+          ...
+        
+        This ensures that the virtual frames don't escape via the f_back of the
+        real frames. For the same reason, the executioncontext's some_frame
+        attribute should only point to real frames.
+
+        All places where a frame can become accessed from applevel-code (like
+        sys._getframe and traceback catching) need to call force_f_back to ensure
+        that the intermediate virtual frames are forced to be real ones.
+
+        """ 
+        frame.f_back_some = None
+        frame.f_forward = None
+        frame.f_back_forced = False
+
+    def _chain(self, frame):
         self.framestackdepth += 1
         #
         frame.f_back_some = self.some_frame
         curtopframe = self.gettopframe()
         if curtopframe is not None:
             curtopframe.f_forward = frame
-        if not we_are_jitted():
+        if not self._we_are_jitted():
             self.some_frame = frame
 
-    def leave(self, frame):
-        if self.profilefunc:
-            self._trace(frame, 'leaveframe', self.space.w_None)
-
+    def _unchain(self, frame):
         #assert frame is self.gettopframe() --- slowish
         f_back = frame.f_back()
         if f_back is not None:
             f_back.f_forward = None
-        if not we_are_jitted() or self.some_frame is frame:
+        if not self._we_are_jitted() or self.some_frame is frame:
             self.some_frame = frame.f_back_some
         self.framestackdepth -= 1
-        
-        if self.w_tracefunc is not None and not frame.hide():
-            self.space.frame_trace_action.fire()
+
+    @staticmethod
+    def _extract_back_from_frame(frame):
+        back_some = frame.f_back_some
+        if frame.f_back_forced:
+            # don't check back_some.f_forward in this case
+            return back_some
+        if back_some is None:
+            return None
+        while back_some.f_forward is not frame:
+            back_some = back_some.f_forward
+        return back_some
+
+    @staticmethod
+    def _force_back_of_frame(frame):
+        orig_frame = frame
+        while frame is not None and not frame.f_back_forced:
+            frame.f_back_some = f_back = ExecutionContext._extract_back_from_frame(frame)
+            frame.f_back_forced = True
+            frame = f_back
+        return orig_frame.f_back_some
+
+    _we_are_jitted = staticmethod(we_are_jitted) # indirection for testing
+    
+    # the methods above are used for chaining frames in JIT-friendly way
+    # ________________________________________________________________
 
 
     class Subcontext(object):

Modified: pypy/branch/spine-of-frames/pypy/interpreter/pyframe.py
==============================================================================
--- pypy/branch/spine-of-frames/pypy/interpreter/pyframe.py	(original)
+++ pypy/branch/spine-of-frames/pypy/interpreter/pyframe.py	Fri Sep  4 11:41:23 2009
@@ -5,6 +5,7 @@
 from pypy.interpreter import eval, baseobjspace, pycode
 from pypy.interpreter.argument import Arguments, ArgumentsFromValuestack
 from pypy.interpreter.error import OperationError
+from pypy.interpreter.executioncontext import ExecutionContext
 from pypy.interpreter import pytraceback
 import opcode
 from pypy.rlib.objectmodel import we_are_translated, instantiate
@@ -34,60 +35,12 @@
      * 'valuestack_w', 'blockstack', control the interpretation
     """
     
-    """
-    explanation of the f_back handling:
-    -----------------------------------
-
-    in the non-JIT case, the frames simply form a doubly linked list via the
-    attributes f_back_some and f_forward.
-
-    When the JIT is used, things become more complex, as functions can be
-    inlined into each other. In this case a frame chain can look like this:
-
-    +---------------+
-    | real_frame    |
-    +---------------+
-       |      ^
-       | f_back_some
-       |      |
-       |      | f_forward
-       |  +--------------+
-       |  | virtual frame|
-       |  +--------------+
-       |      ^ 
-       |      | f_forward
-       |  +--------------+
-       |  | virtual frame|
-       |  +--------------+
-       |      ^
-       |      |
-       v      | f_forward
-    +---------------+
-    | real_frame    |
-    +---------------+
-       |
-       |
-       v
-      ...
-    
-    This ensures that the virtual frames don't escape via the f_back of the
-    real frames. For the same reason, the executioncontext's some_frame
-    attribute should only point to real frames.
-
-    All places where a frame can become accessed from applevel-code (like
-    sys._getframe and traceback catching) need to call force_f_back to ensure
-    that the intermediate virtual frames are forced to be real ones.
-
-    """ 
 
     __metaclass__ = extendabletype
 
     frame_finished_execution = False
     last_instr               = -1
     last_exception           = None
-    f_back_some              = None    # these two should be modified together
-    f_forward                = None    # they make a sort of doubly-linked list
-    f_back_forced            = False
     w_f_trace                = None
     # For tracing
     instr_lb                 = 0
@@ -111,6 +64,7 @@
         self.fastlocals_w = [None]*self.numlocals
         make_sure_not_resized(self.fastlocals_w)
         self.f_lineno = code.co_firstlineno
+        ExecutionContext._init_chaining_attributes(self)
 
     def get_builtin(self):
         if self.space.config.objspace.honor__builtins__:
@@ -457,23 +411,10 @@
         pass
 
     def f_back(self):
-        back_some = self.f_back_some
-        if self.f_back_forced:
-            # don't check back_some.f_forward in this case
-            return back_some
-        if back_some is None:
-            return None
-        while back_some.f_forward is not self:
-            back_some = back_some.f_forward
-        return back_some
-    
-    def force_f_back(self):
-        self.f_back_some = f_back = self.f_back()
-        self.f_back_forced = True
-        if f_back is not None:
-            f_back.force_f_back()
-        return f_back
+        return ExecutionContext._extract_back_from_frame(self)
 
+    def force_f_back(self):
+        return ExecutionContext._force_back_of_frame(self)
 
     ### line numbers ###
 

Modified: pypy/branch/spine-of-frames/pypy/interpreter/test/test_executioncontext.py
==============================================================================
--- pypy/branch/spine-of-frames/pypy/interpreter/test/test_executioncontext.py	(original)
+++ pypy/branch/spine-of-frames/pypy/interpreter/test/test_executioncontext.py	Fri Sep  4 11:41:23 2009
@@ -1,5 +1,6 @@
 import py
 from pypy.interpreter import executioncontext
+from pypy.interpreter.executioncontext import ExecutionContext
 from pypy.conftest import gettestobjspace
 
 class Finished(Exception):
@@ -219,3 +220,216 @@
         """)
         events = space.unwrap(w_events)
         assert [i[0] for i in events] == ['c_call', 'c_return', 'c_call']
+
+class TestFrameChaining(object):
+    class EC(ExecutionContext):
+        def __init__(self, jitted=False):
+            self.jitted = jitted
+            self._init_frame_chain()
+
+        def _we_are_jitted(self):
+            return self.jitted
+
+    class Frame(object):
+        def __init__(self):
+            ExecutionContext._init_chaining_attributes(self)
+
+        def f_back(self):
+            return ExecutionContext._extract_back_from_frame(self)
+
+        def force_f_back(self):
+            return ExecutionContext._force_back_of_frame(self)
+
+    def test_frame_chain(self):
+
+        ec = self.EC()
+
+        assert ec.some_frame is None
+        assert ec.framestackdepth == 0
+
+        frame = self.Frame()
+        ec._chain(frame)
+        assert ec.some_frame is frame
+        assert ec.framestackdepth == 1
+        assert frame.f_back_some is None
+        assert frame.f_forward is None
+        
+        frame2 = self.Frame()
+        ec._chain(frame2)
+        assert ec.some_frame is frame2
+        assert ec.framestackdepth == 2
+        assert frame2.f_back_some is frame
+        assert frame.f_forward is frame2
+        assert frame2.f_forward is None
+       
+        frame3 = self.Frame()
+        ec._chain(frame3)
+        assert ec.some_frame is frame3
+        assert frame3.f_back_some is frame2
+        assert frame2.f_forward is frame3
+        # now we should unchain
+
+        assert frame3.f_back() is frame2
+        ec._unchain(frame3)
+        assert ec.some_frame is frame2
+        assert ec.framestackdepth == 2
+        assert frame2.f_forward is None
+        assert frame3.f_back_some is frame2
+        
+        assert frame2.f_back() is frame
+        ec._unchain(frame2)
+        assert ec.some_frame is frame
+        assert ec.framestackdepth == 1
+        assert frame.f_forward is None
+        assert frame2.f_back_some is frame
+
+        assert frame.f_back() is None
+        ec._unchain(frame)
+        assert ec.some_frame is None
+        assert ec.framestackdepth == 0
+        assert frame.f_back_some is None
+
+    # we want a simplified copy of the previous tests and do a force on frame2 ok
+    def test_frame_chain_forced(self):
+
+        ec = self.EC()
+
+        frame = self.Frame()
+        ec._chain(frame)
+        
+        frame2 = self.Frame()
+        ec._chain(frame2)
+        assert ec.some_frame is frame2
+        assert ec.framestackdepth == 2
+        assert frame2.f_back_some is frame
+        assert frame.f_forward is frame2
+        assert frame2.f_forward is None
+        res = frame2.force_f_back()
+        assert res is frame
+        assert frame.f_back_forced
+        
+        frame3 = self.Frame()
+        ec._chain(frame3)
+        # now we should unchain
+
+        assert frame3.f_back() is frame2
+        ec._unchain(frame3)
+        assert ec.some_frame is frame2
+        assert frame3.f_back_some is frame2
+        
+        assert frame2.f_back() is frame
+        ec._unchain(frame2)
+        assert frame2.f_back_some is frame
+
+        assert frame.f_back() is None
+        ec._unchain(frame)
+        assert ec.some_frame is None
+        assert frame.f_back_some is None
+
+        assert frame2.f_back() is frame
+        assert frame.f_back() is None
+
+    def test_frame_chain_jitted(self):
+
+        ec = self.EC()
+
+        assert ec.some_frame is None
+        assert ec.framestackdepth == 0
+
+        frame = self.Frame()
+        ec._chain(frame)
+        assert ec.some_frame is frame
+        assert ec.framestackdepth == 1
+        assert frame.f_back_some is None
+        assert frame.f_forward is None
+        
+        ec.jitted = True
+        frame2 = self.Frame()
+        ec._chain(frame2)
+        assert ec.some_frame is frame
+        assert ec.framestackdepth == 2
+        assert frame2.f_back_some is frame
+        assert frame.f_forward is frame2
+        assert frame2.f_forward is None
+
+        # recursive enter/leave seen by the jit
+        frame3 = self.Frame()
+        ec._chain(frame3)
+        assert ec.some_frame is frame
+        assert frame3.f_back_some is frame
+        assert frame2.f_forward is frame3
+        # now we should unchain
+
+        assert frame3.f_back() is frame2
+        ec._unchain(frame3)
+        assert ec.some_frame is frame
+        assert ec.framestackdepth == 2
+        assert frame2.f_forward is None
+        assert frame3.f_back_some is frame
+       
+        # recursive enter/leave not seen by the jit
+        ec.jitted = False
+        ec._chain(frame3)
+        assert ec.some_frame is frame3
+        assert frame3.f_back_some is frame
+        assert frame2.f_forward is frame3 # this btw is the bit that may be problematic xxx
+        # now we should unchain
+
+        assert frame3.f_back() is frame2
+        ec._unchain(frame3)
+        assert ec.some_frame is frame
+        assert ec.framestackdepth == 2
+        assert frame2.f_forward is None
+        assert frame3.f_back_some is frame
+        ec.jitted = True
+
+        assert frame2.f_back() is frame
+        ec._unchain(frame2)
+        assert ec.some_frame is frame
+        assert ec.framestackdepth == 1
+        assert frame.f_forward is None
+        assert frame2.f_back_some is frame
+
+        ec.jitted = False
+        assert frame.f_back() is None
+        ec._unchain(frame)
+        assert ec.some_frame is None
+        assert ec.framestackdepth == 0
+        assert frame.f_back_some is None
+
+
+    def test_frame_chain_jitted_forced(self):
+
+        ec = self.EC()
+
+        assert ec.some_frame is None
+        assert ec.framestackdepth == 0
+
+        frame = self.Frame()
+        ec._chain(frame)
+        
+        ec.jitted = True
+        frame2 = self.Frame()
+        ec._chain(frame2)
+
+        # recursive enter/leave seen by the jit
+        frame3 = self.Frame()
+        ec._chain(frame3)
+        res = frame3.force_f_back()
+        assert res is frame2
+
+        assert frame3.f_back() is frame2
+        ec._unchain(frame3)
+      
+        assert frame2.f_back() is frame
+        ec._unchain(frame2)
+        ec.jitted = False
+        assert frame.f_back() is None
+        ec._unchain(frame)
+
+        assert frame3.f_back() is frame2
+        assert frame2.f_back() is frame
+        assert frame.f_back() is None
+         # cool yes
+
+     



More information about the Pypy-commit mailing list