diff --git a/include/pocketpy/objects/error.h b/include/pocketpy/objects/error.h index f80b1ad4..b11f1fa2 100644 --- a/include/pocketpy/objects/error.h +++ b/include/pocketpy/objects/error.h @@ -9,4 +9,4 @@ typedef struct{ char msg[512]; } Error; -void py_BaseException__stpush(py_Ref, SourceData_ src, int lineno, const char* func_name); +void py_BaseException__stpush(py_Frame* frame, py_Ref, SourceData_ src, int lineno, const char* func_name); diff --git a/include/pocketpy/objects/exception.h b/include/pocketpy/objects/exception.h new file mode 100644 index 00000000..d6cb7cc3 --- /dev/null +++ b/include/pocketpy/objects/exception.h @@ -0,0 +1,19 @@ +#pragma once + +#include "pocketpy/objects/sourcedata.h" +#include "pocketpy/objects/base.h" + +typedef struct BaseExceptionFrame { + SourceData_ src; + int lineno; + c11_string* name; + py_TValue locals; // for debugger only + py_TValue globals; // for debugger only +} BaseExceptionFrame; + +typedef struct BaseException { + py_TValue args; + py_TValue inner_exc; + c11_vector /*T=BaseExceptionFrame*/ stacktrace; +} BaseException; + diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 764bfb50..2f23c660 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -68,7 +68,6 @@ typedef struct py_Frame py_Frame; // An enum for tracing events. enum py_TraceEvent { TRACE_EVENT_LINE, - TRACE_EVENT_EXCEPTION, TRACE_EVENT_PUSH, TRACE_EVENT_POP, }; @@ -710,6 +709,8 @@ PK_API char* py_profiler_report(); /************* DAP *************/ PK_API void py_debugger_waitforattach(const char* hostname, unsigned short port); +PK_API bool py_debugger_isattached(); +PK_API void py_debugger_exceptionbreakpoint(py_Ref exc); PK_API void py_debugger_exit(int exitCode); /************* Unchecked Functions *************/ @@ -820,9 +821,9 @@ enum py_PredefinedType { tp_module, tp_function, tp_nativefunc, - tp_boundmethod, // 2 slots (self, func) - tp_super, // 1 slot + py_Type - tp_BaseException, // 2 slots (arg + inner_exc) + tp_boundmethod, // 2 slots (self, func) + tp_super, // 1 slot + py_Type + tp_BaseException, tp_Exception, tp_bytes, tp_namedict, diff --git a/src/debugger/dap.c b/src/debugger/dap.c index d97270e8..c337de59 100644 --- a/src/debugger/dap.c +++ b/src/debugger/dap.c @@ -3,6 +3,8 @@ #include "pocketpy/common/socket.h" #include "pocketpy/debugger/core.h" #include "pocketpy/objects/base.h" +#include "pocketpy/objects/exception.h" +#include "pocketpy/pocketpy.h" #define DAP_COMMAND_LIST(X) \ X(initialize) \ @@ -59,7 +61,7 @@ void c11_dap_handle_attach(py_Ref arguments, c11_sbuf* buffer) { server.isatttac void c11_dap_handle_launch(py_Ref arguments, c11_sbuf* buffer) { server.isatttach = true; } -void c11_dap_handle_ready(py_Ref arguments, c11_sbuf* buffer) {server.isclientready = true;} +void c11_dap_handle_ready(py_Ref arguments, c11_sbuf* buffer) { server.isclientready = true; } void c11_dap_handle_next(py_Ref arguments, c11_sbuf* buffer) { c11_debugger_set_step_mode(C11_STEP_OVER); @@ -340,7 +342,8 @@ inline static void c11_dap_handle_message() { if(message == NULL) { return; } // printf("[DEBUGGER INFO] read request %s\n", message); const char* response_content = c11_dap_handle_request(message); - // if(response_content != NULL) { printf("[DEBUGGER INFO] send response %s\n", response_content); } + // if(response_content != NULL) { printf("[DEBUGGER INFO] send response %s\n", + // response_content); } c11_sbuf buffer; c11_sbuf__ctor(&buffer); pk_sprintf(&buffer, "Content-Length: %d\r\n\r\n%s", strlen(response_content), response_content); @@ -413,6 +416,15 @@ void py_debugger_waitforattach(const char* hostname, unsigned short port) { py_sys_settrace(c11_dap_tracefunc, true); } -void py_debugger_exit(int exitCode) { - c11_dap_send_exited_event(exitCode); +void py_debugger_exit(int exitCode) { c11_dap_send_exited_event(exitCode); } + +bool py_debugger_isattached() { + return false; // TODO: implement this function +} + +void py_debugger_exceptionbreakpoint(py_Ref exc) { + assert(py_isinstance(exc, tp_BaseException)); + // BaseException* ud = py_touserdata(exc); + // c11_vector* stacktrace = &ud->stacktrace; + // TODO: implement this function } \ No newline at end of file diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 4427de9a..23ce786a 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -1219,7 +1219,7 @@ __NEXT_STEP: c11__unreachable(); __ERROR: - py_BaseException__stpush(&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); diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 70ddc599..192896df 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -7,6 +7,7 @@ #include "pocketpy/objects/base.h" #include "pocketpy/interpreter/types.h" #include "pocketpy/common/_generated.h" +#include "pocketpy/objects/exception.h" #include "pocketpy/pocketpy.h" #include #include @@ -719,6 +720,16 @@ void ManagedHeap__mark(ManagedHeap* self) { function__gc_mark(ud, p_stack); break; } + case tp_BaseException: { + BaseException* self = ud; + pk__mark_value(&self->args); + pk__mark_value(&self->inner_exc); + c11__foreach(BaseExceptionFrame, &self->stacktrace, frame) { + pk__mark_value(&frame->locals); + pk__mark_value(&frame->globals); + } + break; + } case tp_code: { CodeObject* self = ud; CodeObject__gc_mark(self, p_stack); diff --git a/src/public/exec.c b/src/public/exec.c index d1cf96da..0df4537d 100644 --- a/src/public/exec.c +++ b/src/public/exec.c @@ -22,7 +22,7 @@ bool _py_compile(CodeObject* out, Error* err = pk_compile(src, out); if(err) { py_exception(tp_SyntaxError, err->msg); - py_BaseException__stpush(&vm->curr_exception, err->src, err->lineno, NULL); + py_BaseException__stpush(NULL, &vm->curr_exception, err->src, err->lineno, NULL); PK_DECREF(src); PK_DECREF(err->src); diff --git a/src/public/py_exception.c b/src/public/py_exception.c index 4110818b..93238296 100644 --- a/src/public/py_exception.c +++ b/src/public/py_exception.c @@ -1,30 +1,29 @@ -#include "pocketpy/objects/codeobject.h" #include "pocketpy/objects/error.h" #include "pocketpy/pocketpy.h" #include "pocketpy/common/utils.h" -#include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" #include "pocketpy/common/sstream.h" +#include "pocketpy/objects/exception.h" -typedef struct BaseExceptionFrame { - SourceData_ src; - int lineno; - c11_string* name; -} BaseExceptionFrame; - -typedef struct BaseException { - c11_vector /*T=BaseExceptionFrame*/ stacktrace; -} BaseException; - -void py_BaseException__stpush(py_Ref self, SourceData_ src, int lineno, const char* func_name) { +void py_BaseException__stpush(py_Frame* frame, + py_Ref self, + SourceData_ src, + int lineno, + const char* func_name) { BaseException* ud = py_touserdata(self); - if(ud->stacktrace.length >= 7) return; - BaseExceptionFrame* frame = c11_vector__emplace(&ud->stacktrace); + int max_frame_dumps = py_debugger_isattached() ? 31 : 7; + if(ud->stacktrace.length >= max_frame_dumps) return; + BaseExceptionFrame* frame_dump = c11_vector__emplace(&ud->stacktrace); PK_INCREF(src); - frame->src = src; - frame->lineno = lineno; - frame->name = func_name ? c11_string__new(func_name) : NULL; + frame_dump->src = src; + 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); + } } static void BaseException__dtor(void* ud) { @@ -38,28 +37,33 @@ static void BaseException__dtor(void* ud) { static bool _py_BaseException__new__(int argc, py_Ref argv) { py_Type cls = py_totype(argv); - BaseException* ud = py_newobject(py_retval(), cls, 2, sizeof(BaseException)); + BaseException* ud = py_newobject(py_retval(), cls, 0, sizeof(BaseException)); + py_newnil(&ud->args); + py_newnil(&ud->inner_exc); c11_vector__ctor(&ud->stacktrace, sizeof(BaseExceptionFrame)); return true; } static bool _py_BaseException__init__(int argc, py_Ref argv) { + BaseException* ud = py_touserdata(argv); py_newnone(py_retval()); if(argc == 1 + 0) return true; if(argc == 1 + 1) { - py_setslot(py_arg(0), 0, py_arg(1)); + py_assign(&ud->args, &argv[1]); return true; } return TypeError("__init__() takes at most 1 arguments but %d were given", argc - 1); } static bool _py_BaseException__repr__(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + BaseException* ud = py_touserdata(argv); c11_sbuf ss; c11_sbuf__ctor(&ss); pk_sprintf(&ss, "%t(", argv->type); - py_Ref arg = py_getslot(argv, 0); - if(!py_isnil(arg)) { - if(!py_repr(arg)) return false; + py_Ref args = &ud->args; + if(!py_isnil(args)) { + if(!py_repr(args)) return false; c11_sbuf__write_sv(&ss, py_tosv(py_retval())); } c11_sbuf__write_char(&ss, ')'); @@ -68,14 +72,16 @@ static bool _py_BaseException__repr__(int argc, py_Ref argv) { } static bool _py_BaseException__str__(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + BaseException* ud = py_touserdata(argv); c11_sbuf ss; c11_sbuf__ctor(&ss); - py_Ref arg = py_getslot(argv, 0); - if(!py_isnil(arg)) { + py_Ref args = &ud->args; + if(!py_isnil(args)) { if(argv->type == tp_KeyError) { - if(!py_repr(arg)) return false; + if(!py_repr(args)) return false; } else { - if(!py_str(arg)) return false; + if(!py_str(args)) return false; } c11_sbuf__write_sv(&ss, py_tosv(py_retval())); } @@ -84,11 +90,12 @@ static bool _py_BaseException__str__(int argc, py_Ref argv) { } static bool BaseException_args(int argc, py_Ref argv) { + BaseException* ud = py_touserdata(argv); PY_CHECK_ARGC(1); - py_Ref arg = py_getslot(argv, 0); - if(!py_isnil(arg)) { + py_Ref args = &ud->args; + if(!py_isnil(args)) { py_Ref p = py_newtuple(py_retval(), 1); - p[0] = *arg; + p[0] = *args; } else { py_newtuple(py_retval(), 0); } @@ -96,12 +103,13 @@ static bool BaseException_args(int argc, py_Ref argv) { } static bool StopIteration_value(int argc, py_Ref argv) { + BaseException* ud = py_touserdata(argv); PY_CHECK_ARGC(1); - py_Ref arg = py_getslot(argv, 0); - if(py_isnil(arg)) { + py_Ref args = &ud->args; + if(py_isnil(args)) { py_newnone(py_retval()); } else { - py_assign(py_retval(), arg); + py_assign(py_retval(), args); } return true; } @@ -158,14 +166,6 @@ void py_clearexc(py_StackRef p0) { if(p0) vm->stack.sp = p0; } -void py_printexc() { - char* msg = py_formatexc(); - if(!msg) return; - pk_current_vm->callbacks.print(msg); - pk_current_vm->callbacks.print("\n"); - PK_FREE(msg); -} - static void c11_sbuf__write_exc(c11_sbuf* self, py_Ref exc) { if(true) { c11_sbuf__write_cstr(self, "Traceback (most recent call last):\n"); } @@ -195,6 +195,14 @@ static void c11_sbuf__write_exc(c11_sbuf* self, py_Ref exc) { c11_sbuf__write_cstr(self, message); } +void py_printexc() { + char* msg = py_formatexc(); + if(!msg) return; + pk_current_vm->callbacks.print(msg); + pk_current_vm->callbacks.print("\n"); + PK_FREE(msg); +} + char* py_formatexc() { VM* vm = pk_current_vm; if(py_isnil(&vm->curr_exception)) return NULL; @@ -205,7 +213,8 @@ char* py_formatexc() { c11_sbuf ss; c11_sbuf__ctor(&ss); - py_Ref inner = py_getslot(&vm->curr_exception, 1); + BaseException* ud = py_touserdata(&vm->curr_exception); + py_Ref inner = &ud->inner_exc; if(py_isnil(inner)) { c11_sbuf__write_exc(&ss, &vm->curr_exception); } else { @@ -221,6 +230,8 @@ char* py_formatexc() { memcpy(dup, res->data, res->size); dup[res->size] = '\0'; c11_string__delete(res); + + if(py_debugger_isattached()) py_debugger_exceptionbreakpoint(&vm->curr_exception); return dup; } @@ -253,16 +264,11 @@ bool py_raise(py_Ref exc) { assert(py_isinstance(exc, tp_BaseException)); VM* vm = pk_current_vm; if(!py_isnil(&vm->curr_exception)) { - // inner exception - py_setslot(exc, 1, &vm->curr_exception); + BaseException* ud = py_touserdata(&vm->curr_exception); + ud->inner_exc = vm->curr_exception; } vm->curr_exception = *exc; vm->is_curr_exc_handled = false; - - if(vm->trace_info.func && !py_istype(exc, tp_StopIteration)) { - py_Frame* frame = vm->top_frame; - vm->trace_info.func(frame, TRACE_EVENT_EXCEPTION); - } return false; }