# HG changeset patch # User Paul Boddie # Date 1298226710 -3600 # Node ID 62d65b1b793669ef9d0431975db65250f69e2847 # Parent 7cc5058cbfb6d2be3308c6ad98079ca22a92e874 Improved exception block tracking in order to make sure that such blocks are discarded when program units are exited. Added a stack to track invocation frame state when handlers are defined, although this could arguably be merged with the locals frame state handling. diff -r 7cc5058cbfb6 -r 62d65b1b7936 docs/exceptions.txt --- a/docs/exceptions.txt Sun Feb 20 01:35:15 2011 +0100 +++ b/docs/exceptions.txt Sun Feb 20 19:31:50 2011 +0100 @@ -47,5 +47,6 @@ PushHandler(block) defines an active handler at the location indicated by the given block. -PopHandler removes the active handler at or after the location indicated by -the previously given block. +PopHandler(n) removes the n topmost active handlers. A single handler is +typically removed when leaving a try block, but potentially more handlers are +removed when such a block is exited using a return statement. diff -r 7cc5058cbfb6 -r 62d65b1b7936 micropython/ast.py --- a/micropython/ast.py Sun Feb 20 01:35:15 2011 +0100 +++ b/micropython/ast.py Sun Feb 20 19:31:50 2011 +0100 @@ -3,7 +3,7 @@ """ Translate the AST of a Python program into a more interpretable representation. -Copyright (C) 2007, 2008, 2009 Paul Boddie +Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -123,6 +123,7 @@ self.unit = self.module self.reset() + self.add_exception_unit() block = self.new_block() self.set_block(block) @@ -142,9 +143,10 @@ if self.module.name == "__main__": self.set_block(handler_block) - self.new_op(PopHandler()) + self.new_op(PopHandler(1)) self.new_op(Return()) + self.drop_exception_unit() self.unit.temp_usage = self.max_temp_position + 1 self.unit.blocks = self.blocks return self.blocks @@ -155,6 +157,7 @@ self.unit = unit self.reset() + self.add_exception_unit() block = self.new_block() self.set_block(block) @@ -162,6 +165,7 @@ if unit.astnode is not None: self.dispatch(unit.astnode) + self.drop_exception_unit() self.unit.temp_usage = self.max_temp_position + 2 # include space for instantiators to expand backwards self.unit.blocks = self.blocks return self.blocks @@ -698,6 +702,7 @@ # Handle exceptions when calling "next"... + self.add_exception_blocks(next_handler_block, end_handler_block) self.new_op(PushHandler(next_handler_block)) # Use the iterator to get the next value. @@ -715,13 +720,17 @@ # Skip the handler where the call was successful. - self.new_op(PopHandler()) + self.new_op(PopHandler(1)) self.new_op(Jump(end_handler_block)) # Enter the exception handler. self.set_block(next_handler_block) - self.new_op(PopHandler()) + self.new_op(PopHandler(1)) + + # Disable the handlers. + + self.drop_exception_blocks() # Test for StopIteration. @@ -846,6 +855,11 @@ if self.in_exception_handler: self.new_op(ClearException()) + # NOTE: Support finally blocks here. + + if self.exception_blocks[-1]: + self.new_op(PopHandler(len(self.exception_blocks[-1]))) + self.new_op(Return()) def visitTryExcept(self, node): @@ -860,7 +874,7 @@ self.new_op(PushHandler(handler_block)) self.dispatch(node.body) - self.new_op(PopHandler()) + self.new_op(PopHandler(1)) if node.else_ is not None: self.new_op(Jump(else_block)) @@ -870,7 +884,11 @@ # Start of handlers. self.set_block(handler_block) - self.new_op(PopHandler()) + self.new_op(PopHandler(1)) + + # Disable the handlers. + + self.drop_exception_blocks() for name, assignment, handler in node.handlers: next_block = self.new_block() @@ -918,7 +936,6 @@ self.set_block(exit_block) self.new_op(ClearException()) - self.drop_exception_blocks() def visitTryFinally(self, node): diff -r 7cc5058cbfb6 -r 62d65b1b7936 micropython/rsvp.py --- a/micropython/rsvp.py Sun Feb 20 01:35:15 2011 +0100 +++ b/micropython/rsvp.py Sun Feb 20 19:31:50 2011 +0100 @@ -625,8 +625,8 @@ "Push an exception handler onto the handler stack." cost = 3 -class PopHandler(Instruction): - "Pop an exception handler from the handler stack." +class PopHandler(Immediate): + "Pop exception handlers from the handler stack." cost = 3 class CheckException(Instruction): diff -r 7cc5058cbfb6 -r 62d65b1b7936 micropython/trans.py --- a/micropython/trans.py Sun Feb 20 01:35:15 2011 +0100 +++ b/micropython/trans.py Sun Feb 20 19:31:50 2011 +0100 @@ -113,13 +113,19 @@ def drop_loop_blocks(self): self.loop_blocks.pop() + def add_exception_unit(self): + self.exception_blocks.append([]) + def get_exception_blocks(self): - return self.exception_blocks[-1] + return self.exception_blocks[-1][-1] def add_exception_blocks(self, handler_block, exit_block): - self.exception_blocks.append((handler_block, exit_block)) + self.exception_blocks[-1].append((handler_block, exit_block)) def drop_exception_blocks(self): + self.exception_blocks[-1].pop() + + def drop_exception_unit(self): self.exception_blocks.pop() # Assignment expression values. diff -r 7cc5058cbfb6 -r 62d65b1b7936 rsvp.py --- a/rsvp.py Sun Feb 20 01:35:15 2011 +0100 +++ b/rsvp.py Sun Feb 20 19:31:50 2011 +0100 @@ -110,8 +110,13 @@ self.frame_stack = [] self.local_sp_stack = [0] self.invocation_sp_stack = [] + + # Exception handler stacks are used to reset the state of the main + # stacks. + self.handler_stack = [len(self.memory) - 1] # final handler is the end of the code self.handler_local_sp_stack = [] + self.handler_invocation_sp_stack = [] self.handler_pc_stack = [] # Registers. @@ -747,14 +752,26 @@ def PushHandler(self): self.handler_stack.append(self.operand) self.handler_local_sp_stack.append(len(self.local_sp_stack)) + self.handler_invocation_sp_stack.append(len(self.invocation_sp_stack)) self.handler_pc_stack.append(len(self.pc_stack)) def PopHandler(self): + nframes = self.operand + # Get the new local frame pointer and PC stack references. + local_sp_top = self.handler_local_sp_stack[-nframes] + invocation_sp_top = self.handler_invocation_sp_stack[-nframes] + pc_top = self.handler_pc_stack[-nframes] # Reduce the local frame pointer stack to refer to the handler's frame. - del self.local_sp_stack[self.handler_local_sp_stack.pop():] + del self.local_sp_stack[local_sp_top:] + # Reduce the invocation frame pointer stack to refer to an outer frame. + del self.invocation_sp_stack[invocation_sp_top:] # Reduce the PC stack to discard all superfluous return addresses. - self.pc_stack = self.pc_stack[:self.handler_pc_stack.pop()] - self.handler_stack.pop() + del self.pc_stack[pc_top:] + # Remove elements from the handler stacks. + del self.handler_pc_stack[-nframes:] + del self.handler_local_sp_stack[-nframes:] + del self.handler_invocation_sp_stack[-nframes:] + del self.handler_stack[-nframes:] def CheckException(self): self.status = self.exception is not None and self._CheckInstance(self.exception, self.value.ref)