From f38a451f19b0b20efd4ff289f5b1b492caff17d0 Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Sun, 3 Jul 2022 20:39:07 +0900 Subject: [PATCH] Attach and build upon partial tracebacks If an exception is raised within a 'try', attach a traceback only up until that 'try'. If a re-raised exception has a traceback already, attach the new traceback "above" the existing one. --- src/vm.c | 35 +++++++++++++---- test/testExceptionTracebacks.krk.expect | 2 - test/testReraisedExceptionTracebacks.krk | 39 +++++++++++++++++++ ...testReraisedExceptionTracebacks.krk.expect | 11 ++++++ 4 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 test/testReraisedExceptionTracebacks.krk create mode 100644 test/testReraisedExceptionTracebacks.krk.expect diff --git a/src/vm.c b/src/vm.c index cec9c54..05dc736 100644 --- a/src/vm.c +++ b/src/vm.c @@ -263,16 +263,28 @@ void krk_dumpTraceback(void) { static void attachTraceback(void) { if (IS_INSTANCE(krk_currentThread.currentException)) { KrkInstance * theException = AS_INSTANCE(krk_currentThread.currentException); - - /* If there already is a traceback, don't add a new one; this exception was re-raised. */ - KrkValue existing; - if (krk_tableGet(&theException->fields, OBJECT_VAL(S("traceback")), &existing)) return; - - KrkValue tracebackList = krk_list_of(0,NULL,0); + KrkValue tracebackList; + if (krk_tableGet(&theException->fields, OBJECT_VAL(S("traceback")), &tracebackList)) { + krk_push(tracebackList); + } else { + krk_push(NONE_VAL()); + } + tracebackList = krk_list_of(0,NULL,0); krk_push(tracebackList); + /* Build the traceback object */ if (krk_currentThread.frameCount) { - for (size_t i = 0; i < krk_currentThread.frameCount; i++) { + + /* Go up until we get to the exit frame */ + size_t frameOffset = 0; + if (krk_currentThread.stackTop > krk_currentThread.stack) { + size_t stackOffset = krk_currentThread.stackTop - krk_currentThread.stack - 1; + while (stackOffset > 0 && !IS_TRY_HANDLER(krk_currentThread.stack[stackOffset])) stackOffset--; + frameOffset = krk_currentThread.frameCount - 1; + while (frameOffset > 0 && krk_currentThread.frames[frameOffset].slots > stackOffset) frameOffset--; + } + + for (size_t i = frameOffset; i < krk_currentThread.frameCount; i++) { KrkCallFrame * frame = &krk_currentThread.frames[i]; KrkTuple * tbEntry = krk_newTuple(2); krk_push(OBJECT_VAL(tbEntry)); @@ -282,8 +294,17 @@ static void attachTraceback(void) { krk_pop(); } } + + if (IS_list(krk_peek(1))) { + KrkValueArray * existingTraceback = AS_LIST(krk_peek(1)); + for (size_t i = 0; i < existingTraceback->count; ++i) { + krk_writeValueArray(AS_LIST(tracebackList), existingTraceback->values[i]); + } + } + krk_attachNamedValue(&theException->fields, "traceback", tracebackList); krk_pop(); + krk_pop(); } /* else: probably a legacy 'raise str', just don't bother. */ } diff --git a/test/testExceptionTracebacks.krk.expect b/test/testExceptionTracebacks.krk.expect index 7919877..b699f1e 100644 --- a/test/testExceptionTracebacks.krk.expect +++ b/test/testExceptionTracebacks.krk.expect @@ -1,7 +1,5 @@ Caught a TypeError('A type error') - File 'test/testExceptionTracebacks.krk', line 14, in File 'test/testExceptionTracebacks.krk', line 4, in doTheThing Caught a ValueError('A value error') - File 'test/testExceptionTracebacks.krk', line 15, in File 'test/testExceptionTracebacks.krk', line 4, in doTheThing That's a name error! diff --git a/test/testReraisedExceptionTracebacks.krk b/test/testReraisedExceptionTracebacks.krk new file mode 100644 index 0000000..0c25b80 --- /dev/null +++ b/test/testReraisedExceptionTracebacks.krk @@ -0,0 +1,39 @@ +def foo(): + try: + raise ValueError("oh no") + except Exception as e: + raise e + +if True: + try: + foo() + except Exception as e: + print("Traceback entries") + for i in e.traceback: + let func, instr = i + print(f" File '{func.__file__}', line {func._ip_to_line(instr)}, in {func.__name__}") +else: + foo() + +def bar(n): + if n == 3: + raise ValueError(n) + else: + bar(n+1) + +if True: + try: + try: + bar(0) + except Exception as e: + raise e + except Exception as e: + print("Traceback entries") + for i in e.traceback: + let func, instr = i + print(f" File '{func.__file__}', line {func._ip_to_line(instr)}, in {func.__name__}") +else: + try: + bar(0) + except Exception as e: + raise e diff --git a/test/testReraisedExceptionTracebacks.krk.expect b/test/testReraisedExceptionTracebacks.krk.expect new file mode 100644 index 0000000..fe6aee4 --- /dev/null +++ b/test/testReraisedExceptionTracebacks.krk.expect @@ -0,0 +1,11 @@ +Traceback entries + File 'test/testReraisedExceptionTracebacks.krk', line 9, in + File 'test/testReraisedExceptionTracebacks.krk', line 5, in foo + File 'test/testReraisedExceptionTracebacks.krk', line 3, in foo +Traceback entries + File 'test/testReraisedExceptionTracebacks.krk', line 29, in + File 'test/testReraisedExceptionTracebacks.krk', line 27, in + File 'test/testReraisedExceptionTracebacks.krk', line 22, in bar + File 'test/testReraisedExceptionTracebacks.krk', line 22, in bar + File 'test/testReraisedExceptionTracebacks.krk', line 22, in bar + File 'test/testReraisedExceptionTracebacks.krk', line 20, in bar