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