blueloveTH 1 năm trước cách đây
mục cha
commit
66f7bbd18c

+ 1 - 0
include/pocketpy/interpreter/vm.h

@@ -47,6 +47,7 @@ typedef struct VM {
 
     py_TValue last_retval;
     py_TValue curr_exception;
+    bool is_curr_exc_handled;  // handled by try-except block but not cleared yet
     bool is_stopiteration;
 
     py_TValue reg[8];  // users' registers

+ 4 - 1
include/pocketpy/pocketpy.h

@@ -393,13 +393,16 @@ bool py_exception(py_Type type, const char* fmt, ...) PY_RAISE;
 /// Raise an expection object. Always return false.
 bool py_raise(py_Ref) PY_RAISE;
 /// Print the current exception.
+/// The exception will be set as handled.
 void py_printexc();
 /// Format the current exception and return a null-terminated string.
 /// The result should be freed by the caller.
+/// The exception will be set as handled.
 char* py_formatexc();
 /// Check if an exception is raised.
-bool py_checkexc();
+bool py_checkexc(bool ignore_handled);
 /// Check if the exception is an instance of the given type.
+/// If match, the exception will be set as handled.
 bool py_matchexc(py_Type type);
 /// Clear the current exception.
 /// @param p0 the unwinding point. Use `NULL` if not needed.

+ 2 - 1
include/pocketpy/xmacros/opcodes.h

@@ -105,7 +105,8 @@ OPCODE(RAISE)
 OPCODE(RAISE_ASSERT)
 OPCODE(RE_RAISE)
 OPCODE(PUSH_EXCEPTION)
-OPCODE(POP_EXCEPTION)
+OPCODE(BEGIN_EXC_HANDLING)
+OPCODE(END_EXC_HANDLING)
 /**************************/
 OPCODE(FSTRING_EVAL)
 OPCODE(FORMAT_STRING)

+ 2 - 2
src/compiler/compiler.c

@@ -2519,13 +2519,13 @@ static Error* compile_try_except(Compiler* self) {
         }
         int patch = Ctx__emit_(ctx(), OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE);
         // on match
+        Ctx__emit_(ctx(), OP_BEGIN_EXC_HANDLING, BC_NOARG, BC_KEEPLINE);
         if(as_name) {
             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);
+        Ctx__emit_(ctx(), OP_END_EXC_HANDLING, BC_NOARG, BC_KEEPLINE);
         patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
         Ctx__patch_jump(ctx(), patch);
     } while(curr()->type == TK_EXCEPT);

+ 5 - 1
src/interpreter/ceval.c

@@ -1013,7 +1013,11 @@ FrameResult VM__run_top_frame(VM* self) {
                 PUSH(&self->curr_exception);
                 DISPATCH();
             }
-            case OP_POP_EXCEPTION: {
+            case OP_BEGIN_EXC_HANDLING: {
+                self->is_curr_exc_handled = true;
+                DISPATCH();
+            }
+            case OP_END_EXC_HANDLING: {
                 assert(self->curr_exception.type);
                 py_clearexc(NULL);
                 DISPATCH();

+ 1 - 0
src/interpreter/vm.c

@@ -69,6 +69,7 @@ void VM__ctor(VM* self) {
 
     self->last_retval = *py_NIL;
     self->curr_exception = *py_NIL;
+    self->is_curr_exc_handled = false;
     self->is_stopiteration = false;
 
     self->__curr_class = NULL;

+ 1 - 1
src/public/internal.c

@@ -104,7 +104,7 @@ bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) {
         c11__abort(
             "py_CFunction returns nothing! Did you forget to call `py_newnone(py_retval())`?");
     }
-    // if(py_checkexc()) { c11__abort("py_CFunction returns `true` but an exception is set!"); }
+    if(py_checkexc(true)) { c11__abort("py_CFunction returns `true` but an exception is set!"); }
     return true;
 }
 #endif

+ 26 - 5
src/public/py_exception.c

@@ -88,7 +88,11 @@ static bool _py_BaseException__str__(int argc, py_Ref argv) {
     c11_sbuf__ctor(&ss);
     py_Ref arg = py_getslot(argv, 0);
     if(!py_isnil(arg)) {
-        if(!py_str(arg)) return false;
+        if(argv->type == tp_KeyError) {
+            if(!py_repr(arg)) return false;
+        } else {
+            if(!py_str(arg)) return false;
+        }
         c11_sbuf__write_sv(&ss, py_tosv(py_retval()));
     }
     c11_sbuf__py_submit(&ss, py_retval());
@@ -111,21 +115,29 @@ py_Type pk_Exception__register() {
 }
 
 //////////////////////////////////////////////////
-bool py_checkexc() {
+bool py_checkexc(bool ignore_handled) {
     VM* vm = pk_current_vm;
+    if(ignore_handled && vm->is_curr_exc_handled) return false;
     return !py_isnil(&vm->curr_exception);
 }
 
 bool py_matchexc(py_Type type) {
     VM* vm = pk_current_vm;
+    if(vm->is_curr_exc_handled) return false;
     if(py_isnil(&vm->curr_exception)) return false;
-    return py_issubclass(vm->curr_exception.type, type);
+    bool ok = py_issubclass(vm->curr_exception.type, type);
+    if(ok) {
+        // if match, then the exception is handled
+        vm->is_curr_exc_handled = true;
+    }
+    return ok;
 }
 
 void py_clearexc(py_StackRef p0) {
     VM* vm = pk_current_vm;
     vm->last_retval = *py_NIL;
     vm->curr_exception = *py_NIL;
+    vm->is_curr_exc_handled = false;
     vm->is_stopiteration = false;
     vm->__curr_class = NULL;
     if(p0) vm->stack.sp = p0;
@@ -155,9 +167,13 @@ static void c11_sbuf__write_exc(c11_sbuf* self, py_Ref exc) {
     }
 
     const char* name = py_tpname(exc->type);
+    const char* message;
     bool ok = py_str(exc);
-    if(!ok) c11__abort("py_printexc(): failed to convert exception to string");
-    const char* message = py_tostr(py_retval());
+    if(!ok || !py_isstr(py_retval())) {
+        message = "<exception str() failed>";
+    } else {
+        message = py_tostr(py_retval());
+    }
 
     c11_sbuf__write_cstr(self, name);
     c11_sbuf__write_cstr(self, ": ");
@@ -167,6 +183,10 @@ static void c11_sbuf__write_exc(c11_sbuf* self, py_Ref exc) {
 char* py_formatexc() {
     VM* vm = pk_current_vm;
     if(py_isnil(&vm->curr_exception)) return NULL;
+
+    // when you call `py_formatexc()`, you are handling the exception
+    vm->is_curr_exc_handled = true;
+
     c11_sbuf ss;
     c11_sbuf__ctor(&ss);
 
@@ -215,6 +235,7 @@ bool py_raise(py_Ref exc) {
         py_setslot(exc, 1, &vm->curr_exception);
     }
     vm->curr_exception = *exc;
+    vm->is_curr_exc_handled = false;
     return false;
 }
 

+ 1 - 1
src2/main.c

@@ -68,7 +68,7 @@ int main(int argc, char** argv) {
         }
     }
 
-    int code = py_checkexc() ? 1 : 0;
+    int code = py_checkexc(false) ? 1 : 0;
     py_finalize();
     return code;
 }

+ 3 - 0
tests/28_exception.py

@@ -7,6 +7,9 @@ try:
 except IndexError:
     pass
 
+k = KeyError('foo')
+assert str(k) == "'foo'"
+
 try:
     assert False
     exit(1)