diff --git a/src/compiler.c b/src/compiler.c index 90c67c4..4605435 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -1986,9 +1986,10 @@ static void breakStatement(struct GlobalState * state) { state->current->breaks = GROW_ARRAY(struct LoopExit,state->current->breaks,old,state->current->breakSpace); } - for (size_t i = state->current->loopLocalCount; i < state->current->localCount; ++i) { - emitByte(OP_POP); + if (state->current->loopLocalCount != state->current->localCount) { + EMIT_OPERAND_OP(OP_EXIT_LOOP, state->current->loopLocalCount); } + state->current->breaks[state->current->breakCount++] = (struct LoopExit){emitJump(OP_JUMP),state->parser.previous}; } @@ -1999,9 +2000,10 @@ static void continueStatement(struct GlobalState * state) { state->current->continues = GROW_ARRAY(struct LoopExit,state->current->continues,old,state->current->continueSpace); } - for (size_t i = state->current->loopLocalCount; i < state->current->localCount; ++i) { - emitByte(OP_POP); + if (state->current->loopLocalCount != state->current->localCount) { + EMIT_OPERAND_OP(OP_EXIT_LOOP, state->current->loopLocalCount); } + state->current->continues[state->current->continueCount++] = (struct LoopExit){emitJump(OP_JUMP),state->parser.previous}; } diff --git a/src/opcodes.h b/src/opcodes.h index f80e4da..9fae36d 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -110,3 +110,4 @@ OPERAND(OP_EXPAND_ARGS,EXPAND_ARGS_MORE) SIMPLE(OP_INHERIT) JUMP(OP_CALL_ITER,+) JUMP(OP_JUMP_IF_TRUE_OR_POP,+) +OPERAND(OP_EXIT_LOOP, (void)0) diff --git a/src/value.c b/src/value.c index 38368ff..35a2924 100644 --- a/src/value.c +++ b/src/value.c @@ -64,6 +64,7 @@ void krk_printValueSafe(FILE * f, KrkValue printable) { case OP_BEGIN_FINALLY: fprintf(f, "{finally<-%d}", (int)AS_HANDLER_TARGET(printable)); break; 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; } break; case KRK_VAL_KWARGS: { diff --git a/src/vm.c b/src/vm.c index dd7c90c..45137c4 100644 --- a/src/vm.c +++ b/src/vm.c @@ -2222,6 +2222,11 @@ _resumeHook: (void)0; krk_callDirect(type->_exit, 4); if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) goto _finishException; } + if (AS_HANDLER_TYPE(handler) == OP_EXIT_LOOP) { + frame->ip = frame->closure->function->chunk.code + AS_HANDLER_TARGET(handler); + OPERAND = AS_INTEGER(krk_peek(1)); + goto _finishPopBlock; + } if (AS_HANDLER_TYPE(handler) != OP_RETURN) break; krk_pop(); /* handler */ } /* fallthrough */ @@ -2388,6 +2393,8 @@ _finishReturn: (void)0; isMatch = 0; } else if (AS_HANDLER_TYPE(krk_peek(1)) == OP_END_FINALLY) { isMatch = 0; + } else if (AS_HANDLER_TYPE(krk_peek(1)) == OP_EXIT_LOOP) { + isMatch = 0; } else if (IS_CLASS(krk_peek(0)) && krk_isInstanceOf(krk_peek(2), AS_CLASS(krk_peek(0)))) { isMatch = 1; } else if (IS_TUPLE(krk_peek(0))) { @@ -2425,6 +2432,10 @@ _finishReturn: (void)0; krk_currentThread.currentException = krk_pop(); krk_currentThread.flags |= KRK_THREAD_HAS_EXCEPTION; goto _finishException; + } else if (AS_HANDLER_TYPE(handler) == OP_EXIT_LOOP) { + frame->ip = frame->closure->function->chunk.code + AS_HANDLER_TARGET(handler); + OPERAND = AS_INTEGER(krk_peek(1)); + goto _finishPopBlock; } else if (AS_HANDLER_TYPE(handler) == OP_RETURN) { krk_push(krk_peek(1)); goto _finishReturn; @@ -2887,6 +2898,34 @@ _finishReturn: (void)0; } break; } + + case OP_EXIT_LOOP_LONG: + THREE_BYTE_OPERAND; + case OP_EXIT_LOOP: { + ONE_BYTE_OPERAND; +_finishPopBlock: + closeUpvalues(frame->slots + OPERAND); + int stackOffset; + for (stackOffset = (int)(krk_currentThread.stackTop - krk_currentThread.stack - 1); + stackOffset >= (int)(frame->slots + OPERAND) && + !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) + ; stackOffset--) krk_pop(); + + /* Do the handler. */ + if (stackOffset >= (int)(frame->slots + OPERAND)) { + uint16_t popTarget = (frame->ip - frame->closure->function->chunk.code); + krk_currentThread.stackTop = &krk_currentThread.stack[stackOffset + 1]; + frame->ip = frame->closure->function->chunk.code + AS_HANDLER_TARGET(krk_peek(0)); + krk_currentThread.stackTop[-1] = HANDLER_VAL(OP_EXIT_LOOP, popTarget); + krk_currentThread.stackTop[-2] = INTEGER_VAL(OPERAND); + } + + /* Continue normally */ + break; + } + case OP_POP_MANY_LONG: THREE_BYTE_OPERAND; case OP_POP_MANY: { diff --git a/test/testBreakFinallys.krk b/test/testBreakFinallys.krk new file mode 100644 index 0000000..da808ce --- /dev/null +++ b/test/testBreakFinallys.krk @@ -0,0 +1,17 @@ +def foo(): + try: + for i in range(5): + try: + try: + print('in try') + break + finally: + print('finally') + finally: + print('another finally') + print('out of try/finally') + print('out of loop') + finally: + print('in other finally') + +foo() diff --git a/test/testBreakFinallys.krk.expect b/test/testBreakFinallys.krk.expect new file mode 100644 index 0000000..b9616fb --- /dev/null +++ b/test/testBreakFinallys.krk.expect @@ -0,0 +1,5 @@ +in try +finally +another finally +out of loop +in other finally diff --git a/test/testBreakWiths.krk b/test/testBreakWiths.krk new file mode 100644 index 0000000..73e72e8 --- /dev/null +++ b/test/testBreakWiths.krk @@ -0,0 +1,25 @@ +class withable(): + def __init__(self, name): + self.name = name + def __enter__(self): + print("enter", self.name) + def __exit__(self,*args): + print("exit", self.name, args) + +while True: + with withable('outer'): + while True: + with withable('a'): + print("into a") + with withable('b'): + print("into b") + with withable('c'): + print("into c") + break + print("nope b") + print("nope a") + print("nope loop") + print("exit from loop") + break + print("nope outer") +print("done") diff --git a/test/testBreakWiths.krk.expect b/test/testBreakWiths.krk.expect new file mode 100644 index 0000000..5551f30 --- /dev/null +++ b/test/testBreakWiths.krk.expect @@ -0,0 +1,13 @@ +enter outer +enter a +into a +enter b +into b +enter c +into c +exit c [None, None, None] +exit b [None, None, None] +exit a [None, None, None] +exit from loop +exit outer [None, None, None] +done