Ver Fonte

add `breakpoint`

blueloveTH há 1 ano atrás
pai
commit
b6d62c9d89

+ 2 - 0
include/pocketpy/frame.h

@@ -117,6 +117,8 @@ struct Frame {
     int _exit_block(ValueStack*, int);
     void jump_abs_break(ValueStack*, int);
 
+    int curr_lineno() const { return co->lines[_ip].lineno; }
+
     void _gc_mark() const {
         PK_OBJ_MARK(_module);
         co->_gc_mark();

+ 1 - 1
include/pocketpy/lexer.h

@@ -26,7 +26,7 @@ constexpr const char* kTokens[] = {
     /** KW_BEGIN **/
     "class", "import", "as", "def", "lambda", "pass", "del", "from", "with", "yield",
     "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>;

+ 0 - 2
include/pocketpy/opcodes.h

@@ -165,6 +165,4 @@ OPCODE(INC_FAST)
 OPCODE(DEC_FAST)
 OPCODE(INC_GLOBAL)
 OPCODE(DEC_GLOBAL)
-/**************************/
-OPCODE(BREAKPOINT)
 #endif

+ 11 - 0
include/pocketpy/vm.h

@@ -29,6 +29,16 @@ namespace pkpy{
 
 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{
     PyObject* obj;      // never be garbage collected
     Type base;
@@ -131,6 +141,7 @@ public:
     void (*_ceval_on_step)(VM*, Frame*, Bytecode bc) = nullptr;
 
     LineProfiler* _profiler = nullptr;
+    NextBreakpoint _next_breakpoint;
 
     PrintFunc _stdout;
     PrintFunc _stderr;

+ 5 - 7
src/ceval.cpp

@@ -73,8 +73,9 @@ PyObject* VM::_run_top_frame(){
 {
 
 #define CEVAL_STEP_CALLBACK() \
-    if(_ceval_on_step) _ceval_on_step(this, frame, byte); \
-    if(_profiler) _profiler->_step(callstack._tail);
+    if(_ceval_on_step) _ceval_on_step(this, frame, byte);   \
+    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; }
 __NEXT_FRAME:
@@ -907,12 +908,9 @@ __NEXT_STEP:;
         if(p == nullptr) vm->NameError(_name);
         *p = VAR(CAST(i64, *p) - 1);
     } DISPATCH();
-    TARGET(BREAKPOINT) {
-        vm->_breakpoint();
-    } DISPATCH();
     /*****************************************/
-        static_assert(OP_BREAKPOINT == 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:
+        static_assert(OP_DEC_GLOBAL == 136);
+        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 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:

+ 0 - 6
src/compiler.cpp

@@ -873,12 +873,6 @@ __EAT_DOTS_END:
             case TK("@"): compile_decorated(); break;
             case TK("try"): compile_try_except(); 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("++"):{
                 consume(TK("@id"));

+ 9 - 0
src/pocketpy.cpp

@@ -72,6 +72,15 @@ void init_builtins(VM* _vm) {
 #undef BIND_NUM_LOGICAL_OPT
 
     // 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) {
         PyObject* class_arg = nullptr;
         PyObject* self_arg = nullptr;

+ 118 - 19
src/vm.cpp

@@ -1353,33 +1353,132 @@ PyObject* NativeFunc::call(VM *vm, ArgsView args) const {
     return f(vm, args);
 }
 
-void VM::_breakpoint(){
-    SStream ss;
-    Frame* frame = vm->top_frame();
-    int lineno = frame->co->lines[frame->_next_ip].lineno;
-    auto [_0, _1] = frame->co->src->_get_line(lineno);
-    ss << "> " << frame->co->src->filename << '(' << lineno << ')';
-    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';
+void NextBreakpoint::_step(VM* vm, LinkedFrame* lf){
+    Frame* frame = &lf->frame;
+    int lineno = frame->co->lines[frame->_ip].lineno;
+    if(should_step_into){
+        if(frame != this->frame || lineno != this->lineno){
+            vm->_breakpoint();
+        }
     }else{
-        ss << "-> <no source code available>\n";
+        if(frame == this->frame){
+            if(lineno != this->lineno) vm->_breakpoint();
+        }else{
+            if(&lf->f_back->frame != this->frame) vm->_breakpoint();
+        }
     }
+}
 
-    vm->stdout_write(ss.str());
-    std::string line;
+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) ");
-        if(!std::getline(std::cin, line)) break;
-        if(line == "h" || line == "help") continue;
+
+        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") break;
+        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

+ 8 - 2
tests/95_pdb.py

@@ -5,9 +5,15 @@ print(a, b)
 
 
 def f(a, b):
-    breakpoint()
     b = a + b
     print(a, b)
     return b
 
-f(1, 2)
+def g(a, b):
+    breakpoint()
+    c = f(a, b)
+    return c
+
+g(1, 2)
+f(3, 4)
+