mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
add breakpoint
This commit is contained in:
parent
aaa0d55361
commit
b6d62c9d89
@ -117,6 +117,8 @@ struct Frame {
|
|||||||
int _exit_block(ValueStack*, int);
|
int _exit_block(ValueStack*, int);
|
||||||
void jump_abs_break(ValueStack*, int);
|
void jump_abs_break(ValueStack*, int);
|
||||||
|
|
||||||
|
int curr_lineno() const { return co->lines[_ip].lineno; }
|
||||||
|
|
||||||
void _gc_mark() const {
|
void _gc_mark() const {
|
||||||
PK_OBJ_MARK(_module);
|
PK_OBJ_MARK(_module);
|
||||||
co->_gc_mark();
|
co->_gc_mark();
|
||||||
|
@ -26,7 +26,7 @@ constexpr const char* kTokens[] = {
|
|||||||
/** KW_BEGIN **/
|
/** KW_BEGIN **/
|
||||||
"class", "import", "as", "def", "lambda", "pass", "del", "from", "with", "yield",
|
"class", "import", "as", "def", "lambda", "pass", "del", "from", "with", "yield",
|
||||||
"None", "in", "is", "and", "or", "not", "True", "False", "global", "try", "except", "finally",
|
"None", "in", "is", "and", "or", "not", "True", "False", "global", "try", "except", "finally",
|
||||||
"while", "for", "if", "elif", "else", "break", "continue", "return", "assert", "raise", "breakpoint"
|
"while", "for", "if", "elif", "else", "break", "continue", "return", "assert", "raise"
|
||||||
};
|
};
|
||||||
|
|
||||||
using TokenValue = std::variant<std::monostate, i64, f64, Str>;
|
using TokenValue = std::variant<std::monostate, i64, f64, Str>;
|
||||||
|
@ -165,6 +165,4 @@ OPCODE(INC_FAST)
|
|||||||
OPCODE(DEC_FAST)
|
OPCODE(DEC_FAST)
|
||||||
OPCODE(INC_GLOBAL)
|
OPCODE(INC_GLOBAL)
|
||||||
OPCODE(DEC_GLOBAL)
|
OPCODE(DEC_GLOBAL)
|
||||||
/**************************/
|
|
||||||
OPCODE(BREAKPOINT)
|
|
||||||
#endif
|
#endif
|
@ -29,6 +29,16 @@ namespace pkpy{
|
|||||||
|
|
||||||
typedef PyObject* (*BinaryFuncC)(VM*, PyObject*, PyObject*);
|
typedef PyObject* (*BinaryFuncC)(VM*, PyObject*, PyObject*);
|
||||||
|
|
||||||
|
struct NextBreakpoint{
|
||||||
|
Frame* frame;
|
||||||
|
int lineno;
|
||||||
|
bool should_step_into;
|
||||||
|
NextBreakpoint(): frame(nullptr), lineno(-1), should_step_into(false) {}
|
||||||
|
NextBreakpoint(Frame* frame, int lineno, bool should_step_info): frame(frame), lineno(lineno), should_step_into(should_step_info) {}
|
||||||
|
void _step(VM* vm, LinkedFrame* lf);
|
||||||
|
bool empty() const { return frame == nullptr; }
|
||||||
|
};
|
||||||
|
|
||||||
struct PyTypeInfo{
|
struct PyTypeInfo{
|
||||||
PyObject* obj; // never be garbage collected
|
PyObject* obj; // never be garbage collected
|
||||||
Type base;
|
Type base;
|
||||||
@ -131,6 +141,7 @@ public:
|
|||||||
void (*_ceval_on_step)(VM*, Frame*, Bytecode bc) = nullptr;
|
void (*_ceval_on_step)(VM*, Frame*, Bytecode bc) = nullptr;
|
||||||
|
|
||||||
LineProfiler* _profiler = nullptr;
|
LineProfiler* _profiler = nullptr;
|
||||||
|
NextBreakpoint _next_breakpoint;
|
||||||
|
|
||||||
PrintFunc _stdout;
|
PrintFunc _stdout;
|
||||||
PrintFunc _stderr;
|
PrintFunc _stderr;
|
||||||
|
@ -73,8 +73,9 @@ PyObject* VM::_run_top_frame(){
|
|||||||
{
|
{
|
||||||
|
|
||||||
#define CEVAL_STEP_CALLBACK() \
|
#define CEVAL_STEP_CALLBACK() \
|
||||||
if(_ceval_on_step) _ceval_on_step(this, frame, byte); \
|
if(_ceval_on_step) _ceval_on_step(this, frame, byte); \
|
||||||
if(_profiler) _profiler->_step(callstack._tail);
|
if(_profiler) _profiler->_step(callstack._tail); \
|
||||||
|
if(!_next_breakpoint.empty()) { _next_breakpoint._step(this, callstack._tail); }
|
||||||
|
|
||||||
#define DISPATCH_OP_CALL() { frame = top_frame(); goto __NEXT_FRAME; }
|
#define DISPATCH_OP_CALL() { frame = top_frame(); goto __NEXT_FRAME; }
|
||||||
__NEXT_FRAME:
|
__NEXT_FRAME:
|
||||||
@ -907,12 +908,9 @@ __NEXT_STEP:;
|
|||||||
if(p == nullptr) vm->NameError(_name);
|
if(p == nullptr) vm->NameError(_name);
|
||||||
*p = VAR(CAST(i64, *p) - 1);
|
*p = VAR(CAST(i64, *p) - 1);
|
||||||
} DISPATCH();
|
} DISPATCH();
|
||||||
TARGET(BREAKPOINT) {
|
|
||||||
vm->_breakpoint();
|
|
||||||
} DISPATCH();
|
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
static_assert(OP_BREAKPOINT == 137);
|
static_assert(OP_DEC_GLOBAL == 136);
|
||||||
case 138: case 139: case 140: case 141: case 142: case 143: case 144: case 145: case 146: case 147: case 148: case 149:
|
case 137: case 138: case 139: case 140: case 141: case 142: case 143: case 144: case 145: case 146: case 147: case 148: case 149:
|
||||||
case 150: case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: case 160: case 161: case 162: case 163: case 164:
|
case 150: case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: case 160: case 161: case 162: case 163: case 164:
|
||||||
case 165: case 166: case 167: case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175: case 176: case 177: case 178: case 179:
|
case 165: case 166: case 167: case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175: case 176: case 177: case 178: case 179:
|
||||||
case 180: case 181: case 182: case 183: case 184: case 185: case 186: case 187: case 188: case 189: case 190: case 191: case 192: case 193: case 194:
|
case 180: case 181: case 182: case 183: case 184: case 185: case 186: case 187: case 188: case 189: case 190: case 191: case 192: case 193: case 194:
|
||||||
|
@ -873,12 +873,6 @@ __EAT_DOTS_END:
|
|||||||
case TK("@"): compile_decorated(); break;
|
case TK("@"): compile_decorated(); break;
|
||||||
case TK("try"): compile_try_except(); break;
|
case TK("try"): compile_try_except(); break;
|
||||||
case TK("pass"): consume_end_stmt(); break;
|
case TK("pass"): consume_end_stmt(); break;
|
||||||
case TK("breakpoint"):
|
|
||||||
consume(TK("("));
|
|
||||||
consume(TK(")"));
|
|
||||||
consume_end_stmt();
|
|
||||||
ctx()->emit_(OP_BREAKPOINT, BC_NOARG, kw_line);
|
|
||||||
break;
|
|
||||||
/*************************************************/
|
/*************************************************/
|
||||||
case TK("++"):{
|
case TK("++"):{
|
||||||
consume(TK("@id"));
|
consume(TK("@id"));
|
||||||
|
@ -72,6 +72,15 @@ void init_builtins(VM* _vm) {
|
|||||||
#undef BIND_NUM_LOGICAL_OPT
|
#undef BIND_NUM_LOGICAL_OPT
|
||||||
|
|
||||||
// builtin functions
|
// builtin functions
|
||||||
|
_vm->bind_func<0>(_vm->builtins, "breakpoint", [](VM* vm, ArgsView args) {
|
||||||
|
vm->_next_breakpoint = NextBreakpoint(
|
||||||
|
vm->top_frame(),
|
||||||
|
vm->top_frame()->curr_lineno(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
return vm->None;
|
||||||
|
});
|
||||||
|
|
||||||
_vm->bind_func<-1>(_vm->builtins, "super", [](VM* vm, ArgsView args) {
|
_vm->bind_func<-1>(_vm->builtins, "super", [](VM* vm, ArgsView args) {
|
||||||
PyObject* class_arg = nullptr;
|
PyObject* class_arg = nullptr;
|
||||||
PyObject* self_arg = nullptr;
|
PyObject* self_arg = nullptr;
|
||||||
|
149
src/vm.cpp
149
src/vm.cpp
@ -1353,33 +1353,132 @@ PyObject* NativeFunc::call(VM *vm, ArgsView args) const {
|
|||||||
return f(vm, args);
|
return f(vm, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VM::_breakpoint(){
|
void NextBreakpoint::_step(VM* vm, LinkedFrame* lf){
|
||||||
SStream ss;
|
Frame* frame = &lf->frame;
|
||||||
Frame* frame = vm->top_frame();
|
int lineno = frame->co->lines[frame->_ip].lineno;
|
||||||
int lineno = frame->co->lines[frame->_next_ip].lineno;
|
if(should_step_into){
|
||||||
auto [_0, _1] = frame->co->src->_get_line(lineno);
|
if(frame != this->frame || lineno != this->lineno){
|
||||||
ss << "> " << frame->co->src->filename << '(' << lineno << ')';
|
vm->_breakpoint();
|
||||||
if(frame->_callable){
|
}
|
||||||
ss << PK_OBJ_GET(Function, frame->_callable).decl->code->name << "()";
|
|
||||||
}
|
|
||||||
ss << '\n';
|
|
||||||
|
|
||||||
if(_0 && _1){
|
|
||||||
ss << "-> " << std::string_view(_0, _1-_0) << '\n';
|
|
||||||
}else{
|
}else{
|
||||||
ss << "-> <no source code available>\n";
|
if(frame == this->frame){
|
||||||
}
|
if(lineno != this->lineno) vm->_breakpoint();
|
||||||
|
}else{
|
||||||
vm->stdout_write(ss.str());
|
if(&lf->f_back->frame != this->frame) vm->_breakpoint();
|
||||||
std::string line;
|
}
|
||||||
while(true){
|
|
||||||
vm->stdout_write("(Pdb) ");
|
|
||||||
if(!std::getline(std::cin, line)) break;
|
|
||||||
if(line == "h" || line == "help") continue;
|
|
||||||
if(line == "q" || line == "quit") std::exit(0);
|
|
||||||
if(line == "n" || line == "next") break;
|
|
||||||
if(line == "c" || line == "continue") break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VM::_breakpoint(){
|
||||||
|
_next_breakpoint = NextBreakpoint();
|
||||||
|
|
||||||
|
bool show_where = false;
|
||||||
|
bool show_headers = true;
|
||||||
|
|
||||||
|
while(true){
|
||||||
|
std::vector<Frame*> frames;
|
||||||
|
LinkedFrame* lf = callstack._tail;
|
||||||
|
while(lf != nullptr){
|
||||||
|
frames.push_back(&lf->frame);
|
||||||
|
lf = lf->f_back;
|
||||||
|
if(frames.size() >= 4) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(show_headers){
|
||||||
|
for(int i=frames.size()-1; i>=0; i--){
|
||||||
|
if(!show_where && i!=0) continue;
|
||||||
|
|
||||||
|
SStream ss;
|
||||||
|
Frame* frame = frames[i];
|
||||||
|
int lineno = frame->curr_lineno();
|
||||||
|
auto [_0, _1] = frame->co->src->_get_line(lineno);
|
||||||
|
ss << "File \"" << frame->co->src->filename << "\", line " << lineno;
|
||||||
|
if(frame->_callable){
|
||||||
|
ss << ", in ";
|
||||||
|
ss << PK_OBJ_GET(Function, frame->_callable).decl->code->name;
|
||||||
|
}
|
||||||
|
ss << '\n';
|
||||||
|
if(_0 && _1){
|
||||||
|
ss << "-> " << std::string_view(_0, _1-_0) << '\n';
|
||||||
|
}else{
|
||||||
|
ss << "-> <no source code available>\n";
|
||||||
|
}
|
||||||
|
stdout_write(ss.str());
|
||||||
|
}
|
||||||
|
show_headers = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm->stdout_write("(Pdb) ");
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
if(!std::getline(std::cin, line)){
|
||||||
|
stdout_write("--KeyboardInterrupt--\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(line == "h" || line == "help"){
|
||||||
|
stdout_write("h, help: show this help message\n");
|
||||||
|
stdout_write("q, quit: exit the debugger\n");
|
||||||
|
stdout_write("n, next: execute next line\n");
|
||||||
|
stdout_write("s, step: step into\n");
|
||||||
|
stdout_write("w, where: show current stack frame\n");
|
||||||
|
stdout_write("c, continue: continue execution\n");
|
||||||
|
stdout_write("a, args: show local variables\n");
|
||||||
|
stdout_write("p, print <expr>: evaluate expression\n");
|
||||||
|
stdout_write("!: execute statement\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(line == "q" || line == "quit") std::exit(0);
|
||||||
|
if(line == "n" || line == "next"){
|
||||||
|
vm->_next_breakpoint = NextBreakpoint(
|
||||||
|
frames[0],
|
||||||
|
frames[0]->curr_lineno(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(line == "s" || line == "step"){
|
||||||
|
vm->_next_breakpoint = NextBreakpoint(
|
||||||
|
frames[0],
|
||||||
|
frames[0]->curr_lineno(),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(line == "w" || line == "where"){
|
||||||
|
show_where = !show_where;
|
||||||
|
show_headers = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(line == "c" || line == "continue") break;
|
||||||
|
if(line == "a" || line == "args"){
|
||||||
|
int i = 0;
|
||||||
|
for(PyObject* obj: frames[0]->_locals){
|
||||||
|
if(obj == PY_NULL) continue;
|
||||||
|
StrName name = frames[0]->co->varnames[i++];
|
||||||
|
stdout_write(_S(name.sv(), " = ", CAST(Str&, vm->py_repr(obj)), '\n'));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int space = line.find_first_of(' ');
|
||||||
|
if(space != -1){
|
||||||
|
std::string cmd = line.substr(0, space);
|
||||||
|
std::string arg = line.substr(space+1);
|
||||||
|
if(arg.empty()) continue; // ignore empty command
|
||||||
|
if(cmd == "p" || cmd == "print"){
|
||||||
|
CodeObject_ code = compile(arg, "<stdin>", EVAL_MODE, true);
|
||||||
|
PyObject* retval = vm->_exec(code.get(), frames[0]->_module, frames[0]->_callable, frames[0]->_locals);
|
||||||
|
stdout_write(CAST(Str&, vm->py_repr(retval)));
|
||||||
|
stdout_write("\n");
|
||||||
|
}else if(cmd == "!"){
|
||||||
|
CodeObject_ code = compile(arg, "<stdin>", EXEC_MODE, true);
|
||||||
|
vm->_exec(code.get(), frames[0]->_module, frames[0]->_callable, frames[0]->_locals);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stdout_write("\n");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pkpy
|
} // namespace pkpy
|
@ -5,9 +5,15 @@ print(a, b)
|
|||||||
|
|
||||||
|
|
||||||
def f(a, b):
|
def f(a, b):
|
||||||
breakpoint()
|
|
||||||
b = a + b
|
b = a + b
|
||||||
print(a, b)
|
print(a, b)
|
||||||
return b
|
return b
|
||||||
|
|
||||||
f(1, 2)
|
def g(a, b):
|
||||||
|
breakpoint()
|
||||||
|
c = f(a, b)
|
||||||
|
return c
|
||||||
|
|
||||||
|
g(1, 2)
|
||||||
|
f(3, 4)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user