From 66f7bbd18c50f814e48e14b7591014665e7f2b63 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 11 Aug 2024 13:05:56 +0800 Subject: [PATCH] ... --- include/pocketpy/interpreter/vm.h | 1 + include/pocketpy/pocketpy.h | 5 ++++- include/pocketpy/xmacros/opcodes.h | 3 ++- src/compiler/compiler.c | 4 ++-- src/interpreter/ceval.c | 6 +++++- src/interpreter/vm.c | 1 + src/public/internal.c | 2 +- src/public/py_exception.c | 31 +++++++++++++++++++++++++----- src2/main.c | 2 +- tests/28_exception.py | 3 +++ 10 files changed, 46 insertions(+), 12 deletions(-) diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 3a7ebba3..922db351 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/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 diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 57bee28c..98f67823 100644 --- a/include/pocketpy/pocketpy.h +++ b/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. diff --git a/include/pocketpy/xmacros/opcodes.h b/include/pocketpy/xmacros/opcodes.h index 4c479aac..bab48a10 100644 --- a/include/pocketpy/xmacros/opcodes.h +++ b/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) diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index d05480d6..1dd95054 100644 --- a/src/compiler/compiler.c +++ b/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); diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 7e9cf108..0934ddbf 100644 --- a/src/interpreter/ceval.c +++ b/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(); diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 9cc75821..8714fc69 100644 --- a/src/interpreter/vm.c +++ b/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; diff --git a/src/public/internal.c b/src/public/internal.c index 36d40699..36d98b5a 100644 --- a/src/public/internal.c +++ b/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 diff --git a/src/public/py_exception.c b/src/public/py_exception.c index 681822ba..2096831d 100644 --- a/src/public/py_exception.c +++ b/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 = ""; + } 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; } diff --git a/src2/main.c b/src2/main.c index cad5340a..f947201b 100644 --- a/src2/main.c +++ b/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; } diff --git a/tests/28_exception.py b/tests/28_exception.py index b3101815..f5022d6b 100644 --- a/tests/28_exception.py +++ b/tests/28_exception.py @@ -7,6 +7,9 @@ try: except IndexError: pass +k = KeyError('foo') +assert str(k) == "'foo'" + try: assert False exit(1)