Index: src/exceptions.c =================================================================== --- src/exceptions.c (revision 10896) +++ src/exceptions.c (working copy) @@ -220,22 +220,28 @@ */ message = VTABLE_get_string_keyed_int(interpreter, exception, 0); do { + PMC *cleanup_sub = NULL; Stack_Entry_t *e = stack_entry(interpreter, CONTEXT(interpreter->ctx)->control_stack, 0); + if (!e) break; if (e->entry_type == STACK_ENTRY_ACTION) { /* - * Clear automatic cleanup routine run in stack_pop - * and run the action subroutine with an INTVAL argument. - * of 1 + * Disable automatic cleanup routine execution in stack_pop so that + * we can run the action subroutine manually with an INTVAL argument + * of 1. Note that we have to run the sub AFTER it has been popped, + * lest a new error in the sub cause an infinite loop. */ - PMC *sub = UVal_pmc(e->entry); + cleanup_sub = UVal_pmc(e->entry); e->cleanup = STACK_CLEANUP_NULL; - Parrot_runops_fromc_args(interpreter, sub, "vI", 1); } (void)stack_pop(interpreter, &CONTEXT(interpreter->ctx)->control_stack, NULL, e->entry_type); + if (cleanup_sub) { + /* Now it's safe to run. */ + Parrot_runops_fromc_args(interpreter, cleanup_sub, "vI", 1); + } if (e->entry_type == STACK_ENTRY_PMC) { /* * During interpreter creation there is an initial context Index: src/stacks.c =================================================================== --- src/stacks.c (revision 10896) +++ src/stacks.c (working copy) @@ -308,7 +308,7 @@ } /* Cleanup routine? */ - if (entry->cleanup) { + if (entry->cleanup != STACK_CLEANUP_NULL) { (*entry->cleanup) (interpreter, entry); } Index: t/pmc/exception.t =================================================================== --- t/pmc/exception.t (revision 10896) +++ t/pmc/exception.t (working copy) @@ -6,7 +6,7 @@ use warnings; use lib qw( . lib ../lib ../../lib ); use Test::More; -use Parrot::Test tests => 24; +use Parrot::Test tests => 26; =head1 NAME @@ -464,3 +464,130 @@ CODE 1 2 OUTPUT + +{ +local $TODO = 'bug'; + +## this is broken; continuation calling does not execute actions when unwinding. +pir_output_is(<<'CODE', <<'OUTPUT', 'cleanup global: continuation'); +.sub main :main + .local pmc outer, cont + outer = new String + outer = "Outer value\n" + store_global "Foo::Bar", "test", outer + new cont, .Continuation + set_addr cont, endcont + store_global "Foo::Bar", "exit", cont + show_value() + test1() + print "skipped.\n" +endcont: + show_value() +.end +.sub test1 + .local pmc test1_binding, old_value, cleanup + .lex "old_value", old_value + test1_binding = new String + test1_binding = "Inner value\n" + old_value = find_global "Foo::Bar", "test" + .const .Sub test1_cleanup_sub = "test1_cleanup" + cleanup = newclosure test1_cleanup_sub + pushaction cleanup + store_global "Foo::Bar", "test", test1_binding + show_value() + test2() + show_value() +.end +.sub test1_cleanup :outer(test1) + .local pmc old_value + print "[in test1_cleanup]\n" + find_lex old_value, "old_value" + store_global "Foo::Bar", "test", old_value +.end +.sub test2 + .local pmc test2_binding, exit + test2_binding = new String + test2_binding = "Innerer value\n" + store_global "Foo::Bar", "test", test2_binding + show_value() + exit = find_global "Foo::Bar", "exit" + exit() +.end +.sub show_value + .local pmc value + value = find_global "Foo::Bar", "test" + print value +.end +CODE +Outer value +Inner value +Innerer value +[in test1_cleanup] +Outer value +OUTPUT + +$TODO++; # suppress warning. +} + +pir_output_is(<<'CODE', <<'OUTPUT', 'cleanup global: throw'); +.sub main :main + .local pmc outer + outer = new String + outer = "Outer value\n" + store_global "Foo::Bar", "test", outer + push_eh eh + show_value() + test1() + print "skipped.\n" +eh: + .local pmc exception + .get_results (exception) + print "Error: " + print exception + print "\n" +last: + show_value() +.end +.sub test1 + .local pmc test1_binding, old_value, cleanup + .lex "old_value", old_value + test1_binding = new String + test1_binding = "Inner value\n" + old_value = find_global "Foo::Bar", "test" + .const .Sub test1_cleanup_sub = "test1_cleanup" + cleanup = newclosure test1_cleanup_sub + pushaction cleanup + store_global "Foo::Bar", "test", test1_binding + show_value() + test2() + show_value() +.end +.sub test1_cleanup :outer(test1) + .local pmc old_value + print "[in test1_cleanup]\n" + find_lex old_value, "old_value" + store_global "Foo::Bar", "test", old_value +.end +.sub test2 + .local pmc test2_binding, exit + test2_binding = new String + test2_binding = "Innerer value\n" + store_global "Foo::Bar", "test", test2_binding + show_value() + exit = new Exception + exit["_message"] = "something happened" + throw exit +.end +.sub show_value + .local pmc value + value = find_global "Foo::Bar", "test" + print value +.end +CODE +Outer value +Inner value +Innerer value +[in test1_cleanup] +Error: something happened +Outer value +OUTPUT