diff --git a/asteroid/state.py b/asteroid/state.py index 605cfeac..74aac668 100644 --- a/asteroid/state.py +++ b/asteroid/state.py @@ -23,6 +23,10 @@ def initialize(self,module=""): # stack of 3-tuples for stack trace of function # calls: (module,lineno,function name) self.trace_stack = [(module,1,"")] + # if an exception occurs then error_trace will point to + # it. an exception handler is responsible for clearing + # this. + self.error_trace = None state = State() @@ -31,10 +35,15 @@ def warning(str): print("Warning: {}: {}: {}".format(module, lineno, str)) def dump_trace(): - if len(state.trace_stack) == 1: - return + if state.error_trace: + _dump_trace(state.error_trace) + return else: - print("traceback (most recent call last):") - for i in range(0,len(state.trace_stack)): - (module,lineno,fname) = state.trace_stack[i] - print("{}: {}: calling {}".format(module,lineno,fname)) + _dump_trace(state.trace_stack) + return + +def _dump_trace(trace): + print("traceback (most recent call last):") + for i in range(0,len(trace)): + (module,lineno,fname) = trace[i] + print("{}: {}: calling {}".format(module,lineno,fname)) diff --git a/asteroid/test-suites/regression-tests/test138.ast b/asteroid/test-suites/regression-tests/test138.ast new file mode 100644 index 00000000..babbed0f --- /dev/null +++ b/asteroid/test-suites/regression-tests/test138.ast @@ -0,0 +1,18 @@ +-- test case for issue #209 +load system io. + +function goo with none do + throw Error("exception"). +end + +function foo with none do + let a = 1. + try + goo(). + catch _ do + io @println "exception caught". + end + assert(a==1). -- make that a is still in scope +end + +foo (). diff --git a/asteroid/version.py b/asteroid/version.py index 6626f8c7..9e7dbc25 100644 --- a/asteroid/version.py +++ b/asteroid/version.py @@ -5,4 +5,4 @@ # (c) University of Rhode Island ########################################################################################## -VERSION = "1.1.3" +VERSION = "1.1.4" diff --git a/asteroid/walk.py b/asteroid/walk.py index d8995ab5..d414dbda 100644 --- a/asteroid/walk.py +++ b/asteroid/walk.py @@ -4,7 +4,7 @@ # (c) University of Rhode Island ######################################################################### -from copy import deepcopy +from copy import deepcopy,copy from re import match as re_match from asteroid.globals import * @@ -941,6 +941,15 @@ def handle_builtins(node): else: raise ValueError("unknown builtin unary operation '{}'".format(opname)) +######################################################################### +def pop_stackframe(error_trace=False): + # pop frame off the stack + state.symbol_table.pop_scope() + state.symbol_table.set_config(state.symbol_table.saved_configs.pop()) + if error_trace: + state.error_trace = copy(state.trace_stack) + state.trace_stack.pop() + ######################################################################### def handle_call(obj_ref, fval, actual_val_args, fname): # Needed for later @@ -1050,19 +1059,11 @@ def handle_call(obj_ref, fval, actual_val_args, fname): # Reset settings except RedundantPatternFound as r: debugging = old_debugging - - # coming back from a function call - restore caller's env + # restore caller's env state.lineinfo = old_lineinfo - # Keep debugger up to date - if debugging: - debugger.set_lineinfo(state.lineinfo) - - state.symbol_table.pop_scope() - state.symbol_table.set_config(state.symbol_table.saved_configs.pop()) - - state.trace_stack.pop() - + if debugging: debugger.set_lineinfo(state.lineinfo) + pop_stackframe() raise r # execute the function @@ -1089,17 +1090,20 @@ def handle_call(obj_ref, fval, actual_val_args, fname): function_return_value.pop() return_value = val.value - # coming back from a function call - restore caller's env + except Exception as e: + # we got some other kind of exception within the function call + # clean up our runtime stack and rethrow + # Note: do not reset lineinfo, this way the state points at the source + # of the exception + pop_stackframe(error_trace=True) + raise e + + # all done with function call -- clean up and exit + # restore caller's env state.lineinfo = old_lineinfo - # Keep debugger up to date if debugging: debugger.set_lineinfo(state.lineinfo) - - state.symbol_table.pop_scope() - state.symbol_table.set_config(state.symbol_table.saved_configs.pop()) - - state.trace_stack.pop() - + pop_stackframe() message_explicit("Return: {} from {}", [("None" if (not return_value[1]) else gen_t2s(return_value)), fname], @@ -1331,6 +1335,8 @@ def try_stmt(node): except PatternMatchFailed: pass else: + # handler found - null out error_trace + state.error_trace = None declare_unifiers(unifiers) walk_stmt_list(catch_stmts, step_state=stepping) return