blueloveTH 1 year ago
parent
commit
0918256c90
3 changed files with 36 additions and 19 deletions
  1. 2 2
      include/pocketpy/pocketpy.h
  2. 1 1
      src/compiler/compiler.c
  3. 33 16
      src/public/py_exception.c

+ 2 - 2
include/pocketpy/pocketpy.h

@@ -415,8 +415,8 @@ enum py_PredefinedTypes {
     tp_function,
     tp_nativefunc,
     tp_boundmethod,
-    tp_super,  // 1 slot + py_Type
-    tp_BaseException,
+    tp_super,          // 1 slot + py_Type
+    tp_BaseException,  // 2 slots (arg + inner exc)
     tp_Exception,
     tp_bytes,
     tp_mappingproxy,

+ 1 - 1
src/compiler/compiler.c

@@ -2544,9 +2544,9 @@ static Error* compile_try_except(Compiler* self) {
             Ctx__emit_(ctx(), OP_PUSH_EXCEPTION, BC_NOARG, BC_KEEPLINE);
             Ctx__emit_store_name(ctx(), name_scope(self), as_name, BC_KEEPLINE);
         }
+        check(compile_block_body(self, compile_stmt));
         // pop the exception
         Ctx__emit_(ctx(), OP_POP_EXCEPTION, BC_NOARG, BC_KEEPLINE);
-        check(compile_block_body(self, compile_stmt));
         patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
         Ctx__patch_jump(ctx(), patch);
     } while(curr()->type == TK_EXCEPT);

+ 33 - 16
src/public/py_exception.c

@@ -52,8 +52,10 @@ static void BaseException__dtor(void* ud) {
 
 static bool _py_BaseException__new__(int argc, py_Ref argv) {
     py_Type cls = py_totype(argv);
-    BaseException* ud = py_newobject(py_retval(), cls, 1, sizeof(BaseException));
+    BaseException* ud = py_newobject(py_retval(), cls, 2, sizeof(BaseException));
     c11_vector__ctor(&ud->stacktrace, sizeof(BaseExceptionFrame));
+    py_setslot(py_retval(), 0, py_NIL);
+    py_setslot(py_retval(), 1, py_NIL);
     ud->lineno_backup = -1;
     ud->code_backup = NULL;
     return true;
@@ -138,34 +140,45 @@ void py_printexc() {
     free(msg);
 }
 
-char* py_formatexc() {
-    VM* vm = pk_current_vm;
-    if(py_isnil(&vm->curr_exception)) return NULL;
-    c11_sbuf ss;
-    c11_sbuf__ctor(&ss);
-
-    if(true) { c11_sbuf__write_cstr(&ss, "Traceback (most recent call last):\n"); }
+static void c11_sbuf__write_exc(c11_sbuf* self, py_Ref exc){
+    if(true) { c11_sbuf__write_cstr(self, "Traceback (most recent call last):\n"); }
 
-    BaseException* ud = py_touserdata(&vm->curr_exception);
+    BaseException* ud = py_touserdata(exc);
 
     for(int i = ud->stacktrace.count - 1; i >= 0; i--) {
         BaseExceptionFrame* frame = c11__at(BaseExceptionFrame, &ud->stacktrace, i);
         SourceData__snapshot(frame->src,
-                             &ss,
+                             self,
                              frame->lineno,
                              NULL,
                              frame->name ? frame->name->data : NULL);
-        c11_sbuf__write_char(&ss, '\n');
+        c11_sbuf__write_char(self, '\n');
     }
 
-    const char* name = py_tpname(vm->curr_exception.type);
-    bool ok = py_str(&vm->curr_exception);
+    const char* name = py_tpname(exc->type);
+    bool ok = py_str(exc);
     if(!ok) c11__abort("py_printexc(): failed to convert exception to string");
     const char* message = py_tostr(py_retval());
 
-    c11_sbuf__write_cstr(&ss, name);
-    c11_sbuf__write_cstr(&ss, ": ");
-    c11_sbuf__write_cstr(&ss, message);
+    c11_sbuf__write_cstr(self, name);
+    c11_sbuf__write_cstr(self, ": ");
+    c11_sbuf__write_cstr(self, message);
+}
+
+char* py_formatexc() {
+    VM* vm = pk_current_vm;
+    if(py_isnil(&vm->curr_exception)) return NULL;
+    c11_sbuf ss;
+    c11_sbuf__ctor(&ss);
+
+    py_Ref inner = py_getslot(&vm->curr_exception, 1);
+    if(py_isnil(inner)) {
+        c11_sbuf__write_exc(&ss, &vm->curr_exception);
+    } else {
+        c11_sbuf__write_exc(&ss, inner);
+        c11_sbuf__write_cstr(&ss, "\n\nDuring handling of the above exception, another exception occurred:\n\n");
+        c11_sbuf__write_exc(&ss, &vm->curr_exception);
+    }
 
     c11_string* res = c11_sbuf__submit(&ss);
     char* dup = malloc(res->size + 1);
@@ -196,6 +209,10 @@ bool py_exception(py_Type type, const char* fmt, ...) {
 bool py_raise(py_Ref exc) {
     assert(py_isinstance(exc, tp_BaseException));
     VM* vm = pk_current_vm;
+    if(!py_isnil(&vm->curr_exception)){
+        // inner exception
+        py_setslot(exc, 1, &vm->curr_exception);
+    }
     vm->curr_exception = *exc;
     return false;
 }