From d575bf76075e499e13615a5c587ba239e203b47a Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Sat, 25 Nov 2023 12:14:05 +0900 Subject: [PATCH] Handle case of a nested exception in a 'final' --- src/compiler.c | 10 +++---- src/value.c | 1 + src/vm.c | 30 ++++++++++++++++---- test/testNestedExceptionInFinally.krk | 13 +++++++++ test/testNestedExceptionInFinally.krk.expect | 4 +++ 5 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 test/testNestedExceptionInFinally.krk create mode 100644 test/testNestedExceptionInFinally.krk.expect diff --git a/src/compiler.c b/src/compiler.c index 3598b99..3370dd4 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -2320,6 +2320,7 @@ _anotherExcept: patchJump(exitJumpOffsets[0]); firstJump = 1; emitByte(OP_TRY_ELSE); + state->current->locals[exceptionObject].name = syntheticToken(""); beginScope(state); block(state, blockWidth, "else"); endScope(state); @@ -2335,17 +2336,16 @@ _anotherExcept: for (int i = firstJump; i < exitJumps; ++i) { patchJump(exitJumpOffsets[i]); } - size_t nameInd = renameLocal(state, exceptionObject, syntheticToken("__tmp")); - emitByte(OP_BEGIN_FINALLY); - exitJumps = 0; if (nextJump != -1) { patchJump(nextJump); } + emitByte(OP_BEGIN_FINALLY); + exitJumps = 0; + state->current->locals[exceptionObject].name = syntheticToken(""); beginScope(state); block(state,blockWidth,"finally"); endScope(state); nextJump = -2; - state->current->codeobject->localNames[nameInd].deathday = (size_t)currentChunk()->count; emitByte(OP_END_FINALLY); } else if (!check(TOKEN_EOL) && !check(TOKEN_EOF)) { krk_ungetToken(&state->scanner, state->parser.current); @@ -2363,8 +2363,8 @@ _anotherExcept: } if (nextJump >= 0) { - emitByte(OP_BEGIN_FINALLY); patchJump(nextJump); + emitByte(OP_BEGIN_FINALLY); emitByte(OP_END_FINALLY); } diff --git a/src/value.c b/src/value.c index d56bcdf..17f7617 100644 --- a/src/value.c +++ b/src/value.c @@ -65,6 +65,7 @@ void krk_printValueSafe(FILE * f, KrkValue printable) { case OP_RETURN: fprintf(f, "{return<-%d}", (int)AS_HANDLER_TARGET(printable)); break; case OP_END_FINALLY: fprintf(f, "{end<-%d}", (int)AS_HANDLER_TARGET(printable)); break; case OP_EXIT_LOOP: fprintf(f, "{exit<-%d}", (int)AS_HANDLER_TARGET(printable)); break; + case OP_RAISE_FROM: fprintf(f, "{reraise<-%d}", (int)AS_HANDLER_TARGET(printable)); break; } break; case KRK_VAL_KWARGS: { diff --git a/src/vm.c b/src/vm.c index 0b9e580..d2e7610 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1132,7 +1132,8 @@ static int handleException(void) { !IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_PUSH_TRY) && !IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_PUSH_WITH) && !IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_FILTER_EXCEPT) && - !IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_RAISE) + !IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_RAISE) && + !IS_HANDLER_TYPE(krk_currentThread.stack[stackOffset], OP_END_FINALLY) ; stackOffset--); if (stackOffset < exitSlot) { if (exitSlot == 0) { @@ -2322,10 +2323,16 @@ _finishReturn: (void)0; } case OP_BEGIN_FINALLY: { if (IS_HANDLER(krk_peek(0))) { - if (AS_HANDLER_TYPE(krk_peek(0)) == OP_PUSH_TRY) { - krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_BEGIN_FINALLY,AS_HANDLER_TARGET(krk_peek(0))); - } else if (AS_HANDLER_TYPE(krk_peek(0)) == OP_FILTER_EXCEPT) { - krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_BEGIN_FINALLY,AS_HANDLER_TARGET(krk_peek(0))); + switch (AS_HANDLER_TYPE(krk_peek(0))) { + /* We either entered the @c finally without an exception, or the exception was handled by an @c except */ + case OP_PUSH_TRY: + case OP_FILTER_EXCEPT: + krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_BEGIN_FINALLY,AS_HANDLER_TARGET(krk_peek(0))); + break; + /* We entered the @c finally without handling an exception. */ + case OP_RAISE: + krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_END_FINALLY,AS_HANDLER_TARGET(krk_peek(0))); + break; } } break; @@ -2583,6 +2590,12 @@ _finishReturn: (void)0; case OP_EXIT_LOOP: frame->ip += OPERAND; break; + case OP_RAISE_FROM: + /* Exception happened while in @c finally */ + krk_pop(); /* handler */ + krk_currentThread.currentException = krk_pop(); + krk_currentThread.flags |= KRK_THREAD_HAS_EXCEPTION; + goto _finishException; } break; } @@ -3161,10 +3174,17 @@ _finishException: frame->ip = frame->closure->function->chunk.code + AS_HANDLER_TARGET(krk_peek(0)); /* Stick the exception into the exception slot */ switch (AS_HANDLER_TYPE(krk_currentThread.stackTop[-1])) { + /* An exception happened while handling an exception */ case OP_RAISE: case OP_FILTER_EXCEPT: krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_END_FINALLY,AS_HANDLER_TARGET(krk_peek(0))); break; + /* An exception happened while already in the @c finally block from handling + * another exception. Bail. */ + case OP_END_FINALLY: + krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_RAISE_FROM,AS_HANDLER_TARGET(krk_peek(0))); + break; + /* First exception in this chain. */ default: krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_RAISE,AS_HANDLER_TARGET(krk_peek(0))); break; diff --git a/test/testNestedExceptionInFinally.krk b/test/testNestedExceptionInFinally.krk new file mode 100644 index 0000000..3f5bdcf --- /dev/null +++ b/test/testNestedExceptionInFinally.krk @@ -0,0 +1,13 @@ +try: + try: + print('try') + raise ValueError() + except 1/0: + print('bad: except body') + finally: + print(inner_error) + print('bad: after inner error') +except BaseException as e: + while e: + print(repr(e)) + e = e.__context__ diff --git a/test/testNestedExceptionInFinally.krk.expect b/test/testNestedExceptionInFinally.krk.expect new file mode 100644 index 0000000..6b568e3 --- /dev/null +++ b/test/testNestedExceptionInFinally.krk.expect @@ -0,0 +1,4 @@ +try +NameError("Undefined variable 'inner_error'.") +ZeroDivisionError('integer division by zero') +ValueError()