# HG changeset patch # User Paul Boddie # Date 1214678805 -7200 # Node ID 78240b642d1fbdca94b615b1123d49a7e44a9303 # Parent 2300627a338d2bca8b9387c4b04abfe504e49575 Added exception handler variable definition support to the inspection process. Introduced frame position tracking in order to more accurately track and assign stack element usage, employing special methods to be used around function invocation code generation. Added some support for raise statement code generation. Simplified the remove_op_using_stack method and made more use of the actual stack element information found in StackLoad operations. Introduced a LoadResult instruction. Added a test of simple exception raising and handling. diff -r 2300627a338d -r 78240b642d1f micropython/ast.py --- a/micropython/ast.py Sun Jun 22 02:21:39 2008 +0200 +++ b/micropython/ast.py Sat Jun 28 20:46:45 2008 +0200 @@ -74,6 +74,8 @@ self.code = None self.temp_position = 0 self.stack = [] + self.suspended_frame = [] + self.frame_positions = [] def __repr__(self): return "Translation(%r)" % self.module @@ -156,6 +158,20 @@ elif effect < 0: raise ProcessingError, "Cannot remove instructions which reduce the stack." + def make_frame(self): + self.frame_positions.append(len(self.stack)) + + def suspend_frame(self): + self.suspended_frame = self.stack[self.frame_positions[-1]:] + self.stack = self.stack[:self.frame_positions[-1]] + + def resume_frame(self): + self.stack += self.suspended_frame + self.suspended_frame = [] + + def drop_frame(self): + self.stack = self.stack[:self.frame_positions.pop()] + def new_label(self): "Return a new label object for use with set_label." @@ -241,23 +257,19 @@ self.remove_ops(1) self.new_op(op) - def remove_op_using_stack(self): + def remove_op_using_stack(self, op): "Remove the instruction which created the top stack position." - position, op = self.stack.pop() + position, op = self.stack[-1] - # NOTE: For now, just erase the instruction. - - self.code[position] = None + # NOTE: Prevent removal of non-end instructions. - op = self.code[-1] - while op is None: - del self.code[-1] - if self.code: - op = self.code[-1] - else: - break + if position < len(self.code) - 1: + return 0 + else: + self.remove_ops(1) + return 1 def last_ops(self, n): @@ -368,6 +380,7 @@ "Record the location of the invocation." self.new_op(MakeFrame()) # records the start of the frame + self.make_frame() def _generateCallFunc(self, args, node): @@ -551,10 +564,21 @@ # raise an exception. self.new_op(DropFrame()) + + # Pretend that the frame is now gone, generating suitable stack + # operations. + + self.suspend_frame() + self.new_op(LoadResult()) + self.dispatch(compiler.ast.Name("TypeError")) self.new_op(RaiseException()) self.set_label(continue_label) + # Obtain the suspended frame for subsequent outcomes. + + self.resume_frame() + first = 0 frame_pos += 1 @@ -607,17 +631,32 @@ else: self.new_op(CheckFrame()) - def _endCallFunc(self, temp): + def _doCallFunc(self, instructions): + + "Make the invocation." - "Make the invocation and tidy up afterwards." + self.new_ops(instructions) + self.new_op(JumpWithFrame()) - self.new_ops(temp) - self.new_op(JumpWithFrame()) + def _endCallFunc(self, instructions=None, keep_frame=0): + + "Finish the invocation and tidy up afterwards." # NOTE: Exception handling required. self.new_op(DropFrame()) - self.discard_temp(temp) + if keep_frame: + self.suspend_frame() + else: + self.drop_frame() + self.new_op(LoadResult()) + if keep_frame: + self.resume_frame() + + # Discard any temporary storage instructions. + + if instructions is not None: + self.discard_temp(instructions) def _visitName(self, node, classes): @@ -716,13 +755,14 @@ # NOTE: would require inspection of the stack operations. return isinstance(last, (LoadName, LoadTemp, LoadAddress, LoadConst)) - def _have_fixed_sources(self, access): + def _have_fixed_sources(self, instruction): access_ops = [] - for i in xrange(0, access): - position, op = self.stack[-1 - i] - if not isinstance(op, (LoadName, LoadTemp, LoadAddress, LoadConst)): - return 0 - access_ops.append(op) + for stack_op in instruction.accesses: + if isinstance(stack_op, StackLoad): + position, op = self.stack[stack_op.n] + if not isinstance(op, (LoadName, LoadTemp, LoadAddress, LoadConst)): + return None + access_ops.append((op, stack_op)) return access_ops # Optimisation methods. See the supported_optimisations class attribute. @@ -852,17 +892,19 @@ """ if self._should_optimise_stack_access(): - ops = self._have_fixed_sources(instruction.stack_access) - if ops: + ops = self._have_fixed_sources(instruction) + if ops is not None: #print "Optimised", instruction.accesses, "->", ops - for i in range(0, instruction.stack_access): - self.remove_op_using_stack() - instruction.accesses = ops + instruction.accesses = [] + for op, stack_op in ops: + if self.remove_op_using_stack(op): + instruction.accesses.append(op) - # Remove the stack side-effects of these accesses. + # Remove the stack side-effects of these accesses. - for op in ops: - op.remove_results() + op.remove_results() + else: + instruction.accesses.append(stack_op) # Visitor methods. @@ -913,13 +955,14 @@ # NOTE: No support for defaults. self.new_ops(temp) # Explicit context as first argument. - self._endCallFunc(temp_method) + self._doCallFunc(temp_method) + self._endCallFunc(temp_method, keep_frame=1) self.new_op(Jump(end_label)) # End method attempt. self.set_label(end_call_label) - self.new_op(DropFrame()) # From the method call. + self._endCallFunc() # From the method call. # Raise a TypeError. @@ -990,7 +1033,8 @@ self.new_ops(temp1) # Explicit context as first argument. self.new_ops(temp2) - self._endCallFunc(temp_method) + self._doCallFunc(temp_method) + self._endCallFunc(temp_method, keep_frame=1) # Test for NotImplemented. # Don't actually raise an exception. @@ -1004,7 +1048,7 @@ # End left method attempt. self.set_label(end_left_label) - self.new_op(DropFrame()) # From the left method call. + self._endCallFunc() # From the left method call. # Right method. @@ -1031,7 +1075,8 @@ self.new_ops(temp2) # Explicit context as first argument. self.new_ops(temp1) - self._endCallFunc(temp_method) + self._doCallFunc(temp_method) + self._endCallFunc(temp_method, keep_frame=1) # Test for NotImplemented. # Don't actually raise an exception. @@ -1045,7 +1090,7 @@ # End right method attempt. self.set_label(end_right_label) - self.new_op(DropFrame()) # From the right method call. + self._endCallFunc() # From the right method call. # Raise a TypeError. @@ -1111,6 +1156,7 @@ self._startCallFunc() self.dispatch(node.node) temp = self._generateCallFunc(node.args, node) + self._doCallFunc(temp) self._endCallFunc(temp) def visitClass(self, node): @@ -1179,6 +1225,7 @@ self.dispatch(node.list) self._generateAttr(node, "__iter__", (LoadAddress, LoadAttr, LoadAttrIndex)) temp = self._generateCallFunc([], node) + self._doCallFunc(temp) self._endCallFunc(temp) # Iterator on stack. @@ -1193,6 +1240,7 @@ self.new_op(Duplicate()) self._generateAttr(node, "next", (LoadAddress, LoadAttr, LoadAttrIndex)) temp = self._generateCallFunc([], node) + self._doCallFunc(temp) self._endCallFunc(temp) # Test for StopIteration. @@ -1333,7 +1381,15 @@ def visitPrintnl(self, node): pass - def visitRaise(self, node): pass + def visitRaise(self, node): + # NOTE: expr1 only => instance provided + self.dispatch(node.expr1) + + if node.expr2 is not None: + self.dispatch(node.expr2) + self._doCallFunc() + + self.new_op(RaiseException()) def visitReturn(self, node): if node.value is not None: @@ -1396,6 +1452,7 @@ # Unhandled exceptions. + self.new_op(LoadException()) self.new_op(RaiseException()) # After exception diff -r 2300627a338d -r 78240b642d1f micropython/inspect.py --- a/micropython/inspect.py Sun Jun 22 02:21:39 2008 +0200 +++ b/micropython/inspect.py Sat Jun 28 20:46:45 2008 +0200 @@ -591,6 +591,10 @@ def visitTryExcept(self, node): self.dispatch(node.body) for name, var, n in node.handlers: + + # Establish the local for the handler. + + self.dispatch(var) self.dispatch(n) if node.else_ is not None: self.dispatch(node.else_) diff -r 2300627a338d -r 78240b642d1f micropython/rsvp.py --- a/micropython/rsvp.py Sun Jun 22 02:21:39 2008 +0200 +++ b/micropython/rsvp.py Sat Jun 28 20:46:45 2008 +0200 @@ -236,24 +236,32 @@ class MakeFrame(Instruction): "Make a new invocation frame." class ReserveFrame(Immediate): "Reserve the given number of entries for the invocation frame." -class DropFrame(Instruction): "Drop an invocation frame." class StoreFrame(StackRemove, Immediate): "Store an argument at the given frame location." class StoreFrameIndex(StackRemove, Immediate): "Store an argument for the parameter with the given index." +class LoadCallable(Instruction): "Load the target of an invocation." +class LoadContext(StackReplace, Instruction): "Load the context of an invocation." class CheckFrame(Instruction): "Check the invocation frame for the target." +class CheckSelf(Instruction): "Check the first argument of an invocation against the target." +class CheckContext(Instruction): """Check the context of an invocation against the target, + potentially discarding the context.""" + +# Invocation-related instructions, using a special result "register". + class JumpWithFrame(Instruction): "Jump, adopting the invocation frame, to the callable found on the stack." +class DropFrame(Instruction): "Drop an invocation frame." +class Return(StackRemove, Instruction): "Return a value from a subprogram." +class LoadResult(StackAdd, Instruction): "Load a returned value." -# Invocation-related instructions. +# Branch-related instructions. class Jump(Address): "Jump unconditionally." class JumpIfFalse(Address): "Jump if the last evaluation gave a false result." class JumpIfTrue(Address): "Jump if the last evaluation gave a true result." -class LoadCallable(Instruction): "Load the target of an invocation." -class LoadContext(StackReplace, Instruction): "Load the context of an invocation." -class CheckContext(Instruction): """Check the context of an invocation against the target, - potentially discarding the context.""" -class CheckSelf(Instruction): "Check the first argument of an invocation against the target." + +# Exception-related instructions, using a special exception "register". + +class LoadException(StackAdd, Instruction): "Load the raised exception." class RaiseException(StackRemove, Instruction): "Raise an exception." -class Return(StackRemove, Instruction): "Return a value from a subprogram." class CheckException(Instruction): "Check the raised exception against another." # General instructions. diff -r 2300627a338d -r 78240b642d1f tests/exception.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/exception.py Sat Jun 28 20:46:45 2008 +0200 @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +class E: + pass + +def f(x, y): + try: + g(x) + except E, exc: + return y + return x + +def g(x): + raise E, x + +# vim: tabstop=4 expandtab shiftwidth=4