Support calling exit handlers for with: statements on exception
This commit is contained in:
parent
8aed1368ea
commit
ee731257dd
@ -1455,6 +1455,10 @@ static void withStatement() {
|
|||||||
|
|
||||||
consume(TOKEN_COLON, "Expected ':' after with statement");
|
consume(TOKEN_COLON, "Expected ':' after with statement");
|
||||||
|
|
||||||
|
/* Storage for return / exception */
|
||||||
|
addLocal(syntheticToken(""));
|
||||||
|
|
||||||
|
/* Handler object */
|
||||||
addLocal(syntheticToken(""));
|
addLocal(syntheticToken(""));
|
||||||
int withJump = emitJump(OP_PUSH_WITH);
|
int withJump = emitJump(OP_PUSH_WITH);
|
||||||
markInitialized();
|
markInitialized();
|
||||||
|
@ -258,7 +258,7 @@ KRK_METHOD(File,__init__,{
|
|||||||
|
|
||||||
KRK_METHOD(File,__enter__,{})
|
KRK_METHOD(File,__enter__,{})
|
||||||
KRK_METHOD(File,__exit__,{
|
KRK_METHOD(File,__exit__,{
|
||||||
return FUNC_NAME(File,close)(argc,argv,0);
|
return FUNC_NAME(File,close)(1,argv,0);
|
||||||
})
|
})
|
||||||
|
|
||||||
static void makeFileInstance(KrkInstance * module, const char name[], FILE * file) {
|
static void makeFileInstance(KrkInstance * module, const char name[], FILE * file) {
|
||||||
@ -487,7 +487,7 @@ KRK_METHOD(Directory,__repr__,{
|
|||||||
|
|
||||||
KRK_METHOD(Directory,__enter__,{})
|
KRK_METHOD(Directory,__enter__,{})
|
||||||
KRK_METHOD(Directory,__exit__,{
|
KRK_METHOD(Directory,__exit__,{
|
||||||
return FUNC_NAME(Directory,close)(argc,argv,0);
|
return FUNC_NAME(Directory,close)(1,argv,0);
|
||||||
})
|
})
|
||||||
|
|
||||||
_noexport
|
_noexport
|
||||||
|
@ -190,7 +190,6 @@ KRK_METHOD(Lock,__enter__,{
|
|||||||
})
|
})
|
||||||
|
|
||||||
KRK_METHOD(Lock,__exit__,{
|
KRK_METHOD(Lock,__exit__,{
|
||||||
METHOD_TAKES_NONE();
|
|
||||||
pthread_mutex_unlock(&self->mutex);
|
pthread_mutex_unlock(&self->mutex);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
36
src/vm.c
36
src/vm.c
@ -1434,6 +1434,7 @@ static int handleException() {
|
|||||||
for (stackOffset = (int)(krk_currentThread.stackTop - krk_currentThread.stack - 1);
|
for (stackOffset = (int)(krk_currentThread.stackTop - krk_currentThread.stack - 1);
|
||||||
stackOffset >= exitSlot &&
|
stackOffset >= exitSlot &&
|
||||||
!IS_TRY_HANDLER(krk_currentThread.stack[stackOffset]) &&
|
!IS_TRY_HANDLER(krk_currentThread.stack[stackOffset]) &&
|
||||||
|
!IS_WITH_HANDLER(krk_currentThread.stack[stackOffset]) &&
|
||||||
!IS_EXCEPT_HANDLER(krk_currentThread.stack[stackOffset])
|
!IS_EXCEPT_HANDLER(krk_currentThread.stack[stackOffset])
|
||||||
; stackOffset--);
|
; stackOffset--);
|
||||||
if (stackOffset < exitSlot) {
|
if (stackOffset < exitSlot) {
|
||||||
@ -1979,14 +1980,33 @@ _resumeHook: (void)0;
|
|||||||
case OP_CLEANUP_WITH: {
|
case OP_CLEANUP_WITH: {
|
||||||
/* Top of stack is a HANDLER that should have had something loaded into it if it was still valid */
|
/* Top of stack is a HANDLER that should have had something loaded into it if it was still valid */
|
||||||
KrkValue handler = krk_peek(0);
|
KrkValue handler = krk_peek(0);
|
||||||
KrkValue contextManager = krk_peek(1);
|
KrkValue exceptionObject = krk_peek(1);
|
||||||
|
KrkValue contextManager = krk_peek(2);
|
||||||
KrkClass * type = krk_getType(contextManager);
|
KrkClass * type = krk_getType(contextManager);
|
||||||
krk_push(contextManager);
|
krk_push(contextManager);
|
||||||
krk_callSimple(OBJECT_VAL(type->_exit), 1, 0);
|
if (AS_HANDLER(handler).type == OP_RAISE) {
|
||||||
|
krk_push(OBJECT_VAL(krk_getType(exceptionObject)));
|
||||||
|
krk_push(exceptionObject);
|
||||||
|
KrkValue tracebackEntries = NONE_VAL();
|
||||||
|
if (IS_INSTANCE(exceptionObject))
|
||||||
|
krk_tableGet(&AS_INSTANCE(exceptionObject)->fields, OBJECT_VAL(S("traceback")), &tracebackEntries);
|
||||||
|
krk_push(tracebackEntries);
|
||||||
|
krk_callSimple(OBJECT_VAL(type->_exit), 4, 0);
|
||||||
/* Top of stack is now either someone else's problem or a return value */
|
/* Top of stack is now either someone else's problem or a return value */
|
||||||
|
if (!(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) {
|
||||||
|
krk_pop(); /* Handler object */
|
||||||
|
krk_currentThread.currentException = krk_pop(); /* Original exception */
|
||||||
|
krk_currentThread.flags |= KRK_THREAD_HAS_EXCEPTION;
|
||||||
|
}
|
||||||
|
goto _finishException;
|
||||||
|
} else {
|
||||||
|
krk_push(NONE_VAL());
|
||||||
|
krk_push(NONE_VAL());
|
||||||
|
krk_push(NONE_VAL());
|
||||||
|
krk_callSimple(OBJECT_VAL(type->_exit), 4, 0);
|
||||||
|
}
|
||||||
if (AS_HANDLER(handler).type != OP_RETURN) break;
|
if (AS_HANDLER(handler).type != OP_RETURN) break;
|
||||||
krk_pop(); /* handler */
|
krk_pop(); /* handler */
|
||||||
krk_pop(); /* contextManager */
|
|
||||||
} /* fallthrough */
|
} /* fallthrough */
|
||||||
case OP_RETURN: {
|
case OP_RETURN: {
|
||||||
_finishReturn: (void)0;
|
_finishReturn: (void)0;
|
||||||
@ -2003,15 +2023,8 @@ _finishReturn: (void)0;
|
|||||||
if (stackOffset >= (int)frame->slots) {
|
if (stackOffset >= (int)frame->slots) {
|
||||||
krk_currentThread.stackTop = &krk_currentThread.stack[stackOffset + 1];
|
krk_currentThread.stackTop = &krk_currentThread.stack[stackOffset + 1];
|
||||||
frame->ip = frame->closure->function->chunk.code + AS_HANDLER(krk_peek(0)).target;
|
frame->ip = frame->closure->function->chunk.code + AS_HANDLER(krk_peek(0)).target;
|
||||||
int wasWith = (IS_WITH_HANDLER(krk_peek(0)));
|
|
||||||
AS_HANDLER(krk_currentThread.stackTop[-1]).type = OP_RETURN;
|
AS_HANDLER(krk_currentThread.stackTop[-1]).type = OP_RETURN;
|
||||||
krk_push(result);
|
krk_currentThread.stackTop[-2] = result;
|
||||||
krk_swap(2);
|
|
||||||
if (wasWith) {
|
|
||||||
krk_swap(1);
|
|
||||||
} else {
|
|
||||||
krk_pop();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
krk_currentThread.frameCount--;
|
krk_currentThread.frameCount--;
|
||||||
@ -2317,6 +2330,7 @@ _finishReturn: (void)0;
|
|||||||
krk_push(contextManager);
|
krk_push(contextManager);
|
||||||
krk_callSimple(OBJECT_VAL(type->_enter), 1, 0);
|
krk_callSimple(OBJECT_VAL(type->_enter), 1, 0);
|
||||||
/* Ignore result; don't need to pop */
|
/* Ignore result; don't need to pop */
|
||||||
|
krk_push(NONE_VAL());
|
||||||
KrkValue handler = HANDLER_VAL(OP_PUSH_WITH, cleanupTarget);
|
KrkValue handler = HANDLER_VAL(OP_PUSH_WITH, cleanupTarget);
|
||||||
krk_push(handler);
|
krk_push(handler);
|
||||||
break;
|
break;
|
||||||
|
@ -5,7 +5,7 @@ class ContextManager:
|
|||||||
self.title = title
|
self.title = title
|
||||||
def __enter__():
|
def __enter__():
|
||||||
print("Enter context manager", self.title)
|
print("Enter context manager", self.title)
|
||||||
def __exit__():
|
def __exit__(type,value,traceback):
|
||||||
print("Exit context manager", self.title)
|
print("Exit context manager", self.title)
|
||||||
|
|
||||||
|
|
||||||
|
36
test/testContextManagerException.krk
Normal file
36
test/testContextManagerException.krk
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
class Context:
|
||||||
|
def __enter__(self):
|
||||||
|
print("Entering")
|
||||||
|
def __exit__(self, *args):
|
||||||
|
print("Exiting with",[type(x) for x in args])
|
||||||
|
|
||||||
|
def simple():
|
||||||
|
print("Before")
|
||||||
|
with Context() as c:
|
||||||
|
print("In context")
|
||||||
|
print("After")
|
||||||
|
|
||||||
|
simple()
|
||||||
|
|
||||||
|
def withReturn():
|
||||||
|
print("Before")
|
||||||
|
with Context() as c:
|
||||||
|
print("in context")
|
||||||
|
return 42
|
||||||
|
print("after return")
|
||||||
|
print("After")
|
||||||
|
|
||||||
|
print(withReturn())
|
||||||
|
|
||||||
|
def withException():
|
||||||
|
print("Before")
|
||||||
|
with Context() as c:
|
||||||
|
print("Raising")
|
||||||
|
raise ValueError()
|
||||||
|
print("Don't print me")
|
||||||
|
print("After")
|
||||||
|
|
||||||
|
try:
|
||||||
|
withException()
|
||||||
|
except Exception as e:
|
||||||
|
print(repr(e))
|
15
test/testContextManagerException.krk.expect
Normal file
15
test/testContextManagerException.krk.expect
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
Before
|
||||||
|
Entering
|
||||||
|
In context
|
||||||
|
Exiting with [<class 'NoneType'>, <class 'NoneType'>, <class 'NoneType'>]
|
||||||
|
After
|
||||||
|
Before
|
||||||
|
Entering
|
||||||
|
in context
|
||||||
|
Exiting with [<class 'NoneType'>, <class 'NoneType'>, <class 'NoneType'>]
|
||||||
|
42
|
||||||
|
Before
|
||||||
|
Entering
|
||||||
|
Raising
|
||||||
|
Exiting with [<class 'type'>, <class 'ValueError'>, <class 'list'>]
|
||||||
|
ValueError(None)
|
20
test/testContextManagerRaisesInExit.krk
Normal file
20
test/testContextManagerRaisesInExit.krk
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
class Context:
|
||||||
|
def __enter__(self):
|
||||||
|
print("Entering")
|
||||||
|
def __exit__(self, *args):
|
||||||
|
print("Exiting with",[type(x) for x in args])
|
||||||
|
raise TypeError()
|
||||||
|
|
||||||
|
def withException():
|
||||||
|
print("Before")
|
||||||
|
with Context() as c:
|
||||||
|
print("Raising")
|
||||||
|
raise ValueError()
|
||||||
|
print("Don't print me")
|
||||||
|
print("After")
|
||||||
|
|
||||||
|
try:
|
||||||
|
withException()
|
||||||
|
except Exception as e:
|
||||||
|
print(repr(e))
|
||||||
|
|
5
test/testContextManagerRaisesInExit.krk.expect
Normal file
5
test/testContextManagerRaisesInExit.krk.expect
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Before
|
||||||
|
Entering
|
||||||
|
Raising
|
||||||
|
Exiting with [<class 'type'>, <class 'ValueError'>, <class 'list'>]
|
||||||
|
TypeError(None)
|
Loading…
Reference in New Issue
Block a user