This commit is contained in:
blueloveTH 2024-08-11 13:05:56 +08:00
parent 145782b789
commit 66f7bbd18c
10 changed files with 46 additions and 12 deletions

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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);

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

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