mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-19 19:10:17 +00:00
Merge branch 'pocketpy:main' into gsoc-2025-debugger
This commit is contained in:
commit
28c079cd8c
@ -159,7 +159,7 @@ __ERROR:
|
||||
| F-String | `f'value is {x}'` | ✅ |
|
||||
| Unpacking | `a, b = 1, 2` | ✅ |
|
||||
| Star Unpacking | `a, *b = [1, 2, 3]` | ✅ |
|
||||
| Exception | `raise/try..catch..finally` | ✅ |
|
||||
| Exception | `raise/try..except..` | ✅ |
|
||||
| Dynamic Code | `eval()/exec()` | ✅ |
|
||||
| Reflection | `hasattr()/getattr()/setattr()` | ✅ |
|
||||
| Import | `import/from..import` | ✅ |
|
||||
|
@ -20,7 +20,7 @@ The following table shows the basic features of pkpy with respect to [cpython](h
|
||||
| F-String | `f'value is {x}'` | ✅ |
|
||||
| Unpacking | `a, b = 1, 2` | ✅ |
|
||||
| Star Unpacking | `a, *b = [1, 2, 3]` | ✅ |
|
||||
| Exception | `raise/try..catch..finally` | ✅ |
|
||||
| Exception | `raise/try..except..` | ✅ |
|
||||
| Dynamic Code | `eval()/exec()` | ✅ |
|
||||
| Reflection | `hasattr()/getattr()/setattr()` | ✅ |
|
||||
| Import | `import/from..import` | ✅ |
|
||||
|
@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
// clang-format off
|
||||
|
||||
#define PK_VERSION "2.1.1"
|
||||
#define PK_VERSION "2.1.2"
|
||||
#define PK_VERSION_MAJOR 2
|
||||
#define PK_VERSION_MINOR 1
|
||||
#define PK_VERSION_PATCH 1
|
||||
#define PK_VERSION_PATCH 2
|
||||
|
||||
/*************** feature settings ***************/
|
||||
#ifndef PK_ENABLE_OS // can be overridden by cmake
|
||||
|
@ -11,20 +11,14 @@ typedef struct ValueStack {
|
||||
py_TValue* sp;
|
||||
py_TValue* end;
|
||||
// We allocate extra places to keep `_sp` valid to detect stack overflow
|
||||
py_TValue begin[PK_VM_STACK_SIZE + PK_MAX_CO_VARNAMES * 2];
|
||||
py_TValue begin[PK_VM_STACK_SIZE + PK_MAX_CO_VARNAMES];
|
||||
} ValueStack;
|
||||
|
||||
void ValueStack__ctor(ValueStack* self);
|
||||
void ValueStack__dtor(ValueStack* self);
|
||||
|
||||
typedef struct UnwindTarget {
|
||||
struct UnwindTarget* next;
|
||||
int iblock;
|
||||
int offset;
|
||||
} UnwindTarget;
|
||||
|
||||
UnwindTarget* UnwindTarget__new(UnwindTarget* next, int iblock, int offset);
|
||||
void UnwindTarget__delete(UnwindTarget* self);
|
||||
typedef struct FrameExcInfo {
|
||||
int iblock; // try block index
|
||||
int offset; // stack offset from p0
|
||||
py_TValue exc; // handled exception
|
||||
} FrameExcInfo;
|
||||
|
||||
typedef struct py_Frame {
|
||||
struct py_Frame* f_back;
|
||||
@ -35,7 +29,7 @@ typedef struct py_Frame {
|
||||
py_Ref locals;
|
||||
bool is_locals_special;
|
||||
int ip;
|
||||
UnwindTarget* uw_list;
|
||||
c11_vector /*T=FrameExcInfo*/ exc_stack;
|
||||
} py_Frame;
|
||||
|
||||
typedef struct SourceLocation {
|
||||
@ -61,10 +55,9 @@ int Frame__delglobal(py_Frame* self, py_Name name) PY_RAISE;
|
||||
py_Ref Frame__getclosure(py_Frame* self, py_Name name);
|
||||
py_StackRef Frame__getlocal_noproxy(py_Frame* self, py_Name name);
|
||||
|
||||
int Frame__prepare_jump_exception_handler(py_Frame* self, ValueStack*);
|
||||
|
||||
UnwindTarget* Frame__find_unwind_target(py_Frame* self, int iblock);
|
||||
void Frame__set_unwind_target(py_Frame* self, py_TValue* sp);
|
||||
int Frame__goto_exception_handler(py_Frame* self, ValueStack*, py_Ref);
|
||||
void Frame__begin_try(py_Frame* self, py_TValue* sp);
|
||||
FrameExcInfo* Frame__top_exc_info(py_Frame* self);
|
||||
|
||||
void Frame__gc_mark(py_Frame* self, c11_vector* p_stack);
|
||||
SourceLocation Frame__source_location(py_Frame* self);
|
@ -53,13 +53,11 @@ typedef struct VM {
|
||||
py_Callbacks callbacks;
|
||||
|
||||
py_TValue last_retval;
|
||||
py_TValue curr_exception;
|
||||
py_TValue unhandled_exc;
|
||||
|
||||
int recursion_depth;
|
||||
int max_recursion_depth;
|
||||
|
||||
bool is_curr_exc_handled; // handled by try-except block but not cleared yet
|
||||
|
||||
py_TValue reg[8]; // users' registers
|
||||
void* ctx; // user-defined context
|
||||
|
||||
|
@ -35,7 +35,6 @@ typedef enum CodeBlockType {
|
||||
CodeBlockType_WITH,
|
||||
/* context blocks (flag-based) */
|
||||
CodeBlockType_EXCEPT,
|
||||
CodeBlockType_FINALLY,
|
||||
} CodeBlockType;
|
||||
|
||||
typedef enum Opcode {
|
||||
|
@ -17,3 +17,5 @@ typedef struct BaseException {
|
||||
c11_vector /*T=BaseExceptionFrame*/ stacktrace;
|
||||
} BaseException;
|
||||
|
||||
char* safe_stringify_exception(py_Ref exc);
|
||||
char* formatexc_internal(py_Ref exc);
|
@ -600,20 +600,17 @@ PK_API int py_import(const char* path) PY_RAISE PY_RETURN;
|
||||
PK_API bool py_exception(py_Type type, const char* fmt, ...) PY_RAISE;
|
||||
/// Raise an exception object. Always return false.
|
||||
PK_API bool py_raise(py_Ref) PY_RAISE;
|
||||
/// Print the current exception.
|
||||
/// The exception will be set as handled.
|
||||
/// Print the unhandled exception.
|
||||
PK_API 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.
|
||||
/// Format the unhandled exception and return a null-terminated string.
|
||||
/// The returned string should be freed by the caller.
|
||||
PK_API char* py_formatexc();
|
||||
/// Check if an exception is raised.
|
||||
PK_API bool py_checkexc(bool ignore_handled);
|
||||
/// Check if the exception is an instance of the given type.
|
||||
/// This function is roughly equivalent to python's `except <T> as e:` block.
|
||||
/// If match, the exception will be stored in `py_retval()` as handled.
|
||||
/// Check if there is an unhandled exception.
|
||||
PK_API bool py_checkexc();
|
||||
/// Check if the unhandled exception is an instance of the given type.
|
||||
/// If match, the exception will be stored in `py_retval()`.
|
||||
PK_API bool py_matchexc(py_Type type) PY_RETURN;
|
||||
/// Clear the current exception.
|
||||
/// Clear the unhandled exception.
|
||||
/// @param p0 the unwinding point. Use `NULL` if not needed.
|
||||
PK_API void py_clearexc(py_StackRef p0);
|
||||
|
||||
|
@ -116,16 +116,14 @@ OPCODE(ADD_CLASS_ANNOTATION)
|
||||
OPCODE(WITH_ENTER)
|
||||
OPCODE(WITH_EXIT)
|
||||
/**************************/
|
||||
OPCODE(TRY_ENTER)
|
||||
OPCODE(BEGIN_TRY)
|
||||
OPCODE(END_TRY)
|
||||
OPCODE(EXCEPTION_MATCH)
|
||||
OPCODE(HANDLE_EXCEPTION)
|
||||
OPCODE(RAISE)
|
||||
OPCODE(RAISE_ASSERT)
|
||||
OPCODE(RE_RAISE)
|
||||
OPCODE(PUSH_EXCEPTION)
|
||||
OPCODE(BEGIN_EXC_HANDLING)
|
||||
OPCODE(END_EXC_HANDLING)
|
||||
OPCODE(BEGIN_FINALLY)
|
||||
OPCODE(END_FINALLY)
|
||||
/**************************/
|
||||
OPCODE(FORMAT_STRING)
|
||||
/**************************/
|
||||
|
@ -1132,12 +1132,12 @@ static int Ctx__prepare_loop_divert(Ctx* self, int line, bool is_break) {
|
||||
Ctx__emit_(self, OP_POP_TOP, BC_NOARG, line);
|
||||
break;
|
||||
}
|
||||
case CodeBlockType_EXCEPT: {
|
||||
Ctx__emit_(self, OP_END_EXC_HANDLING, 1, line);
|
||||
case CodeBlockType_TRY: {
|
||||
Ctx__emit_(self, OP_END_TRY, BC_NOARG, line);
|
||||
break;
|
||||
}
|
||||
case CodeBlockType_FINALLY: {
|
||||
Ctx__emit_(self, OP_END_FINALLY, 1, line);
|
||||
case CodeBlockType_EXCEPT: {
|
||||
Ctx__emit_(self, OP_END_TRY, BC_NOARG, line);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
@ -1919,9 +1919,11 @@ static Error* exprCompileTimeCall(Compiler* self, py_ItemRef func, int line) {
|
||||
} while(match(TK_COMMA));
|
||||
consume(TK_RPAREN);
|
||||
|
||||
py_StackRef p0 = py_peek(0);
|
||||
bool ok = py_vectorcall(argc, kwargc);
|
||||
if(!ok) {
|
||||
char* msg = py_formatexc();
|
||||
py_clearexc(p0);
|
||||
err = SyntaxError(self, "compile-time call error:\n%s", msg);
|
||||
PK_FREE(msg);
|
||||
return err;
|
||||
@ -2000,7 +2002,7 @@ static Error* exprSlice0(Compiler* self) {
|
||||
check(EXPR(self));
|
||||
slice->step = Ctx__s_popx(ctx());
|
||||
} // else ::
|
||||
} // else :
|
||||
} // else :
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -2627,8 +2629,9 @@ static Error* compile_try_except(Compiler* self) {
|
||||
int patches_length = 0;
|
||||
|
||||
Ctx__enter_block(ctx(), CodeBlockType_TRY);
|
||||
Ctx__emit_(ctx(), OP_TRY_ENTER, BC_NOARG, prev()->line);
|
||||
Ctx__emit_(ctx(), OP_BEGIN_TRY, BC_NOARG, prev()->line);
|
||||
check(compile_block_body(self));
|
||||
Ctx__emit_(ctx(), OP_END_TRY, BC_NOARG, BC_KEEPLINE);
|
||||
|
||||
// https://docs.python.org/3/reference/compound_stmts.html#finally-clause
|
||||
/* If finally is present, it specifies a ‘cleanup’ handler. The try clause is executed,
|
||||
@ -2644,23 +2647,10 @@ static Error* compile_try_except(Compiler* self) {
|
||||
// A return, break, continue in try/except block will make the finally block not executed
|
||||
|
||||
bool has_finally = curr()->type == TK_FINALLY;
|
||||
if(!has_finally) {
|
||||
patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
|
||||
}
|
||||
Ctx__exit_block(ctx());
|
||||
if(has_finally) return SyntaxError(self, "finally clause is not supported yet");
|
||||
|
||||
if(has_finally) {
|
||||
consume(TK_FINALLY);
|
||||
Ctx__emit_(ctx(), OP_BEGIN_FINALLY, BC_NOARG, prev()->line);
|
||||
// finally only, no except block
|
||||
Ctx__enter_block(ctx(), CodeBlockType_FINALLY);
|
||||
check(compile_block_body(self));
|
||||
Ctx__exit_block(ctx());
|
||||
Ctx__emit_(ctx(), OP_END_FINALLY, BC_NOARG, BC_KEEPLINE);
|
||||
// re-raise if needed
|
||||
Ctx__emit_(ctx(), OP_RE_RAISE, BC_NOARG, BC_KEEPLINE);
|
||||
return NULL;
|
||||
}
|
||||
patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
|
||||
Ctx__exit_block(ctx());
|
||||
|
||||
do {
|
||||
if(patches_length == 8) {
|
||||
@ -2684,7 +2674,7 @@ 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);
|
||||
Ctx__emit_(ctx(), OP_HANDLE_EXCEPTION, 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);
|
||||
@ -2692,27 +2682,20 @@ static Error* compile_try_except(Compiler* self) {
|
||||
Ctx__enter_block(ctx(), CodeBlockType_EXCEPT);
|
||||
check(compile_block_body(self));
|
||||
Ctx__exit_block(ctx());
|
||||
Ctx__emit_(ctx(), OP_END_EXC_HANDLING, BC_NOARG, BC_KEEPLINE);
|
||||
Ctx__emit_(ctx(), OP_END_TRY, 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);
|
||||
|
||||
// no match, re-raise
|
||||
// ...
|
||||
Ctx__emit_(ctx(), OP_RE_RAISE, BC_NOARG, BC_KEEPLINE);
|
||||
|
||||
// match one & handled, jump to the end
|
||||
for(int i = 0; i < patches_length; i++)
|
||||
for(int i = 0; i < patches_length; i++) {
|
||||
Ctx__patch_jump(ctx(), patches[i]);
|
||||
|
||||
if(match(TK_FINALLY)) {
|
||||
Ctx__emit_(ctx(), OP_BEGIN_FINALLY, BC_NOARG, prev()->line);
|
||||
Ctx__enter_block(ctx(), CodeBlockType_FINALLY);
|
||||
check(compile_block_body(self));
|
||||
Ctx__exit_block(ctx());
|
||||
Ctx__emit_(ctx(), OP_END_FINALLY, BC_NOARG, BC_KEEPLINE);
|
||||
}
|
||||
// re-raise if needed
|
||||
Ctx__emit_(ctx(), OP_RE_RAISE, BC_NOARG, BC_KEEPLINE);
|
||||
|
||||
if(match(TK_FINALLY)) return SyntaxError(self, "finally clause is not supported yet");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -2815,9 +2798,19 @@ static Error* compile_stmt(Compiler* self) {
|
||||
consume_end_stmt();
|
||||
break;
|
||||
case TK_RAISE: {
|
||||
check(EXPR(self));
|
||||
Ctx__s_emit_top(ctx());
|
||||
Ctx__emit_(ctx(), OP_RAISE, BC_NOARG, kw_line);
|
||||
if(is_expression(self, false)) {
|
||||
check(EXPR(self));
|
||||
Ctx__s_emit_top(ctx());
|
||||
Ctx__emit_(ctx(), OP_RAISE, BC_NOARG, kw_line);
|
||||
} else {
|
||||
int iblock = ctx()->curr_iblock;
|
||||
CodeBlock* blocks = (CodeBlock*)ctx()->co->blocks.data;
|
||||
if(blocks[iblock].type != CodeBlockType_EXCEPT) {
|
||||
return SyntaxError(self,
|
||||
"raise without exception is only allowed in except block");
|
||||
}
|
||||
Ctx__emit_(ctx(), OP_RE_RAISE, BC_NOARG, kw_line);
|
||||
}
|
||||
consume_end_stmt();
|
||||
} break;
|
||||
case TK_DEL: {
|
||||
|
@ -108,22 +108,15 @@ void c11_debugger_exception_on_trace(py_Ref exc) {
|
||||
BaseException* ud = py_touserdata(exc);
|
||||
c11_vector* stacktrace = &ud->stacktrace;
|
||||
const char* name = py_tpname(exc->type);
|
||||
const char* message;
|
||||
bool ok = py_str(exc);
|
||||
if(!ok || !py_isstr(py_retval())) {
|
||||
message = "<exception str() failed>";
|
||||
} else {
|
||||
message = c11_strdup(py_tostr(py_retval()));
|
||||
}
|
||||
const char* message = safe_stringify_exception(exc);
|
||||
debugger.exception_stacktrace = stacktrace;
|
||||
debugger.isexceptionmode = true;
|
||||
debugger.current_excname = name;
|
||||
debugger.current_excmessage = message;
|
||||
clear_structures();
|
||||
|
||||
}
|
||||
|
||||
const char* c11_debugger_excinfo(const char ** message){
|
||||
const char* c11_debugger_excinfo(const char** message) {
|
||||
*message = debugger.current_excmessage;
|
||||
return debugger.current_excname;
|
||||
}
|
||||
@ -183,7 +176,8 @@ bool c11_debugger_path_equal(const char* path1, const char* path2) {
|
||||
}
|
||||
|
||||
C11_STOP_REASON c11_debugger_should_pause() {
|
||||
if(debugger.current_event == TRACE_EVENT_POP && !debugger.isexceptionmode) return C11_DEBUGGER_NOSTOP;
|
||||
if(debugger.current_event == TRACE_EVENT_POP && !debugger.isexceptionmode)
|
||||
return C11_DEBUGGER_NOSTOP;
|
||||
C11_STOP_REASON pause_resaon = C11_DEBUGGER_NOSTOP;
|
||||
int is_out = debugger.curr_stack_depth <= debugger.pause_allowed_depth;
|
||||
int is_new_line = debugger.current_line != debugger.step_line;
|
||||
@ -399,4 +393,4 @@ bool c11_debugger_unfold_var(int var_id, c11_sbuf* buffer) {
|
||||
|
||||
#undef python_vars
|
||||
|
||||
#endif // PK_ENABLE_OS
|
||||
#endif // PK_ENABLE_OS
|
||||
|
@ -13,9 +13,6 @@
|
||||
|
||||
static bool stack_format_object(VM* self, c11_sv spec);
|
||||
|
||||
#define CHECK_RETURN_FROM_EXCEPT_OR_FINALLY() \
|
||||
if(self->is_curr_exc_handled) py_clearexc(NULL)
|
||||
|
||||
#define DISPATCH() \
|
||||
do { \
|
||||
frame->ip++; \
|
||||
@ -787,7 +784,6 @@ __NEXT_STEP:
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_RETURN_VALUE: {
|
||||
CHECK_RETURN_FROM_EXCEPT_OR_FINALLY();
|
||||
if(byte.arg == BC_NOARG) {
|
||||
self->last_retval = POPX();
|
||||
} else {
|
||||
@ -804,7 +800,6 @@ __NEXT_STEP:
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_YIELD_VALUE: {
|
||||
CHECK_RETURN_FROM_EXCEPT_OR_FINALLY();
|
||||
if(byte.arg == 1) {
|
||||
py_newnone(py_retval());
|
||||
} else {
|
||||
@ -814,7 +809,6 @@ __NEXT_STEP:
|
||||
return RES_YIELD;
|
||||
}
|
||||
case OP_FOR_ITER_YIELD_VALUE: {
|
||||
CHECK_RETURN_FROM_EXCEPT_OR_FINALLY();
|
||||
int res = py_next(TOP());
|
||||
if(res == -1) goto __ERROR;
|
||||
if(res) {
|
||||
@ -1135,16 +1129,27 @@ __NEXT_STEP:
|
||||
DISPATCH();
|
||||
}
|
||||
///////////
|
||||
case OP_TRY_ENTER: {
|
||||
Frame__set_unwind_target(frame, SP());
|
||||
case OP_BEGIN_TRY: {
|
||||
Frame__begin_try(frame, SP());
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_END_TRY: {
|
||||
c11_vector__pop(&frame->exc_stack);
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_EXCEPTION_MATCH: {
|
||||
if(!py_checktype(TOP(), tp_type)) goto __ERROR;
|
||||
bool ok = py_isinstance(&self->curr_exception, py_totype(TOP()));
|
||||
bool ok = py_isinstance(&self->unhandled_exc, py_totype(TOP()));
|
||||
py_newbool(TOP(), ok);
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_HANDLE_EXCEPTION: {
|
||||
FrameExcInfo* info = Frame__top_exc_info(frame);
|
||||
assert(info != NULL && py_isnil(&info->exc));
|
||||
info->exc = self->unhandled_exc;
|
||||
py_newnil(&self->unhandled_exc);
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_RAISE: {
|
||||
// [exception]
|
||||
if(py_istype(TOP(), tp_type)) {
|
||||
@ -1169,46 +1174,18 @@ __NEXT_STEP:
|
||||
goto __ERROR;
|
||||
}
|
||||
case OP_RE_RAISE: {
|
||||
if(self->curr_exception.type) {
|
||||
assert(!self->is_curr_exc_handled);
|
||||
goto __ERROR_RE_RAISE;
|
||||
if(py_isnil(&self->unhandled_exc)) {
|
||||
FrameExcInfo* info = Frame__top_exc_info(frame);
|
||||
assert(info != NULL && !py_isnil(&info->exc));
|
||||
self->unhandled_exc = info->exc;
|
||||
}
|
||||
DISPATCH();
|
||||
c11_vector__pop(&frame->exc_stack);
|
||||
goto __ERROR_RE_RAISE;
|
||||
}
|
||||
case OP_PUSH_EXCEPTION: {
|
||||
assert(self->curr_exception.type);
|
||||
PUSH(&self->curr_exception);
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_BEGIN_EXC_HANDLING: {
|
||||
assert(self->curr_exception.type);
|
||||
self->is_curr_exc_handled = true;
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_END_EXC_HANDLING: {
|
||||
assert(self->curr_exception.type);
|
||||
py_clearexc(NULL);
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_BEGIN_FINALLY: {
|
||||
if(self->curr_exception.type) {
|
||||
assert(!self->is_curr_exc_handled);
|
||||
// temporarily handle the exception if any
|
||||
self->is_curr_exc_handled = true;
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_END_FINALLY: {
|
||||
if(byte.arg == BC_NOARG) {
|
||||
if(self->curr_exception.type) {
|
||||
assert(self->is_curr_exc_handled);
|
||||
// revert the exception handling if needed
|
||||
self->is_curr_exc_handled = false;
|
||||
}
|
||||
} else {
|
||||
// break or continue inside finally block
|
||||
py_clearexc(NULL);
|
||||
}
|
||||
FrameExcInfo* info = Frame__top_exc_info(frame);
|
||||
assert(info != NULL && !py_isnil(&info->exc));
|
||||
PUSH(&info->exc);
|
||||
DISPATCH();
|
||||
}
|
||||
//////////////////
|
||||
@ -1224,16 +1201,19 @@ __NEXT_STEP:
|
||||
c11__unreachable();
|
||||
|
||||
__ERROR:
|
||||
assert(!py_isnil(&self->unhandled_exc));
|
||||
py_BaseException__stpush(frame,
|
||||
&self->curr_exception,
|
||||
&self->unhandled_exc,
|
||||
frame->co->src,
|
||||
Frame__lineno(frame),
|
||||
!frame->is_locals_special ? frame->co->name->data : NULL);
|
||||
__ERROR_RE_RAISE:
|
||||
do {
|
||||
self->curr_class = NULL;
|
||||
self->curr_decl_based_function = NULL;
|
||||
} while(0);
|
||||
|
||||
int target = Frame__prepare_jump_exception_handler(frame, &self->stack);
|
||||
int target = Frame__goto_exception_handler(frame, &self->stack, &self->unhandled_exc);
|
||||
if(target >= 0) {
|
||||
// 1. Exception can be handled inside the current frame
|
||||
DISPATCH_JUMP_ABSOLUTE(target);
|
||||
|
@ -7,13 +7,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
void ValueStack__ctor(ValueStack* self) {
|
||||
self->sp = self->begin;
|
||||
self->end = self->begin + PK_VM_STACK_SIZE;
|
||||
}
|
||||
|
||||
void ValueStack__dtor(ValueStack* self) { self->sp = self->begin; }
|
||||
|
||||
void FastLocals__to_dict(py_TValue* locals, const CodeObject* co) {
|
||||
py_StackRef dict = py_pushtmp();
|
||||
py_newdict(dict);
|
||||
@ -38,16 +31,6 @@ NameDict* FastLocals__to_namedict(py_TValue* locals, const CodeObject* co) {
|
||||
return dict;
|
||||
}
|
||||
|
||||
UnwindTarget* UnwindTarget__new(UnwindTarget* next, int iblock, int offset) {
|
||||
UnwindTarget* self = PK_MALLOC(sizeof(UnwindTarget));
|
||||
self->next = next;
|
||||
self->iblock = iblock;
|
||||
self->offset = offset;
|
||||
return self;
|
||||
}
|
||||
|
||||
void UnwindTarget__delete(UnwindTarget* self) { PK_FREE(self); }
|
||||
|
||||
py_Frame* Frame__new(const CodeObject* co,
|
||||
py_StackRef p0,
|
||||
py_GlobalRef module,
|
||||
@ -68,57 +51,47 @@ py_Frame* Frame__new(const CodeObject* co,
|
||||
self->locals = locals;
|
||||
self->is_locals_special = is_locals_special;
|
||||
self->ip = -1;
|
||||
self->uw_list = NULL;
|
||||
c11_vector__ctor(&self->exc_stack, sizeof(FrameExcInfo));
|
||||
return self;
|
||||
}
|
||||
|
||||
void Frame__delete(py_Frame* self) {
|
||||
while(self->uw_list) {
|
||||
UnwindTarget* p = self->uw_list;
|
||||
self->uw_list = p->next;
|
||||
UnwindTarget__delete(p);
|
||||
}
|
||||
c11_vector__dtor(&self->exc_stack);
|
||||
FixedMemoryPool__dealloc(&pk_current_vm->pool_frame, self);
|
||||
}
|
||||
|
||||
int Frame__prepare_jump_exception_handler(py_Frame* self, ValueStack* _s) {
|
||||
// try to find a parent try block
|
||||
int iblock = Frame__iblock(self);
|
||||
while(iblock >= 0) {
|
||||
CodeBlock* block = c11__at(CodeBlock, &self->co->blocks, iblock);
|
||||
if(block->type == CodeBlockType_TRY) break;
|
||||
iblock = block->parent;
|
||||
int Frame__goto_exception_handler(py_Frame* self, ValueStack* value_stack, py_Ref exc) {
|
||||
FrameExcInfo* p = self->exc_stack.data;
|
||||
for(int i = self->exc_stack.length - 1; i >= 0; i--) {
|
||||
if(py_isnil(&p[i].exc)) {
|
||||
value_stack->sp = (self->p0 + p[i].offset); // unwind the stack
|
||||
return c11__at(CodeBlock, &self->co->blocks, p[i].iblock)->end;
|
||||
} else {
|
||||
self->exc_stack.length--;
|
||||
}
|
||||
}
|
||||
if(iblock < 0) return -1;
|
||||
UnwindTarget* uw = Frame__find_unwind_target(self, iblock);
|
||||
_s->sp = (self->p0 + uw->offset); // unwind the stack
|
||||
return c11__at(CodeBlock, &self->co->blocks, iblock)->end;
|
||||
return -1;
|
||||
}
|
||||
|
||||
UnwindTarget* Frame__find_unwind_target(py_Frame* self, int iblock) {
|
||||
UnwindTarget* uw;
|
||||
for(uw = self->uw_list; uw; uw = uw->next) {
|
||||
if(uw->iblock == iblock) return uw;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Frame__set_unwind_target(py_Frame* self, py_TValue* sp) {
|
||||
void Frame__begin_try(py_Frame* self, py_TValue* sp) {
|
||||
int iblock = Frame__iblock(self);
|
||||
assert(iblock >= 0);
|
||||
UnwindTarget* existing = Frame__find_unwind_target(self, iblock);
|
||||
if(existing) {
|
||||
existing->offset = sp - self->p0;
|
||||
} else {
|
||||
UnwindTarget* prev = self->uw_list;
|
||||
self->uw_list = UnwindTarget__new(prev, iblock, sp - self->p0);
|
||||
}
|
||||
FrameExcInfo* info = c11_vector__emplace(&self->exc_stack);
|
||||
info->iblock = iblock;
|
||||
info->offset = (int)(sp - self->p0);
|
||||
py_newnil(&info->exc);
|
||||
}
|
||||
|
||||
FrameExcInfo* Frame__top_exc_info(py_Frame* self) {
|
||||
if(self->exc_stack.length == 0) return NULL;
|
||||
return &c11_vector__back(FrameExcInfo, &self->exc_stack);
|
||||
}
|
||||
|
||||
void Frame__gc_mark(py_Frame* self, c11_vector* p_stack) {
|
||||
pk__mark_value(self->globals);
|
||||
if(self->is_locals_special) pk__mark_value(self->locals);
|
||||
CodeObject__gc_mark(self->co, p_stack);
|
||||
c11__foreach(FrameExcInfo, &self->exc_stack, info) { pk__mark_value(&info->exc); }
|
||||
}
|
||||
|
||||
int Frame__lineno(const py_Frame* self) {
|
||||
|
@ -93,13 +93,11 @@ void VM__ctor(VM* self) {
|
||||
self->callbacks.getchr = pk_default_getchr;
|
||||
|
||||
self->last_retval = *py_NIL();
|
||||
self->curr_exception = *py_NIL();
|
||||
self->unhandled_exc = *py_NIL();
|
||||
|
||||
self->recursion_depth = 0;
|
||||
self->max_recursion_depth = 1000;
|
||||
|
||||
self->is_curr_exc_handled = false;
|
||||
|
||||
self->ctx = NULL;
|
||||
self->curr_class = NULL;
|
||||
self->curr_decl_based_function = NULL;
|
||||
@ -110,7 +108,8 @@ void VM__ctor(VM* self) {
|
||||
FixedMemoryPool__ctor(&self->pool_frame, sizeof(py_Frame), 32);
|
||||
|
||||
ManagedHeap__ctor(&self->heap);
|
||||
ValueStack__ctor(&self->stack);
|
||||
self->stack.sp = self->stack.begin;
|
||||
self->stack.end = self->stack.begin + PK_VM_STACK_SIZE;
|
||||
|
||||
CachedNames__ctor(&self->cached_names);
|
||||
NameDict__ctor(&self->compile_time_funcs, PK_TYPE_ATTR_LOAD_FACTOR);
|
||||
@ -288,11 +287,11 @@ void VM__dtor(VM* self) {
|
||||
// destroy all objects
|
||||
ManagedHeap__dtor(&self->heap);
|
||||
// clear frames
|
||||
while(self->top_frame)
|
||||
while(self->top_frame) {
|
||||
VM__pop_frame(self);
|
||||
}
|
||||
BinTree__dtor(&self->modules);
|
||||
FixedMemoryPool__dtor(&self->pool_frame);
|
||||
ValueStack__dtor(&self->stack);
|
||||
CachedNames__dtor(&self->cached_names);
|
||||
NameDict__dtor(&self->compile_time_funcs);
|
||||
c11_vector__dtor(&self->types);
|
||||
@ -462,6 +461,11 @@ static bool
|
||||
FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall) {
|
||||
#ifndef NDEBUG
|
||||
pk_print_stack(self, self->top_frame, (Bytecode){0});
|
||||
|
||||
if(py_checkexc()) {
|
||||
const char* name = py_tpname(self->unhandled_exc.type);
|
||||
c11__abort("unhandled exception `%s` was set!", name);
|
||||
}
|
||||
#endif
|
||||
|
||||
py_Ref p1 = self->stack.sp - kwargc * 2;
|
||||
@ -666,7 +670,7 @@ void ManagedHeap__mark(ManagedHeap* self) {
|
||||
}
|
||||
// mark vm's registers
|
||||
pk__mark_value(&vm->last_retval);
|
||||
pk__mark_value(&vm->curr_exception);
|
||||
pk__mark_value(&vm->unhandled_exc);
|
||||
for(int i = 0; i < c11__count_array(vm->reg); i++) {
|
||||
pk__mark_value(&vm->reg[i]);
|
||||
}
|
||||
|
@ -57,7 +57,6 @@ static bool disassemble(CodeObject* co) {
|
||||
|
||||
c11_sbuf__write_int(&ss, byte.arg);
|
||||
switch(byte.op) {
|
||||
// TODO: see `dis.py` there is a memory issue
|
||||
case OP_LOAD_CONST: {
|
||||
py_Ref value = c11__at(py_TValue, &co->consts, byte.arg);
|
||||
if(py_repr(value)) {
|
||||
|
@ -1,14 +1,20 @@
|
||||
#include "pocketpy/pocketpy.h"
|
||||
#include "pocketpy/objects/exception.h"
|
||||
#include "pocketpy/interpreter/vm.h"
|
||||
|
||||
static bool traceback_format_exc(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(0);
|
||||
char* s = py_formatexc();
|
||||
if(!s) {
|
||||
py_newnone(py_retval());
|
||||
} else {
|
||||
py_newstr(py_retval(), s);
|
||||
PK_FREE(s);
|
||||
VM* vm = pk_current_vm;
|
||||
if(vm->top_frame) {
|
||||
FrameExcInfo* info = Frame__top_exc_info(vm->top_frame);
|
||||
if(info && !py_isnil(&info->exc)) {
|
||||
char* res = formatexc_internal(&info->exc);
|
||||
py_newstr(py_retval(), res);
|
||||
PK_FREE(res);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
py_newnone(py_retval());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ bool _py_compile(CodeObject* out,
|
||||
Error* err = pk_compile(src, out);
|
||||
if(err) {
|
||||
py_exception(tp_SyntaxError, err->msg);
|
||||
py_BaseException__stpush(NULL, &vm->curr_exception, err->src, err->lineno, NULL);
|
||||
py_BaseException__stpush(NULL, &vm->unhandled_exc, err->src, err->lineno, NULL);
|
||||
PK_DECREF(src);
|
||||
|
||||
PK_DECREF(err->src);
|
||||
|
@ -152,15 +152,17 @@ bool py_call(py_Ref f, int argc, py_Ref argv) {
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) {
|
||||
if(py_checkexc()) {
|
||||
const char* name = py_tpname(pk_current_vm->unhandled_exc.type);
|
||||
c11__abort("unhandled exception `%s` was set!", name);
|
||||
}
|
||||
py_StackRef p0 = py_peek(0);
|
||||
// NOTE: sometimes users are using `py_retval()` to pass `argv`
|
||||
// It will be reset to `nil` and cause an exception
|
||||
py_newnil(py_retval());
|
||||
bool ok = f(argc, argv);
|
||||
if(!ok) {
|
||||
if(!py_checkexc(true)) {
|
||||
c11__abort("py_CFunction returns `false` but no exception is set!");
|
||||
}
|
||||
if(!py_checkexc()) { c11__abort("py_CFunction returns `false` but no exception is set!"); }
|
||||
return false;
|
||||
}
|
||||
if(py_peek(0) != p0) {
|
||||
@ -170,8 +172,8 @@ 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(true)) {
|
||||
const char* name = py_tpname(pk_current_vm->curr_exception.type);
|
||||
if(py_checkexc()) {
|
||||
const char* name = py_tpname(pk_current_vm->unhandled_exc.type);
|
||||
c11__abort("py_CFunction returns `true`, but `%s` was set!", name);
|
||||
}
|
||||
return true;
|
||||
@ -258,7 +260,7 @@ bool pk_loadmethod(py_StackRef self, py_Name name) {
|
||||
self[0] = *py_getslot(cls_var, 0);
|
||||
self[1] = ti->self;
|
||||
break;
|
||||
default: c11__unreachable();
|
||||
default: return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -142,32 +142,22 @@ py_Type pk_StopIteration__register() {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
bool py_checkexc(bool ignore_handled) {
|
||||
bool py_checkexc() {
|
||||
VM* vm = pk_current_vm;
|
||||
if(ignore_handled && vm->is_curr_exc_handled) return false;
|
||||
return !py_isnil(&vm->curr_exception);
|
||||
return !py_isnil(&vm->unhandled_exc);
|
||||
}
|
||||
|
||||
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;
|
||||
bool ok = py_issubclass(vm->curr_exception.type, type);
|
||||
if(ok) {
|
||||
// if match, then the exception is handled
|
||||
vm->is_curr_exc_handled = true;
|
||||
vm->last_retval = vm->curr_exception;
|
||||
}
|
||||
if(py_isnil(&vm->unhandled_exc)) return false;
|
||||
bool ok = py_issubclass(vm->unhandled_exc.type, type);
|
||||
if(ok) vm->last_retval = vm->unhandled_exc;
|
||||
return ok;
|
||||
}
|
||||
|
||||
void py_clearexc(py_StackRef p0) {
|
||||
VM* vm = pk_current_vm;
|
||||
vm->curr_exception = *py_NIL();
|
||||
vm->is_curr_exc_handled = false;
|
||||
/* Don't clear this, because StopIteration() may corrupt the class definition */
|
||||
// vm->curr_class = NULL;
|
||||
vm->curr_decl_based_function = NULL;
|
||||
py_newnil(&vm->unhandled_exc);
|
||||
if(p0) vm->stack.sp = p0;
|
||||
}
|
||||
|
||||
@ -187,17 +177,37 @@ 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 || !py_isstr(py_retval())) {
|
||||
message = "<exception str() failed>";
|
||||
} else {
|
||||
message = py_tostr(py_retval());
|
||||
}
|
||||
char* message = safe_stringify_exception(exc);
|
||||
|
||||
c11_sbuf__write_cstr(self, name);
|
||||
c11_sbuf__write_cstr(self, ": ");
|
||||
c11_sbuf__write_cstr(self, message);
|
||||
|
||||
PK_FREE(message);
|
||||
}
|
||||
|
||||
char* safe_stringify_exception(py_Ref exc) {
|
||||
VM* vm = pk_current_vm;
|
||||
|
||||
const char* message = "<exception str() failed>";
|
||||
|
||||
py_Ref tmp = py_pushtmp();
|
||||
py_Ref old_unhandled_exc = py_pushtmp();
|
||||
*tmp = *exc;
|
||||
*old_unhandled_exc = vm->unhandled_exc;
|
||||
py_newnil(&vm->unhandled_exc);
|
||||
|
||||
py_StackRef p0 = py_peek(0);
|
||||
bool ok = py_str(tmp);
|
||||
if(ok) {
|
||||
if(py_isstr(py_retval())) message = py_tostr(py_retval());
|
||||
} else {
|
||||
py_clearexc(p0);
|
||||
}
|
||||
|
||||
vm->unhandled_exc = *old_unhandled_exc;
|
||||
py_shrink(2);
|
||||
return c11_strdup(message);
|
||||
}
|
||||
|
||||
void py_printexc() {
|
||||
@ -210,24 +220,29 @@ void py_printexc() {
|
||||
|
||||
char* py_formatexc() {
|
||||
VM* vm = pk_current_vm;
|
||||
if(py_isnil(&vm->curr_exception)) return NULL;
|
||||
if(py_isnil(&vm->unhandled_exc)) return NULL;
|
||||
char* res = formatexc_internal(&vm->unhandled_exc);
|
||||
if(py_debugger_isattached()) py_debugger_exceptionbreakpoint(&vm->unhandled_exc);
|
||||
return res;
|
||||
}
|
||||
|
||||
// when you call `py_formatexc()`, you are handling the exception
|
||||
vm->is_curr_exc_handled = true;
|
||||
char* formatexc_internal(py_Ref exc) {
|
||||
c11__rtassert(exc != NULL);
|
||||
c11__rtassert(py_issubclass(exc->type, tp_BaseException));
|
||||
|
||||
c11_sbuf ss;
|
||||
c11_sbuf__ctor(&ss);
|
||||
|
||||
BaseException* ud = py_touserdata(&vm->curr_exception);
|
||||
BaseException* ud = py_touserdata(exc);
|
||||
py_Ref inner = &ud->inner_exc;
|
||||
if(py_isnil(inner)) {
|
||||
c11_sbuf__write_exc(&ss, &vm->curr_exception);
|
||||
c11_sbuf__write_exc(&ss, exc);
|
||||
} 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_sbuf__write_exc(&ss, exc);
|
||||
}
|
||||
|
||||
c11_string* res = c11_sbuf__submit(&ss);
|
||||
@ -235,15 +250,13 @@ char* py_formatexc() {
|
||||
memcpy(dup, res->data, res->size);
|
||||
dup[res->size] = '\0';
|
||||
c11_string__delete(res);
|
||||
|
||||
if(py_debugger_isattached()) py_debugger_exceptionbreakpoint(&vm->curr_exception);
|
||||
return dup;
|
||||
}
|
||||
|
||||
bool py_exception(py_Type type, const char* fmt, ...) {
|
||||
#ifndef NDEBUG
|
||||
if(py_checkexc(true)) {
|
||||
const char* name = py_tpname(pk_current_vm->curr_exception.type);
|
||||
if(py_checkexc()) {
|
||||
const char* name = py_tpname(pk_current_vm->unhandled_exc.type);
|
||||
c11__abort("py_exception(): `%s` was already set!", name);
|
||||
}
|
||||
#endif
|
||||
@ -268,12 +281,15 @@ 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)) {
|
||||
BaseException* ud = py_touserdata(&vm->curr_exception);
|
||||
ud->inner_exc = vm->curr_exception;
|
||||
if(vm->top_frame) {
|
||||
FrameExcInfo* info = Frame__top_exc_info(vm->top_frame);
|
||||
if(info && !py_isnil(&info->exc)) {
|
||||
BaseException* ud = py_touserdata(exc);
|
||||
ud->inner_exc = info->exc;
|
||||
}
|
||||
}
|
||||
vm->curr_exception = *exc;
|
||||
vm->is_curr_exc_handled = false;
|
||||
assert(py_isnil(&vm->unhandled_exc));
|
||||
vm->unhandled_exc = *exc;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -111,8 +111,8 @@ int py_next(py_Ref val) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(vm->curr_exception.type == tp_StopIteration) {
|
||||
vm->last_retval = vm->curr_exception;
|
||||
if(vm->unhandled_exc.type == tp_StopIteration) {
|
||||
vm->last_retval = vm->unhandled_exc;
|
||||
py_clearexc(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
int code = py_checkexc(false) ? 1 : 0;
|
||||
int code = py_checkexc() ? 1 : 0;
|
||||
py_finalize();
|
||||
|
||||
if(debug) py_debugger_exit(code);
|
||||
|
@ -146,7 +146,62 @@ except SyntaxError as e:
|
||||
ok = True
|
||||
assert ok
|
||||
|
||||
# nested try
|
||||
def g():
|
||||
try:
|
||||
raise KeyError
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
raise IndexError
|
||||
except IndexError:
|
||||
g()
|
||||
|
||||
a = []
|
||||
for i in range(10):
|
||||
a.append(i)
|
||||
try:
|
||||
try:
|
||||
if i % 2 == 0:
|
||||
raise KeyError(i)
|
||||
else:
|
||||
raise IndexError(i)
|
||||
except KeyError as e:
|
||||
assert i % 2 == 0
|
||||
assert e.args[0] == i
|
||||
raise
|
||||
except IndexError as e:
|
||||
assert i % 2 == 1
|
||||
assert e.args[0] == i
|
||||
raise
|
||||
except Exception as e:
|
||||
assert e.args[0] == i
|
||||
assert a == list(range(10))
|
||||
|
||||
# inner exc
|
||||
x = 0
|
||||
try:
|
||||
try:
|
||||
[][1]
|
||||
except:
|
||||
raise KeyError
|
||||
except KeyError:
|
||||
x = 5
|
||||
assert x == 5
|
||||
|
||||
a = []
|
||||
for i in range(6):
|
||||
try:
|
||||
[][1]
|
||||
except IndexError:
|
||||
if i == 2:
|
||||
continue
|
||||
else:
|
||||
a.append(i)
|
||||
assert a == [0, 1, 3, 4, 5]
|
||||
|
||||
"""
|
||||
# finally, only
|
||||
def finally_only():
|
||||
try:
|
||||
@ -224,5 +279,4 @@ def finally_return():
|
||||
return 1
|
||||
|
||||
assert finally_return() == 1
|
||||
|
||||
|
||||
"""
|
||||
|
@ -169,4 +169,14 @@ for x in xs:
|
||||
xs.append(x+1)
|
||||
|
||||
assert res == list(range(101))
|
||||
assert xs == res
|
||||
assert xs == res
|
||||
|
||||
# call property
|
||||
from vmath import vec2
|
||||
|
||||
a = vec2(1, 2)
|
||||
try:
|
||||
x = a.x()
|
||||
exit(1)
|
||||
except TypeError:
|
||||
pass
|
Loading…
x
Reference in New Issue
Block a user