Merge branch 'pocketpy:main' into gsoc-2025-debugger

This commit is contained in:
lightovernight 2025-08-26 16:32:15 +08:00 committed by GitHub
commit 97f18a99f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 129 additions and 42 deletions

View File

@ -12,6 +12,7 @@
#define BC_NOARG 0 #define BC_NOARG 0
#define BC_KEEPLINE -1 #define BC_KEEPLINE -1
#define BC_RETURN_VIRTUAL 5
typedef enum FuncType { typedef enum FuncType {
FuncType_UNSET, FuncType_UNSET,
@ -62,7 +63,6 @@ typedef struct CodeBlock {
typedef struct BytecodeEx { typedef struct BytecodeEx {
int lineno; // line number for each bytecode int lineno; // line number for each bytecode
bool is_virtual; // whether this bytecode is virtual (not in source code)
int iblock; // block index int iblock; // block index
} BytecodeEx; } BytecodeEx;

View File

@ -9,3 +9,13 @@ y = 2
costly_func(2) # 3s costly_func(2) # 3s
time.sleep(1) # 1s time.sleep(1) # 1s
def new_func(a, b, c):
x = a + b
x = x + c
return a
def new_func2(a, b, c):
x = a + b
x = x + c
return a

View File

@ -76,7 +76,6 @@ static int Ctx__prepare_loop_divert(Ctx* self, int line, bool is_break);
static int Ctx__enter_block(Ctx* self, CodeBlockType type); static int Ctx__enter_block(Ctx* self, CodeBlockType type);
static void Ctx__exit_block(Ctx* self); static void Ctx__exit_block(Ctx* self);
static int Ctx__emit_(Ctx* self, Opcode opcode, uint16_t arg, int line); static int Ctx__emit_(Ctx* self, Opcode opcode, uint16_t arg, int line);
static int Ctx__emit_virtual(Ctx* self, Opcode opcode, uint16_t arg, int line, bool virtual);
// static void Ctx__revert_last_emit_(Ctx* self); // static void Ctx__revert_last_emit_(Ctx* self);
static int Ctx__emit_int(Ctx* self, int64_t value, int line); static int Ctx__emit_int(Ctx* self, int64_t value, int line);
static void Ctx__patch_jump(Ctx* self, int index); static void Ctx__patch_jump(Ctx* self, int index);
@ -1177,9 +1176,9 @@ static void Ctx__s_emit_decorators(Ctx* self, int count) {
} }
} }
static int Ctx__emit_virtual(Ctx* self, Opcode opcode, uint16_t arg, int line, bool is_virtual) { static int Ctx__emit_(Ctx* self, Opcode opcode, uint16_t arg, int line) {
Bytecode bc = {(uint8_t)opcode, arg}; Bytecode bc = {(uint8_t)opcode, arg};
BytecodeEx bcx = {line, is_virtual, self->curr_iblock}; BytecodeEx bcx = {line, self->curr_iblock};
c11_vector__push(Bytecode, &self->co->codes, bc); c11_vector__push(Bytecode, &self->co->codes, bc);
c11_vector__push(BytecodeEx, &self->co->codes_ex, bcx); c11_vector__push(BytecodeEx, &self->co->codes_ex, bcx);
int i = self->co->codes.length - 1; int i = self->co->codes.length - 1;
@ -1188,10 +1187,6 @@ static int Ctx__emit_virtual(Ctx* self, Opcode opcode, uint16_t arg, int line, b
return i; return i;
} }
static int Ctx__emit_(Ctx* self, Opcode opcode, uint16_t arg, int line) {
return Ctx__emit_virtual(self, opcode, arg, line, false);
}
// static void Ctx__revert_last_emit_(Ctx* self) { // static void Ctx__revert_last_emit_(Ctx* self) {
// c11_vector__pop(&self->co->codes); // c11_vector__pop(&self->co->codes);
// c11_vector__pop(&self->co->codes_ex); // c11_vector__pop(&self->co->codes_ex);
@ -1512,7 +1507,7 @@ static Error* pop_context(Compiler* self) {
// previously, we only do this if the last opcode is not a return // previously, we only do this if the last opcode is not a return
// however, this is buggy...since there may be a jump to the end (out of bound) even if the last // however, this is buggy...since there may be a jump to the end (out of bound) even if the last
// opcode is a return // opcode is a return
Ctx__emit_virtual(ctx(), OP_RETURN_VALUE, 1, BC_KEEPLINE, true); Ctx__emit_(ctx(), OP_RETURN_VALUE, BC_RETURN_VIRTUAL, BC_KEEPLINE);
CodeObject* co = ctx()->co; CodeObject* co = ctx()->co;
// find the last valid token // find the last valid token
@ -2421,6 +2416,7 @@ static Error* consume_pep695_py312(Compiler* self) {
static Error* compile_function(Compiler* self, int decorators) { static Error* compile_function(Compiler* self, int decorators) {
Error* err; Error* err;
int def_line = prev()->line;
consume(TK_ID); consume(TK_ID);
c11_sv decl_name_sv = Token__sv(prev()); c11_sv decl_name_sv = Token__sv(prev());
int decl_index; int decl_index;
@ -2450,7 +2446,7 @@ static Error* compile_function(Compiler* self, int decorators) {
} }
} }
Ctx__emit_(ctx(), OP_LOAD_FUNCTION, decl_index, prev()->line); Ctx__emit_(ctx(), OP_LOAD_FUNCTION, decl_index, def_line);
Ctx__s_emit_decorators(ctx(), decorators); Ctx__s_emit_decorators(ctx(), decorators);
py_Name decl_name = py_namev(decl_name_sv); py_Name decl_name = py_namev(decl_name_sv);
@ -2463,9 +2459,9 @@ static Error* compile_function(Compiler* self, int decorators) {
} }
} }
Ctx__emit_(ctx(), OP_STORE_CLASS_ATTR, Ctx__add_name(ctx(), decl_name), prev()->line); Ctx__emit_(ctx(), OP_STORE_CLASS_ATTR, Ctx__add_name(ctx(), decl_name), def_line);
} else { } else {
NameExpr* e = NameExpr__new(prev()->line, decl_name, name_scope(self)); NameExpr* e = NameExpr__new(def_line, decl_name, name_scope(self));
vtemit_store((Expr*)e, ctx()); vtemit_store((Expr*)e, ctx());
vtdelete((Expr*)e); vtdelete((Expr*)e);
} }

View File

@ -4,6 +4,7 @@
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#include "pocketpy/common/sstream.h" #include "pocketpy/common/sstream.h"
#include "pocketpy/objects/codeobject.h" #include "pocketpy/objects/codeobject.h"
#include "pocketpy/objects/exception.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/objects/error.h" #include "pocketpy/objects/error.h"
#include <stdbool.h> #include <stdbool.h>
@ -105,13 +106,16 @@ __NEXT_STEP:
byte = co_codes[frame->ip]; byte = co_codes[frame->ip];
if(self->trace_info.func) { if(self->trace_info.func) {
SourceLocation loc = Frame__source_location(frame); bool is_virtual = byte.op == OP_RETURN_VALUE && byte.arg == BC_RETURN_VIRTUAL;
SourceLocation prev_loc = self->trace_info.prev_loc; if(!is_virtual) {
if(loc.lineno != prev_loc.lineno || loc.src != prev_loc.src) { SourceLocation loc = Frame__source_location(frame);
if(prev_loc.src) PK_DECREF(prev_loc.src); SourceLocation prev_loc = self->trace_info.prev_loc;
PK_INCREF(loc.src); if(loc.lineno != prev_loc.lineno || loc.src != prev_loc.src) {
self->trace_info.prev_loc = loc; if(prev_loc.src) PK_DECREF(prev_loc.src);
self->trace_info.func(frame, TRACE_EVENT_LINE); PK_INCREF(loc.src);
self->trace_info.prev_loc = loc;
self->trace_info.func(frame, TRACE_EVENT_LINE);
}
} }
} }
@ -817,7 +821,8 @@ __NEXT_STEP:
return RES_YIELD; return RES_YIELD;
} else { } else {
assert(self->last_retval.type == tp_StopIteration); assert(self->last_retval.type == tp_StopIteration);
py_ObjectRef value = py_getslot(&self->last_retval, 0); BaseException* ud = py_touserdata(py_retval());
py_ObjectRef value = &ud->args;
if(py_isnil(value)) value = py_None(); if(py_isnil(value)) value = py_None();
*TOP() = *value; // [iter] -> [retval] *TOP() = *value; // [iter] -> [retval]
DISPATCH_JUMP((int16_t)byte.arg); DISPATCH_JUMP((int16_t)byte.arg);
@ -1219,14 +1224,15 @@ __NEXT_STEP:
c11__unreachable(); c11__unreachable();
__ERROR: __ERROR:
py_BaseException__stpush(frame, &self->curr_exception, py_BaseException__stpush(frame,
&self->curr_exception,
frame->co->src, frame->co->src,
Frame__lineno(frame), Frame__lineno(frame),
!frame->is_locals_special ? frame->co->name->data : NULL); !frame->is_locals_special ? frame->co->name->data : NULL);
__ERROR_RE_RAISE: __ERROR_RE_RAISE:
do { do {
} while(0); } while(0);
int target = Frame__prepare_jump_exception_handler(frame, &self->stack); int target = Frame__prepare_jump_exception_handler(frame, &self->stack);
if(target >= 0) { if(target >= 0) {
// 1. Exception can be handled inside the current frame // 1. Exception can be handled inside the current frame
@ -1306,7 +1312,10 @@ bool pk_stack_binaryop(VM* self, py_Name op, py_Name rop) {
py_Type lhs_t = rop ? TOP()->type : SECOND()->type; py_Type lhs_t = rop ? TOP()->type : SECOND()->type;
py_Type rhs_t = rop ? SECOND()->type : TOP()->type; py_Type rhs_t = rop ? SECOND()->type : TOP()->type;
return TypeError("unsupported operand type(s) for '%s': '%t' and '%t'", pk_op2str(op), lhs_t, rhs_t); return TypeError("unsupported operand type(s) for '%s': '%t' and '%t'",
pk_op2str(op),
lhs_t,
rhs_t);
} }
bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) { bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) {

View File

@ -635,7 +635,8 @@ void ManagedHeap__mark(ManagedHeap* self) {
assert(p_stack->length == 0); assert(p_stack->length == 0);
// mark value stack // mark value stack
for(py_TValue* p = vm->stack.begin; p != vm->stack.end; p++) { for(py_TValue* p = vm->stack.begin; p < vm->stack.sp; p++) {
// assert(p->type != tp_nil);
pk__mark_value(p); pk__mark_value(p);
} }
// mark modules // mark modules

View File

@ -3,7 +3,7 @@
void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte) { void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte) {
return; return;
if(frame == NULL || py_isnil(self->main)) return; if(frame == NULL || !self->main || py_isnil(self->main)) return;
py_TValue* sp = self->stack.sp; py_TValue* sp = self->stack.sp;
c11_sbuf buf; c11_sbuf buf;

View File

@ -44,7 +44,7 @@ static bool disassemble(CodeObject* co) {
c11_sbuf__write_cstr(&ss, buf); c11_sbuf__write_cstr(&ss, buf);
c11_sbuf__write_cstr(&ss, pk_opname(byte.op)); c11_sbuf__write_cstr(&ss, pk_opname(byte.op));
c11_sbuf__write_char(&ss, ex.is_virtual ? '*' : ' '); c11_sbuf__write_char(&ss, ' ');
int padding = 24 - strlen(pk_opname(byte.op)); int padding = 24 - strlen(pk_opname(byte.op));
for(int j = 0; j < padding; j++) for(int j = 0; j < padding; j++)
c11_sbuf__write_char(&ss, ' '); c11_sbuf__write_char(&ss, ' ');

View File

@ -270,9 +270,9 @@ static bool Random_choices(int argc, py_Ref argv) {
} }
py_f64 total = cum_weights[length - 1]; py_f64 total = cum_weights[length - 1];
if(total <= 0) { if(total <= 1e-6) {
PK_FREE(cum_weights); PK_FREE(cum_weights);
return ValueError("total of weights must be greater than zero"); return ValueError("total of weights must be greater than 1e-6");
} }
py_newlistn(py_retval(), k); py_newlistn(py_retval(), k);

View File

@ -5,27 +5,29 @@
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#define HASH_KEY(__k) ((uintptr_t)(__k) >> 3U)
#define HASH_PROBE_0(__k, ok, i) \ #define HASH_PROBE_0(__k, ok, i) \
ok = false; \ ok = false; \
i = (uintptr_t)(__k)&self->mask; \ i = HASH_KEY(__k) & self->mask; \
do { \ do { \
if(self->items[i].key == (__k)) { \ if(self->items[i].key == (__k)) { \
ok = true; \ ok = true; \
break; \ break; \
} \ } \
if(self->items[i].key == NULL) break; \ if(self->items[i].key == NULL) break; \
i = (5 * i + 1) & self->mask; \ i = (i + 1) & self->mask; \
} while(true); } while(true);
#define HASH_PROBE_1(__k, ok, i) \ #define HASH_PROBE_1(__k, ok, i) \
ok = false; \ ok = false; \
i = (uintptr_t)(__k)&self->mask; \ i = HASH_KEY(__k) & self->mask; \
while(self->items[i].key != NULL) { \ while(self->items[i].key != NULL) { \
if(self->items[i].key == (__k)) { \ if(self->items[i].key == (__k)) { \
ok = true; \ ok = true; \
break; \ break; \
} \ } \
i = (5 * i + 1) & self->mask; \ i = (i + 1) & self->mask; \
} }
static void NameDict__set_capacity_and_alloc_items(NameDict* self, int val) { static void NameDict__set_capacity_and_alloc_items(NameDict* self, int val) {
@ -111,12 +113,12 @@ bool NameDict__del(NameDict* self, py_Name key) {
self->items[i].value = *py_NIL(); self->items[i].value = *py_NIL();
self->length--; self->length--;
/* tidy */ /* tidy */
uint32_t posToRemove = i; uintptr_t posToRemove = i;
uint32_t posToShift = posToRemove; uintptr_t posToShift = posToRemove;
while(true) { while(true) {
posToShift = (5 * posToShift + 1) & self->mask; posToShift = (posToShift + 1) & self->mask;
if(self->items[posToShift].key == NULL) break; if(self->items[posToShift].key == NULL) break;
uintptr_t hash_z = (uintptr_t)self->items[posToShift].key; uintptr_t hash_z = HASH_KEY(self->items[posToShift].key);
uintptr_t insertPos = hash_z & self->mask; uintptr_t insertPos = hash_z & self->mask;
bool cond1 = insertPos <= posToRemove; bool cond1 = insertPos <= posToRemove;
bool cond2 = posToRemove <= posToShift; bool cond2 = posToRemove <= posToShift;
@ -124,6 +126,7 @@ bool NameDict__del(NameDict* self, py_Name key) {
// chain wrapped around capacity // chain wrapped around capacity
(posToShift < insertPos && (cond1 || cond2))) { (posToShift < insertPos && (cond1 || cond2))) {
NameDict_KV tmp = self->items[posToRemove]; NameDict_KV tmp = self->items[posToRemove];
assert(tmp.key == NULL);
self->items[posToRemove] = self->items[posToShift]; self->items[posToRemove] = self->items[posToShift];
self->items[posToShift] = tmp; self->items[posToShift] = tmp;
posToRemove = posToShift; posToRemove = posToShift;
@ -141,4 +144,5 @@ void NameDict__clear(NameDict* self) {
} }
#undef HASH_PROBE_0 #undef HASH_PROBE_0
#undef HASH_PROBE_1 #undef HASH_PROBE_1
#undef HASH_KEY

View File

@ -20,9 +20,14 @@ void py_BaseException__stpush(py_Frame* frame,
frame_dump->lineno = lineno; frame_dump->lineno = lineno;
frame_dump->name = func_name ? c11_string__new(func_name) : NULL; frame_dump->name = func_name ? c11_string__new(func_name) : NULL;
if(py_debugger_isattached() && frame != NULL) { if(py_debugger_isattached()) {
py_Frame_newlocals(frame, &frame_dump->locals); if(frame != NULL) {
py_Frame_newglobals(frame, &frame_dump->globals); py_Frame_newlocals(frame, &frame_dump->locals);
py_Frame_newglobals(frame, &frame_dump->globals);
} else {
py_newdict(&frame_dump->locals);
py_newdict(&frame_dump->globals);
}
} }
} }

View File

@ -47,7 +47,10 @@ void py_newellipsis(py_OutRef out) {
out->is_ptr = false; out->is_ptr = false;
} }
void py_newnil(py_OutRef out) { out->type = 0; } void py_newnil(py_OutRef out) {
out->type = tp_nil;
out->is_ptr = false;
}
void py_newnativefunc(py_OutRef out, py_CFunction f) { void py_newnativefunc(py_OutRef out, py_CFunction f) {
out->type = tp_nativefunc; out->type = tp_nativefunc;

View File

@ -165,4 +165,17 @@ for i in range(len(data)):
b.append(z) b.append(z)
if i % 3 == 0: if i % 3 == 0:
y = b.pop() y = b.pop()
delattr(a, y) delattr(a, y)
# bug test
d = {
'__name__': '__main__',
'__package__': '',
'__path__': '__main__',
'a': [],
'gc': 1,
}
del d['a']
assert 'a' not in d
assert d['gc'] == 1

46
tests/71_gc_bug.py Normal file
View File

@ -0,0 +1,46 @@
# a=[]
# import gc
# gc.collect()
# # a.append(a)
# print(globals().items())
# del a
# print(list(globals().items()))
# print(globals()['gc'])
# gc.collect()
d = object()
d.__name__ = '__main__'
d.__package__ = ''
d.__path__ = '__main__'
d.a = []
d.gc = 1
assert d.gc == 1
del d.a
assert not hasattr(d, 'a')
assert d.gc == 1
# [0, 1, 6, 7, 4, 5, 2, 3]
# 0 __name__ [0]
# 1 __package__ [1]
# 2 nil
# 3 nil
# 4 gc [4]
# 5 nil
# 6 __path__ [2]
# 7 a [3]
import gc
gc.collect()
a = []
del a
assert gc.collect() == 1
a = []
a.append(a)
del a
assert gc.collect() == 1