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

View File

@ -9,3 +9,13 @@ y = 2
costly_func(2) # 3s
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 void Ctx__exit_block(Ctx* self);
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 int Ctx__emit_int(Ctx* self, int64_t value, int line);
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};
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(BytecodeEx, &self->co->codes_ex, bcx);
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;
}
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) {
// c11_vector__pop(&self->co->codes);
// 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
// however, this is buggy...since there may be a jump to the end (out of bound) even if the last
// 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;
// find the last valid token
@ -2421,6 +2416,7 @@ static Error* consume_pep695_py312(Compiler* self) {
static Error* compile_function(Compiler* self, int decorators) {
Error* err;
int def_line = prev()->line;
consume(TK_ID);
c11_sv decl_name_sv = Token__sv(prev());
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);
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 {
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());
vtdelete((Expr*)e);
}

View File

@ -4,6 +4,7 @@
#include "pocketpy/interpreter/vm.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/objects/codeobject.h"
#include "pocketpy/objects/exception.h"
#include "pocketpy/pocketpy.h"
#include "pocketpy/objects/error.h"
#include <stdbool.h>
@ -105,13 +106,16 @@ __NEXT_STEP:
byte = co_codes[frame->ip];
if(self->trace_info.func) {
SourceLocation loc = Frame__source_location(frame);
SourceLocation prev_loc = self->trace_info.prev_loc;
if(loc.lineno != prev_loc.lineno || loc.src != prev_loc.src) {
if(prev_loc.src) PK_DECREF(prev_loc.src);
PK_INCREF(loc.src);
self->trace_info.prev_loc = loc;
self->trace_info.func(frame, TRACE_EVENT_LINE);
bool is_virtual = byte.op == OP_RETURN_VALUE && byte.arg == BC_RETURN_VIRTUAL;
if(!is_virtual) {
SourceLocation loc = Frame__source_location(frame);
SourceLocation prev_loc = self->trace_info.prev_loc;
if(loc.lineno != prev_loc.lineno || loc.src != prev_loc.src) {
if(prev_loc.src) PK_DECREF(prev_loc.src);
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;
} else {
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();
*TOP() = *value; // [iter] -> [retval]
DISPATCH_JUMP((int16_t)byte.arg);
@ -1219,14 +1224,15 @@ __NEXT_STEP:
c11__unreachable();
__ERROR:
py_BaseException__stpush(frame, &self->curr_exception,
py_BaseException__stpush(frame,
&self->curr_exception,
frame->co->src,
Frame__lineno(frame),
!frame->is_locals_special ? frame->co->name->data : NULL);
__ERROR_RE_RAISE:
do {
} while(0);
int target = Frame__prepare_jump_exception_handler(frame, &self->stack);
if(target >= 0) {
// 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 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) {

View File

@ -635,7 +635,8 @@ void ManagedHeap__mark(ManagedHeap* self) {
assert(p_stack->length == 0);
// 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);
}
// mark modules

View File

@ -3,7 +3,7 @@
void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte) {
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;
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, 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));
for(int j = 0; j < padding; j++)
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];
if(total <= 0) {
if(total <= 1e-6) {
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);

View File

@ -5,27 +5,29 @@
#include <string.h>
#include <assert.h>
#define HASH_KEY(__k) ((uintptr_t)(__k) >> 3U)
#define HASH_PROBE_0(__k, ok, i) \
ok = false; \
i = (uintptr_t)(__k)&self->mask; \
i = HASH_KEY(__k) & self->mask; \
do { \
if(self->items[i].key == (__k)) { \
ok = true; \
break; \
} \
if(self->items[i].key == NULL) break; \
i = (5 * i + 1) & self->mask; \
i = (i + 1) & self->mask; \
} while(true);
#define HASH_PROBE_1(__k, ok, i) \
ok = false; \
i = (uintptr_t)(__k)&self->mask; \
i = HASH_KEY(__k) & self->mask; \
while(self->items[i].key != NULL) { \
if(self->items[i].key == (__k)) { \
ok = true; \
break; \
} \
i = (5 * i + 1) & self->mask; \
i = (i + 1) & self->mask; \
}
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->length--;
/* tidy */
uint32_t posToRemove = i;
uint32_t posToShift = posToRemove;
uintptr_t posToRemove = i;
uintptr_t posToShift = posToRemove;
while(true) {
posToShift = (5 * posToShift + 1) & self->mask;
posToShift = (posToShift + 1) & self->mask;
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;
bool cond1 = insertPos <= posToRemove;
bool cond2 = posToRemove <= posToShift;
@ -124,6 +126,7 @@ bool NameDict__del(NameDict* self, py_Name key) {
// chain wrapped around capacity
(posToShift < insertPos && (cond1 || cond2))) {
NameDict_KV tmp = self->items[posToRemove];
assert(tmp.key == NULL);
self->items[posToRemove] = self->items[posToShift];
self->items[posToShift] = tmp;
posToRemove = posToShift;
@ -141,4 +144,5 @@ void NameDict__clear(NameDict* self) {
}
#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->name = func_name ? c11_string__new(func_name) : NULL;
if(py_debugger_isattached() && frame != NULL) {
py_Frame_newlocals(frame, &frame_dump->locals);
py_Frame_newglobals(frame, &frame_dump->globals);
if(py_debugger_isattached()) {
if(frame != NULL) {
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;
}
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) {
out->type = tp_nativefunc;

View File

@ -165,4 +165,17 @@ for i in range(len(data)):
b.append(z)
if i % 3 == 0:
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