From 648c1bedb4ce6980d17167f2f88b40997585ebbc Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Thu, 28 Aug 2025 19:34:53 +0800 Subject: [PATCH 01/11] [skip ci] add a bad case --- tests/28_exception.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/28_exception.py b/tests/28_exception.py index fa1ae92b..e37f6244 100644 --- a/tests/28_exception.py +++ b/tests/28_exception.py @@ -226,3 +226,15 @@ def finally_return(): assert finally_return() == 1 +# nested try +def g(): + try: + raise KeyError + except KeyError: + pass + +try: + raise IndexError +except IndexError: + g() + From 410a3c5dd42d577b9c31713ed0af673f29decfc2 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 29 Aug 2025 19:38:49 +0800 Subject: [PATCH 02/11] test new sweep algo --- include/pocketpy/interpreter/objectpool.h | 19 ++++- src/interpreter/objectpool.c | 85 +++++++++++++++++------ tests/28_exception.py | 9 +-- 3 files changed, 86 insertions(+), 27 deletions(-) diff --git a/include/pocketpy/interpreter/objectpool.h b/include/pocketpy/interpreter/objectpool.h index 85911161..c6ebb759 100644 --- a/include/pocketpy/interpreter/objectpool.h +++ b/include/pocketpy/interpreter/objectpool.h @@ -7,17 +7,32 @@ #define kMultiPoolCount 5 #define kPoolMaxBlockSize (32 * kMultiPoolCount) +typedef uint16_t PoolBlockIndex; + +typedef struct UsedBlockListNode { + PoolBlockIndex next; + PoolBlockIndex data; +} UsedBlockListNode; + +typedef struct UsedBlockList { + UsedBlockListNode* nodes; + int head_idx; +} UsedBlockList; + +void UsedBlockList__ctor(UsedBlockList* self, int capacity); +void UsedBlockList__dtor(UsedBlockList* self); + typedef struct PoolArena { int block_size; int block_count; int unused_length; + PoolBlockIndex* unused; + UsedBlockList used_blocks; union { char data[kPoolArenaSize]; int64_t _align64; }; - - int unused[]; } PoolArena; typedef struct Pool { diff --git a/src/interpreter/objectpool.c b/src/interpreter/objectpool.c index 08a18fae..26a41b4c 100644 --- a/src/interpreter/objectpool.c +++ b/src/interpreter/objectpool.c @@ -7,16 +7,52 @@ #include #include +void UsedBlockList__ctor(UsedBlockList* self, int capacity) { + capacity += 2; + self->nodes = PK_MALLOC(sizeof(UsedBlockListNode) * capacity); + self->head_idx = capacity - 1; + for(int i = 0; i < capacity - 1; i++) { + self->nodes[i].next = (PoolBlockIndex)(i + 1); + self->nodes[i].data = (PoolBlockIndex)-1; + } + self->nodes[self->head_idx - 1].next = (PoolBlockIndex)-1; + self->nodes[self->head_idx].next = (PoolBlockIndex)-1; + self->nodes[self->head_idx].data = (PoolBlockIndex)-1; +} + +void UsedBlockList__dtor(UsedBlockList* self) { PK_FREE(self->nodes); } + +void UsedBlockList__insert(UsedBlockList* self, PoolBlockIndex data) { + PoolBlockIndex idx = self->nodes[0].next; + assert(idx != (PoolBlockIndex)-1); + self->nodes[0].next = self->nodes[idx].next; + self->nodes[idx].data = data; + self->nodes[idx].next = self->nodes[self->head_idx].next; + self->nodes[self->head_idx].next = idx; +} + +void UsedBlockList__remove(UsedBlockList* self, PoolBlockIndex prev_idx, PoolBlockIndex idx) { + self->nodes[prev_idx].next = self->nodes[idx].next; + self->nodes[idx].next = self->nodes[0].next; +#ifndef NDEBUG + self->nodes[idx].data = (PoolBlockIndex)-1; +#endif + self->nodes[0].next = idx; +} + static PoolArena* PoolArena__new(int block_size) { assert(kPoolArenaSize % block_size == 0); int block_count = kPoolArenaSize / block_size; - PoolArena* self = PK_MALLOC(sizeof(PoolArena) + sizeof(int) * block_count); + assert(block_count < (PoolBlockIndex)-1); + PoolArena* self = PK_MALLOC(sizeof(PoolArena)); self->block_size = block_size; self->block_count = block_count; self->unused_length = block_count; - for(int i = 0; i < block_count; i++) { + self->unused = PK_MALLOC(sizeof(PoolBlockIndex) * block_count); + for(PoolBlockIndex i = 0; i < block_count; i++) { self->unused[i] = i; } + UsedBlockList__ctor(&self->used_blocks, block_count); memset(self->data, 0, kPoolArenaSize); return self; } @@ -26,37 +62,44 @@ static void PoolArena__delete(PoolArena* self) { PyObject* obj = (PyObject*)(self->data + i * self->block_size); if(obj->type != 0) PyObject__dtor(obj); } + PK_FREE(self->unused); + UsedBlockList__dtor(&self->used_blocks); PK_FREE(self); } static void* PoolArena__alloc(PoolArena* self) { assert(self->unused_length > 0); - int index = self->unused[self->unused_length - 1]; + PoolBlockIndex index = self->unused[self->unused_length - 1]; self->unused_length--; + UsedBlockList__insert(&self->used_blocks, index); return self->data + index * self->block_size; } static int PoolArena__sweep_dealloc(PoolArena* self) { int freed = 0; - self->unused_length = 0; - for(int i = 0; i < self->block_count; i++) { - PyObject* obj = (PyObject*)(self->data + i * self->block_size); - if(obj->type == 0) { - // free slot - self->unused[self->unused_length] = i; - self->unused_length++; + UsedBlockListNode* nodes = self->used_blocks.nodes; + PoolBlockIndex prev_idx = self->used_blocks.head_idx; + PoolBlockIndex idx = nodes[prev_idx].next; + while(idx != (PoolBlockIndex)-1) { + PoolBlockIndex data_idx = nodes[idx].data; + assert(data_idx != (PoolBlockIndex)-1); + PyObject* obj = (PyObject*)(self->data + data_idx * self->block_size); + assert(obj->type != 0); + if(obj->gc_marked) { + obj->gc_marked = false; + prev_idx = idx; + idx = nodes[idx].next; } else { - if(!obj->gc_marked) { - // not marked, need to free - PyObject__dtor(obj); - obj->type = 0; - freed++; - self->unused[self->unused_length] = i; - self->unused_length++; - } else { - // marked, clear mark - obj->gc_marked = false; - } + PyObject__dtor(obj); + obj->type = 0; + freed++; + // add into unused + self->unused[self->unused_length] = data_idx; + self->unused_length++; + // remove from used_blocks + PoolBlockIndex next_idx = nodes[idx].next; + UsedBlockList__remove(&self->used_blocks, prev_idx, idx); + idx = next_idx; } } return freed; diff --git a/tests/28_exception.py b/tests/28_exception.py index e37f6244..adeabe21 100644 --- a/tests/28_exception.py +++ b/tests/28_exception.py @@ -233,8 +233,9 @@ def g(): except KeyError: pass -try: - raise IndexError -except IndexError: - g() +if 0: + try: + raise IndexError + except IndexError: + g() From 5f265e5ec8f453e3d5f250916e4490c97cd48e2e Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 29 Aug 2025 23:43:21 +0800 Subject: [PATCH 03/11] Revert "test new sweep algo" This reverts commit 410a3c5dd42d577b9c31713ed0af673f29decfc2. --- include/pocketpy/interpreter/objectpool.h | 19 +---- src/interpreter/objectpool.c | 85 ++++++----------------- tests/28_exception.py | 9 ++- 3 files changed, 27 insertions(+), 86 deletions(-) diff --git a/include/pocketpy/interpreter/objectpool.h b/include/pocketpy/interpreter/objectpool.h index c6ebb759..85911161 100644 --- a/include/pocketpy/interpreter/objectpool.h +++ b/include/pocketpy/interpreter/objectpool.h @@ -7,32 +7,17 @@ #define kMultiPoolCount 5 #define kPoolMaxBlockSize (32 * kMultiPoolCount) -typedef uint16_t PoolBlockIndex; - -typedef struct UsedBlockListNode { - PoolBlockIndex next; - PoolBlockIndex data; -} UsedBlockListNode; - -typedef struct UsedBlockList { - UsedBlockListNode* nodes; - int head_idx; -} UsedBlockList; - -void UsedBlockList__ctor(UsedBlockList* self, int capacity); -void UsedBlockList__dtor(UsedBlockList* self); - typedef struct PoolArena { int block_size; int block_count; int unused_length; - PoolBlockIndex* unused; - UsedBlockList used_blocks; union { char data[kPoolArenaSize]; int64_t _align64; }; + + int unused[]; } PoolArena; typedef struct Pool { diff --git a/src/interpreter/objectpool.c b/src/interpreter/objectpool.c index 26a41b4c..08a18fae 100644 --- a/src/interpreter/objectpool.c +++ b/src/interpreter/objectpool.c @@ -7,52 +7,16 @@ #include #include -void UsedBlockList__ctor(UsedBlockList* self, int capacity) { - capacity += 2; - self->nodes = PK_MALLOC(sizeof(UsedBlockListNode) * capacity); - self->head_idx = capacity - 1; - for(int i = 0; i < capacity - 1; i++) { - self->nodes[i].next = (PoolBlockIndex)(i + 1); - self->nodes[i].data = (PoolBlockIndex)-1; - } - self->nodes[self->head_idx - 1].next = (PoolBlockIndex)-1; - self->nodes[self->head_idx].next = (PoolBlockIndex)-1; - self->nodes[self->head_idx].data = (PoolBlockIndex)-1; -} - -void UsedBlockList__dtor(UsedBlockList* self) { PK_FREE(self->nodes); } - -void UsedBlockList__insert(UsedBlockList* self, PoolBlockIndex data) { - PoolBlockIndex idx = self->nodes[0].next; - assert(idx != (PoolBlockIndex)-1); - self->nodes[0].next = self->nodes[idx].next; - self->nodes[idx].data = data; - self->nodes[idx].next = self->nodes[self->head_idx].next; - self->nodes[self->head_idx].next = idx; -} - -void UsedBlockList__remove(UsedBlockList* self, PoolBlockIndex prev_idx, PoolBlockIndex idx) { - self->nodes[prev_idx].next = self->nodes[idx].next; - self->nodes[idx].next = self->nodes[0].next; -#ifndef NDEBUG - self->nodes[idx].data = (PoolBlockIndex)-1; -#endif - self->nodes[0].next = idx; -} - static PoolArena* PoolArena__new(int block_size) { assert(kPoolArenaSize % block_size == 0); int block_count = kPoolArenaSize / block_size; - assert(block_count < (PoolBlockIndex)-1); - PoolArena* self = PK_MALLOC(sizeof(PoolArena)); + PoolArena* self = PK_MALLOC(sizeof(PoolArena) + sizeof(int) * block_count); self->block_size = block_size; self->block_count = block_count; self->unused_length = block_count; - self->unused = PK_MALLOC(sizeof(PoolBlockIndex) * block_count); - for(PoolBlockIndex i = 0; i < block_count; i++) { + for(int i = 0; i < block_count; i++) { self->unused[i] = i; } - UsedBlockList__ctor(&self->used_blocks, block_count); memset(self->data, 0, kPoolArenaSize); return self; } @@ -62,44 +26,37 @@ static void PoolArena__delete(PoolArena* self) { PyObject* obj = (PyObject*)(self->data + i * self->block_size); if(obj->type != 0) PyObject__dtor(obj); } - PK_FREE(self->unused); - UsedBlockList__dtor(&self->used_blocks); PK_FREE(self); } static void* PoolArena__alloc(PoolArena* self) { assert(self->unused_length > 0); - PoolBlockIndex index = self->unused[self->unused_length - 1]; + int index = self->unused[self->unused_length - 1]; self->unused_length--; - UsedBlockList__insert(&self->used_blocks, index); return self->data + index * self->block_size; } static int PoolArena__sweep_dealloc(PoolArena* self) { int freed = 0; - UsedBlockListNode* nodes = self->used_blocks.nodes; - PoolBlockIndex prev_idx = self->used_blocks.head_idx; - PoolBlockIndex idx = nodes[prev_idx].next; - while(idx != (PoolBlockIndex)-1) { - PoolBlockIndex data_idx = nodes[idx].data; - assert(data_idx != (PoolBlockIndex)-1); - PyObject* obj = (PyObject*)(self->data + data_idx * self->block_size); - assert(obj->type != 0); - if(obj->gc_marked) { - obj->gc_marked = false; - prev_idx = idx; - idx = nodes[idx].next; - } else { - PyObject__dtor(obj); - obj->type = 0; - freed++; - // add into unused - self->unused[self->unused_length] = data_idx; + self->unused_length = 0; + for(int i = 0; i < self->block_count; i++) { + PyObject* obj = (PyObject*)(self->data + i * self->block_size); + if(obj->type == 0) { + // free slot + self->unused[self->unused_length] = i; self->unused_length++; - // remove from used_blocks - PoolBlockIndex next_idx = nodes[idx].next; - UsedBlockList__remove(&self->used_blocks, prev_idx, idx); - idx = next_idx; + } else { + if(!obj->gc_marked) { + // not marked, need to free + PyObject__dtor(obj); + obj->type = 0; + freed++; + self->unused[self->unused_length] = i; + self->unused_length++; + } else { + // marked, clear mark + obj->gc_marked = false; + } } } return freed; diff --git a/tests/28_exception.py b/tests/28_exception.py index adeabe21..e37f6244 100644 --- a/tests/28_exception.py +++ b/tests/28_exception.py @@ -233,9 +233,8 @@ def g(): except KeyError: pass -if 0: - try: - raise IndexError - except IndexError: - g() +try: + raise IndexError +except IndexError: + g() From b57b16efaf5ab852927649606068e72bcaed8e75 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 29 Aug 2025 23:47:51 +0800 Subject: [PATCH 04/11] Update 28_exception.py --- tests/28_exception.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/28_exception.py b/tests/28_exception.py index e37f6244..adeabe21 100644 --- a/tests/28_exception.py +++ b/tests/28_exception.py @@ -233,8 +233,9 @@ def g(): except KeyError: pass -try: - raise IndexError -except IndexError: - g() +if 0: + try: + raise IndexError + except IndexError: + g() From 1d16d1a6f77e22d462d8efeb50b938111ea27483 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sat, 30 Aug 2025 21:34:37 +0800 Subject: [PATCH 05/11] disable `finally` cuz it is buggy --- README.md | 2 +- docs/features/basic.md | 2 +- include/pocketpy/objects/codeobject.h | 1 - include/pocketpy/xmacros/opcodes.h | 2 -- src/compiler/compiler.c | 34 +++++++-------------------- src/interpreter/ceval.c | 21 ----------------- tests/28_exception.py | 29 +++++++++++------------ 7 files changed, 24 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 71aad9cc..f433a131 100644 --- a/README.md +++ b/README.md @@ -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` | ✅ | diff --git a/docs/features/basic.md b/docs/features/basic.md index 0ece257a..138936da 100644 --- a/docs/features/basic.md +++ b/docs/features/basic.md @@ -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` | ✅ | diff --git a/include/pocketpy/objects/codeobject.h b/include/pocketpy/objects/codeobject.h index 88af8a55..4d61e98f 100644 --- a/include/pocketpy/objects/codeobject.h +++ b/include/pocketpy/objects/codeobject.h @@ -35,7 +35,6 @@ typedef enum CodeBlockType { CodeBlockType_WITH, /* context blocks (flag-based) */ CodeBlockType_EXCEPT, - CodeBlockType_FINALLY, } CodeBlockType; typedef enum Opcode { diff --git a/include/pocketpy/xmacros/opcodes.h b/include/pocketpy/xmacros/opcodes.h index 99f3e448..f4cf9ac4 100644 --- a/include/pocketpy/xmacros/opcodes.h +++ b/include/pocketpy/xmacros/opcodes.h @@ -124,8 +124,6 @@ OPCODE(RE_RAISE) OPCODE(PUSH_EXCEPTION) OPCODE(BEGIN_EXC_HANDLING) OPCODE(END_EXC_HANDLING) -OPCODE(BEGIN_FINALLY) -OPCODE(END_FINALLY) /**************************/ OPCODE(FORMAT_STRING) /**************************/ diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index d1c3b9d0..1cd5106a 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -1136,10 +1136,6 @@ static int Ctx__prepare_loop_divert(Ctx* self, int line, bool is_break) { Ctx__emit_(self, OP_END_EXC_HANDLING, 1, line); break; } - case CodeBlockType_FINALLY: { - Ctx__emit_(self, OP_END_FINALLY, 1, line); - break; - } default: break; } index = block->parent; @@ -2000,7 +1996,7 @@ static Error* exprSlice0(Compiler* self) { check(EXPR(self)); slice->step = Ctx__s_popx(ctx()); } // else :: - } // else : + } // else : return NULL; } @@ -2646,21 +2642,11 @@ static Error* compile_try_except(Compiler* self) { bool has_finally = curr()->type == TK_FINALLY; if(!has_finally) { patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); + } else { + return SyntaxError(self, "finally clause is not supported yet"); } - Ctx__exit_block(ctx()); - 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; - } + Ctx__exit_block(ctx()); do { if(patches_length == 8) { @@ -2701,16 +2687,12 @@ static Error* compile_try_except(Compiler* self) { // ... // 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); } + + if(match(TK_FINALLY)) { return SyntaxError(self, "finally clause is not supported yet"); } + // re-raise if needed Ctx__emit_(ctx(), OP_RE_RAISE, BC_NOARG, BC_KEEPLINE); return NULL; diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 4bcac41d..b0734801 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -1190,27 +1190,6 @@ __NEXT_STEP: 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); - } - DISPATCH(); - } ////////////////// case OP_FORMAT_STRING: { py_Ref spec = c11__at(py_TValue, &frame->co->consts, byte.arg); diff --git a/tests/28_exception.py b/tests/28_exception.py index adeabe21..5fdd20f8 100644 --- a/tests/28_exception.py +++ b/tests/28_exception.py @@ -146,7 +146,20 @@ except SyntaxError as e: ok = True assert ok +# nested try +def g(): + try: + raise KeyError + except KeyError: + pass +if 0: + try: + raise IndexError + except IndexError: + g() + +""" # finally, only def finally_only(): try: @@ -224,18 +237,4 @@ def finally_return(): return 1 assert finally_return() == 1 - - -# nested try -def g(): - try: - raise KeyError - except KeyError: - pass - -if 0: - try: - raise IndexError - except IndexError: - g() - +""" From 33be8e869ad09f6acd62605686df01a3c946079a Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sat, 30 Aug 2025 21:44:33 +0800 Subject: [PATCH 06/11] some refactor --- include/pocketpy/interpreter/frame.h | 5 +---- src/interpreter/frame.c | 6 ------ src/interpreter/vm.c | 7 ++++--- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index 8b0fcf22..15577be4 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -11,12 +11,9 @@ 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; diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index 2c7eccb5..6a70054c 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -7,12 +7,6 @@ #include #include -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(); diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index eb5109a6..bc9f5979 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -110,7 +110,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 +289,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); From c658b5a7a434702aea9f358b56899ddaf018f1ba Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 31 Aug 2025 00:09:55 +0800 Subject: [PATCH 07/11] backup --- include/pocketpy/config.h | 4 +- include/pocketpy/interpreter/frame.h | 22 +++---- include/pocketpy/interpreter/vm.h | 4 +- include/pocketpy/objects/exception.h | 2 + include/pocketpy/pocketpy.h | 19 +++--- include/pocketpy/xmacros/opcodes.h | 6 +- src/compiler/compiler.c | 45 +++++++++----- src/debugger/core.c | 16 ++--- src/interpreter/ceval.c | 55 +++++++++-------- src/interpreter/frame.c | 63 ++++++------------- src/interpreter/vm.c | 11 ++-- src/modules/dis.c | 1 - src/modules/traceback.c | 18 ++++-- src/public/exec.c | 2 +- src/public/internal.c | 12 ++-- src/public/py_exception.c | 92 ++++++++++++++++------------ src/public/py_ops.c | 4 +- src2/main.c | 2 +- tests/28_exception.py | 11 ++-- 19 files changed, 194 insertions(+), 195 deletions(-) diff --git a/include/pocketpy/config.h b/include/pocketpy/config.h index 53109114..8d2e09b0 100644 --- a/include/pocketpy/config.h +++ b/include/pocketpy/config.h @@ -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 diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index 15577be4..36163ee7 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -14,14 +14,11 @@ typedef struct ValueStack { py_TValue begin[PK_VM_STACK_SIZE + PK_MAX_CO_VARNAMES]; } ValueStack; -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; @@ -32,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 { @@ -58,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); \ No newline at end of file diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index d216e612..8cde83d4 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -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 diff --git a/include/pocketpy/objects/exception.h b/include/pocketpy/objects/exception.h index d6cb7cc3..140f989a 100644 --- a/include/pocketpy/objects/exception.h +++ b/include/pocketpy/objects/exception.h @@ -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); \ No newline at end of file diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index d9741ae5..ef57ef0d 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -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 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); diff --git a/include/pocketpy/xmacros/opcodes.h b/include/pocketpy/xmacros/opcodes.h index f4cf9ac4..3253cfd7 100644 --- a/include/pocketpy/xmacros/opcodes.h +++ b/include/pocketpy/xmacros/opcodes.h @@ -116,14 +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(FORMAT_STRING) /**************************/ diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 1cd5106a..4dd0db17 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -1132,8 +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_TRY: { + Ctx__emit_(self, OP_END_TRY, BC_NOARG, line); + break; + } case CodeBlockType_EXCEPT: { - Ctx__emit_(self, OP_END_EXC_HANDLING, 1, line); + Ctx__emit_(self, OP_END_TRY, BC_NOARG, line); break; } default: break; @@ -1915,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; @@ -2623,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, @@ -2640,12 +2647,9 @@ 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); - } else { - return SyntaxError(self, "finally clause is not supported yet"); - } + if(has_finally) return SyntaxError(self, "finally clause is not supported yet"); + patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); Ctx__exit_block(ctx()); do { @@ -2670,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); @@ -2678,23 +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++) { Ctx__patch_jump(ctx(), patches[i]); } - if(match(TK_FINALLY)) { return SyntaxError(self, "finally clause is not supported yet"); } - - // 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; } @@ -2797,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: { diff --git a/src/debugger/core.c b/src/debugger/core.c index 8d48b63c..56432400 100644 --- a/src/debugger/core.c +++ b/src/debugger/core.c @@ -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 = ""; - } 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 diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index b0734801..38e55d3d 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -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,25 +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); + FrameExcInfo* info = Frame__top_exc_info(frame); + assert(info != NULL && !py_isnil(&info->exc)); + PUSH(&info->exc); DISPATCH(); } ////////////////// @@ -1203,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); diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index 6a70054c..a3bdb367 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -7,7 +7,6 @@ #include #include - void FastLocals__to_dict(py_TValue* locals, const CodeObject* co) { py_StackRef dict = py_pushtmp(); py_newdict(dict); @@ -32,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, @@ -62,57 +51,41 @@ 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; - } - 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; +int Frame__goto_exception_handler(py_Frame* self, ValueStack* value_stack, py_Ref exc) { + if(self->exc_stack.length == 0) return -1; + FrameExcInfo* info = &c11_vector__back(FrameExcInfo, &self->exc_stack); + value_stack->sp = (self->p0 + info->offset); // unwind the stack + return c11__at(CodeBlock, &self->co->blocks, info->iblock)->end; } -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) { diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index bc9f5979..0b25a015 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -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; @@ -463,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; @@ -667,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]); } diff --git a/src/modules/dis.c b/src/modules/dis.c index 213c4805..175ae2db 100644 --- a/src/modules/dis.c +++ b/src/modules/dis.c @@ -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)) { diff --git a/src/modules/traceback.c b/src/modules/traceback.c index 5fec7afb..0c37c28d 100644 --- a/src/modules/traceback.c +++ b/src/modules/traceback.c @@ -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; } diff --git a/src/public/exec.c b/src/public/exec.c index 0df4537d..6a7f9a2a 100644 --- a/src/public/exec.c +++ b/src/public/exec.c @@ -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); diff --git a/src/public/internal.c b/src/public/internal.c index 82a5a2b2..832e30e7 100644 --- a/src/public/internal.c +++ b/src/public/internal.c @@ -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; diff --git a/src/public/py_exception.c b/src/public/py_exception.c index 4fb0d117..3b97eac8 100644 --- a/src/public/py_exception.c +++ b/src/public/py_exception.c @@ -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 = ""; - } 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 = ""; + + 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; } diff --git a/src/public/py_ops.c b/src/public/py_ops.c index 8a3fcaf5..16e63a83 100644 --- a/src/public/py_ops.c +++ b/src/public/py_ops.c @@ -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; } diff --git a/src2/main.c b/src2/main.c index 3477c4c8..2b9b9256 100644 --- a/src2/main.c +++ b/src2/main.c @@ -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); diff --git a/tests/28_exception.py b/tests/28_exception.py index 5fdd20f8..ba0f21ab 100644 --- a/tests/28_exception.py +++ b/tests/28_exception.py @@ -153,11 +153,12 @@ def g(): except KeyError: pass -if 0: - try: - raise IndexError - except IndexError: - g() +try: + raise IndexError +except IndexError: + g() + +a = 1 + 2 """ # finally, only From 0b7f63ccaf4420379c26c440eb299031813d0dc2 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 31 Aug 2025 00:13:54 +0800 Subject: [PATCH 08/11] Update 28_exception.py --- tests/28_exception.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/28_exception.py b/tests/28_exception.py index ba0f21ab..6c657593 100644 --- a/tests/28_exception.py +++ b/tests/28_exception.py @@ -158,7 +158,26 @@ try: except IndexError: g() -a = 1 + 2 +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)) """ # finally, only From 4a81e975c883755d53f0b807a97d89120a2f9d74 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 31 Aug 2025 01:28:33 +0800 Subject: [PATCH 09/11] fix a bug --- src/interpreter/frame.c | 14 ++++++++++---- tests/28_exception.py | 11 +++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index a3bdb367..78a93d79 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -61,10 +61,16 @@ void Frame__delete(py_Frame* self) { } int Frame__goto_exception_handler(py_Frame* self, ValueStack* value_stack, py_Ref exc) { - if(self->exc_stack.length == 0) return -1; - FrameExcInfo* info = &c11_vector__back(FrameExcInfo, &self->exc_stack); - value_stack->sp = (self->p0 + info->offset); // unwind the stack - return c11__at(CodeBlock, &self->co->blocks, info->iblock)->end; + 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--; + } + } + return -1; } void Frame__begin_try(py_Frame* self, py_TValue* sp) { diff --git a/tests/28_exception.py b/tests/28_exception.py index 6c657593..9c174312 100644 --- a/tests/28_exception.py +++ b/tests/28_exception.py @@ -179,6 +179,17 @@ for i in range(10): 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 + """ # finally, only def finally_only(): From 6e9dd9516b49f83b01af4e9e82636dd2b28c6516 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 31 Aug 2025 01:52:18 +0800 Subject: [PATCH 10/11] Update 28_exception.py --- tests/28_exception.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/28_exception.py b/tests/28_exception.py index 9c174312..85828d6d 100644 --- a/tests/28_exception.py +++ b/tests/28_exception.py @@ -190,6 +190,17 @@ 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(): From 1ab175006dcbe76cd89a4f941e0b5ea2d98089bd Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 31 Aug 2025 13:35:25 +0800 Subject: [PATCH 11/11] fix a bug of property --- src/public/internal.c | 2 +- tests/95_bugs.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/public/internal.c b/src/public/internal.c index 832e30e7..964800d7 100644 --- a/src/public/internal.c +++ b/src/public/internal.c @@ -260,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; } diff --git a/tests/95_bugs.py b/tests/95_bugs.py index fe9071bd..c4ccae94 100644 --- a/tests/95_bugs.py +++ b/tests/95_bugs.py @@ -169,4 +169,14 @@ for x in xs: xs.append(x+1) assert res == list(range(101)) -assert xs == res \ No newline at end of file +assert xs == res + +# call property +from vmath import vec2 + +a = vec2(1, 2) +try: + x = a.x() + exit(1) +except TypeError: + pass \ No newline at end of file