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
97f18a99f2
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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, ' ');
|
||||
|
@ -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);
|
||||
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
46
tests/71_gc_bug.py
Normal 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
|
Loading…
x
Reference in New Issue
Block a user