Author: Armin Rigo <ar...@tunes.org>
Branch: py3.5-corowrapper
Changeset: r87138:7b5b1a370414
Date: 2016-09-16 16:16 +0200
http://bitbucket.org/pypy/pypy/changeset/7b5b1a370414/

Log:    rewrite rewrite rewrite in progress

diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -1,6 +1,6 @@
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter.error import OperationError, oefmt
-from pypy.interpreter.pyopcode import LoopBlock
+from pypy.interpreter.pyopcode import LoopBlock, SApplicationException
 from pypy.interpreter.pycode import CO_YIELD_INSIDE_TRY
 from pypy.interpreter.astcompiler import consts
 from rpython.rlib import jit
@@ -9,6 +9,9 @@
 class GeneratorOrCoroutine(W_Root):
     _immutable_fields_ = ['pycode']
 
+    w_yielded_from = None
+    thrown_operr = None
+
     def __init__(self, frame, name=None, qualname=None):
         self.space = frame.space
         self.frame = frame     # turned into None when frame_finished_execution
@@ -75,23 +78,26 @@
                 assert False
         self.running = self.space.is_true(w_running)
 
-    def descr_send(self, w_arg=None):
+    def descr_send(self, w_arg):
         """send(arg) -> send 'arg' into generator/coroutine,
 return next yielded value or raise StopIteration."""
         return self.send_ex(w_arg)
 
-    def send_ex(self, w_arg, operr=None):
+    def send_ex(self, w_arg_or_err):
+        assert w_arg_or_err is not None
         pycode = self.pycode
         if pycode is not None:
             if jit.we_are_jitted() and should_not_inline(pycode):
-                generatorentry_driver.jit_merge_point(gen=self, w_arg=w_arg,
-                                                    operr=operr, pycode=pycode)
-        return self._send_ex(w_arg, operr)
+                generatorentry_driver.jit_merge_point(gen=self,
+                                                      w_arg=w_arg_or_err,
+                                                      pycode=pycode)
+        return self._send_ex(w_arg_or_err)
 
-    def _send_ex(self, w_arg, operr):
+    def _send_ex(self, w_arg_or_err):
         space = self.space
         if self.running:
             raise oefmt(space.w_ValueError, "%s already executing", self.KIND)
+
         frame = self.frame
         if frame is None:
             if isinstance(self, Coroutine):
@@ -99,46 +105,64 @@
                             "cannot reuse already awaited coroutine")
             # xxx a bit ad-hoc, but we don't want to go inside
             # execute_frame() if the frame is actually finished
-            if operr is None:
+            if isinstance(w_arg_or_err, SApplicationException):
+                operr = w_arg_or_err.operr
+            else:
                 operr = OperationError(space.w_StopIteration, space.w_None)
             raise operr
 
+        w_result = self._invoke_execute_frame(frame, w_arg_or_err)
+
+        # if the frame is now marked as finished, it was RETURNed from
+        if frame.frame_finished_execution:
+            self.frame = None
+            if space.is_none(w_result):
+                # Delay exception instantiation if we can
+                raise OperationError(space.w_StopIteration, space.w_None)
+            else:
+                raise OperationError(space.w_StopIteration,
+                                     space.newtuple([w_result]))
+        else:
+            return w_result     # YIELDed
+
+    def _invoke_execute_frame(self, frame, w_arg_or_err):
+        space = self.space
+        self.running = True
+        try:
+            w_result = frame.execute_frame(self, w_arg_or_err)
+        except OperationError as e:
+            # errors finish a frame
+            try:
+                if e.match(space, space.w_StopIteration):
+                    self._leak_stopiteration(e)
+            finally:
+                self.frame = None
+            raise
+        finally:
+            frame.f_backref = jit.vref_None
+            self.running = False
+        return w_result
+
+    def resume_execute_frame(self, frame, w_arg_or_err):
+        # Called from execute_frame() just before resuming the bytecode
+        # interpretation.
+        space = self.space
+        if self.w_yielded_from is not None:
+            XXX
+
+        if isinstance(w_arg_or_err, SApplicationException):
+            ec = space.getexecutioncontext()
+            return frame.handle_operation_error(ec, w_arg_or_err.operr)
+
         last_instr = jit.promote(frame.last_instr)
         if last_instr == -1:
-            if w_arg and not space.is_w(w_arg, space.w_None):
+            if not space.is_w(w_arg_or_err, space.w_None):
                 raise oefmt(space.w_TypeError,
                             "can't send non-None value to a just-started %s",
                             self.KIND)
         else:
-            if not w_arg:
-                w_arg = space.w_None
-        self.running = True
-        try:
-            try:
-                w_result = frame.execute_frame(w_arg, operr)
-            except OperationError as e:
-                # errors finish a frame
-                try:
-                    if e.match(space, space.w_StopIteration):
-                        self._leak_stopiteration(e)
-                finally:
-                    self.frame = None
-                raise
-            #
-            # if the frame is now marked as finished, it was RETURNed from
-            if frame.frame_finished_execution:
-                self.frame = None
-                if space.is_none(w_result):
-                    # Delay exception instantiation if we can
-                    raise OperationError(space.w_StopIteration, space.w_None)
-                else:
-                    raise OperationError(space.w_StopIteration,
-                                         space.newtuple([w_result]))
-            else:
-                return w_result     # YIELDed
-        finally:
-            frame.f_backref = jit.vref_None
-            self.running = False
+            frame.pushvalue(w_arg_or_err)
+        return last_instr + 1
 
     def _leak_stopiteration(self, e):
         # Check for __future__ generator_stop and conditionally turn
@@ -166,22 +190,10 @@
             w_val = self.space.w_None
         return self.throw(w_type, w_val, w_tb)
 
-    def _get_yield_from(self):
-        # Probably a hack (but CPython has the same, _PyGen_yf()):
-        # If the current frame is stopped in a "yield from",
-        # return the paused generator.
-        # XXX this is probably very bad for the JIT.  Think again!
-        if not self.frame:
-            return None
-        co_code = self.frame.pycode.co_code
-        opcode = ord(co_code[self.frame.last_instr + 1])
-        if opcode == YIELD_FROM:
-            return self.frame.peekvalue()
-
     def throw(self, w_type, w_val, w_tb):
         space = self.space
 
-        w_yf = self._get_yield_from()
+        w_yf = self.w_yielded_from
         if w_yf is not None:
             # Paused in a "yield from", pass the throw to the inner generator.
             return self._throw_delegate(space, w_yf, w_type, w_val, w_tb)
@@ -252,7 +264,7 @@
                                space.wrap('__traceback__'))
             if not space.is_w(tb, space.w_None):
                 operr.set_traceback(tb)
-        return self.send_ex(space.w_None, operr)
+        return self.send_ex(SApplicationException(operr))
 
     def descr_close(self):
         """close() -> raise GeneratorExit inside generator/coroutine."""
@@ -346,7 +358,8 @@
                     jitdriver.jit_merge_point(self=self, frame=frame,
                                               results=results, pycode=pycode)
                     try:
-                        w_result = frame.execute_frame(space.w_None)
+                        w_result = self._invoke_execute_frame(
+                                                frame, space.w_None)
                     except OperationError as e:
                         if not e.match(space, space.w_StopIteration):
                             raise
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -261,11 +261,11 @@
         else:
             return self.execute_frame()
 
-    def execute_frame(self, w_inputvalue=None, operr=None):
+    def execute_frame(self, in_generator=None, w_arg_or_err=None):
         """Execute this frame.  Main entry point to the interpreter.
-        The optional arguments are there to handle a generator's frame:
-        w_inputvalue is for generator.send() and operr is for
-        generator.throw().
+        'in_generator' is non-None iff we are starting or resuming
+        a generator or coroutine frame; in that case, w_arg_or_err
+        is the input argument -or- an SApplicationException instance.
         """
         # the following 'assert' is an annotation hint: it hides from
         # the annotator all methods that are defined in PyFrame but
@@ -279,17 +279,16 @@
         try:
             executioncontext.call_trace(self)
             #
-            if operr is not None:
-                ec = self.space.getexecutioncontext()
-                next_instr = self.handle_operation_error(ec, operr)
-                self.last_instr = intmask(next_instr - 1)
+            # Execution starts just after the last_instr.  Initially,
+            # last_instr is -1.  After a generator suspends it points to
+            # the YIELD_VALUE instruction.
+            if in_generator is None:
+                assert self.last_instr == -1
+                next_instr = 0
             else:
-                # Execution starts just after the last_instr.  Initially,
-                # last_instr is -1.  After a generator suspends it points to
-                # the YIELD_VALUE instruction.
-                next_instr = r_uint(self.last_instr + 1)
-                if next_instr != 0:
-                    self.pushvalue(w_inputvalue)
+                next_instr = in_generator.resume_execute_frame(
+                                                self, w_arg_or_err)
+            next_instr = r_uint(next_instr)
             #
             try:
                 w_exitvalue = self.dispatch(self.pycode, next_instr,
@@ -898,8 +897,14 @@
             frame = frame.f_backref()
         return None
 
+    def get_generator(self):
+        if space.config.translation.rweakref:
+            return self.f_generator_wref()
+        else:
+            return self.f_generator_nowref
+
     def descr_clear(self, space):
-        # Clears a random subset of the attributes (e.g. some the fast
+        # Clears a random subset of the attributes (e.g. the fast
         # locals, but not f_locals).  Also clears last_exception, which
         # is not quite like CPython when it clears f_exc_* (however
         # there might not be an observable difference).
@@ -907,10 +912,7 @@
             if not self._is_generator_or_coroutine():
                 raise oefmt(space.w_RuntimeError,
                             "cannot clear an executing frame")
-            if space.config.translation.rweakref:
-                gen = self.f_generator_wref()
-            else:
-                gen = self.f_generator_nowref
+            gen = self.get_generator()
             if gen is not None:
                 if gen.running:
                     raise oefmt(space.w_RuntimeError,
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -71,11 +71,11 @@
         try:
             while True:
                 next_instr = self.handle_bytecode(co_code, next_instr, ec)
+        except Return:
+            self.last_exception = None
         except Yield:
-            return self.popvalue()
-        except ExitFrame:
-            self.last_exception = None
-            return self.popvalue()
+            pass
+        return self.popvalue()
 
     def handle_bytecode(self, co_code, next_instr, ec):
         try:
@@ -1530,15 +1530,11 @@
         
 ### ____________________________________________________________ ###
 
-class ExitFrame(Exception):
-    pass
-
-
-class Return(ExitFrame):
+class Return(Exception):
     """Raised when exiting a frame via a 'return' statement."""
 
 
-class Yield(ExitFrame):
+class Yield(Exception):
     """Raised when exiting a frame via a 'yield' statement."""
 
 
diff --git a/pypy/interpreter/test/test_generator.py 
b/pypy/interpreter/test/test_generator.py
--- a/pypy/interpreter/test/test_generator.py
+++ b/pypy/interpreter/test/test_generator.py
@@ -57,13 +57,16 @@
         def f():
             yield 2
         g = f()
+        next(g)
         raises(NameError, g.throw, NameError, "Error")
+        raises(StopIteration, next, g)
 
     def test_throw2(self):
         def f():
             yield 2
         g = f()
         raises(NameError, g.throw, NameError("Error"))
+        raises(StopIteration, next, g)
 
     def test_throw3(self):
         def f():
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -795,18 +795,18 @@
     gi_running = interp_attrproperty('running', cls=GeneratorIterator),
     gi_frame   = GetSetProperty(GeneratorIterator.descr_gicr_frame),
     gi_code    = interp_attrproperty_w('pycode', cls=GeneratorIterator),
-    gi_yieldfrom = XXX,
+    gi_yieldfrom=interp_attrproperty_w('w_yielded_from', 
cls=GeneratorIterator),
     __name__   = GetSetProperty(GeneratorIterator.descr__name__),
     __qualname__ = GetSetProperty(GeneratorIterator.descr__qualname__),
     __weakref__ = make_weakref_descr(GeneratorIterator),
 )
 assert not GeneratorIterator.typedef.acceptable_as_base_class  # no __new__
 
-CoroutineWrapper.typedef = TypeDef("coroutine_wrapper",
-    __anext__   = interp2app(CoroutineWrapper.descr_next,
-                            descrmismatch='__anext__'),
-)
-assert not CoroutineWrapper.typedef.acceptable_as_base_class  # no __new__
+#CoroutineWrapper.typedef = TypeDef("coroutine_wrapper",
+#    __anext__   = interp2app(CoroutineWrapper.descr_next,
+#                            descrmismatch='__anext__'),
+#)
+#assert not CoroutineWrapper.typedef.acceptable_as_base_class  # no __new__
 
 Coroutine.typedef = TypeDef("coroutine",
     __repr__   = interp2app(Coroutine.descr__repr__),
@@ -823,7 +823,7 @@
     cr_running = interp_attrproperty('running', cls=Coroutine),
     cr_frame   = GetSetProperty(Coroutine.descr_gicr_frame),
     cr_code    = interp_attrproperty_w('pycode', cls=Coroutine),
-    cr_await   = XXX,
+    cr_await   = interp_attrproperty_w('w_yielded_from', cls=Coroutine),
     __name__   = GetSetProperty(Coroutine.descr__name__),
     __qualname__ = GetSetProperty(Coroutine.descr__qualname__),
     __weakref__ = make_weakref_descr(Coroutine),
diff --git a/pypy/module/_continuation/interp_pickle.py 
b/pypy/module/_continuation/interp_pickle.py
--- a/pypy/module/_continuation/interp_pickle.py
+++ b/pypy/module/_continuation/interp_pickle.py
@@ -1,6 +1,6 @@
 from pypy.tool import stdlib_opcode as pythonopcode
 from rpython.rlib import jit
-from pypy.interpreter.error import OperationError
+from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.pyframe import PyFrame
 from pypy.module._continuation.interp_continuation import State, global_state
 from pypy.module._continuation.interp_continuation import build_sthread
@@ -34,6 +34,8 @@
     return space.newtuple(args)
 
 def setstate(self, w_args):
+    raise oefmt(space.w_NotImplementedError,
+                "continulet.__setstate__() needs to be fixed")
     space = self.space
     if self.sthread is not None:
         raise geterror(space, "continulet.__setstate__() on an already-"
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to