Properly run __exit__, finally on break or continue

This commit is contained in:
K. Lange 2022-07-29 19:13:33 +09:00
parent d89b023752
commit d6c9602abd
8 changed files with 107 additions and 4 deletions

View File

@ -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};
}

View File

@ -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)

View File

@ -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: {

View File

@ -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: {

View File

@ -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()

View File

@ -0,0 +1,5 @@
in try
finally
another finally
out of loop
in other finally

25
test/testBreakWiths.krk Normal file
View File

@ -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")

View File

@ -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