From b3bbe3be58647d13914ec4130803a06bf74ca5c9 Mon Sep 17 00:00:00 2001 From: lightovernight <119399319+lightovernight@users.noreply.github.com> Date: Tue, 19 Aug 2025 20:59:39 +0800 Subject: [PATCH] implement `evaluate` request (#389) * implement `evaluate` request * Update main.c * Update main.c * change port to 6110 * Update main.c * simplify the workdir process and minor optimizations * implement exit event * fix bp not hit because of path format * implement output event * add `ctype.h` to `core.c` --------- Co-authored-by: blueloveTH --- src/debugger/core.c | 22 ++++++++- src/debugger/dap.c | 107 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 104 insertions(+), 25 deletions(-) diff --git a/src/debugger/core.c b/src/debugger/core.c index 021c9a07..2b512978 100644 --- a/src/debugger/core.c +++ b/src/debugger/core.c @@ -1,5 +1,6 @@ #include "pocketpy/interpreter/frame.h" #include "pocketpy/pocketpy.h" +#include #include "pocketpy/debugger/core.h" @@ -109,6 +110,7 @@ void c11_debugger_set_step_mode(C11_STEP_MODE mode) { debugger.keep_suspend = false; } + int c11_debugger_setbreakpoint(const char* filename, int lineno) { c11_debugger_breakpoint breakpoint = {.sourcename = c11_strdup(filename), .lineno = lineno}; c11_vector__push(c11_debugger_breakpoint, &debugger.breakpoints, breakpoint); @@ -134,6 +136,21 @@ int c11_debugger_reset_breakpoints_by_source(const char* sourcesname) { return debugger.breakpoints.length; } +bool c11_debugger_path_equal(const char* path1, const char* path2) { + if (path1 == NULL || path2 == NULL) return false; + while (*path1 && *path2) { + char c1 = (*path1 == '\\') ? '/' : *path1; + char c2 = (*path2 == '\\') ? '/' : *path2; + c1 = (char)tolower((unsigned char)c1); + c2 = (char)tolower((unsigned char)c2); + if (c1 != c2) return false; + path1++; + path2++; + } + return *path1 == *path2; +} + + int c11_debugger_should_pause() { if(debugger.current_event == TRACE_EVENT_POP) return false; bool should_pause = false; @@ -153,7 +170,7 @@ int c11_debugger_should_pause() { } if(debugger.step_mode == C11_STEP_CONTINUE) { c11__foreach(c11_debugger_breakpoint, &debugger.breakpoints, bp) { - if(strcmp(debugger.current_filename, bp->sourcename) == 0 && + if(c11_debugger_path_equal(debugger.current_filename, bp->sourcename) && debugger.current_line == bp->lineno) { should_pause = true; break; @@ -166,6 +183,7 @@ int c11_debugger_should_pause() { int c11_debugger_should_keep_pause(void) { return debugger.keep_suspend; } + inline static c11_sv sv_from_cstr(const char* str) { c11_sv sv = {.data = str, .size = strlen(str)}; return sv; @@ -295,7 +313,7 @@ bool c11_debugger_unfold_var(int var_id, c11_sbuf* buffer) { // 5. dump & write if(!py_json_dumps(dap_obj, 0)) { - printf("dap_obj: %s\n", py_tpname(py_typeof(dap_obj))); + // printf("dap_obj: %s\n", py_tpname(py_typeof(dap_obj))); py_printexc(); return false; } diff --git a/src/debugger/dap.c b/src/debugger/dap.c index c337de59..a09b6c5a 100644 --- a/src/debugger/dap.c +++ b/src/debugger/dap.c @@ -20,7 +20,8 @@ X(variables) \ X(threads) \ X(configurationDone) \ - X(ready) + X(ready) \ + X(evaluate) #define DECLARE_HANDLE_FN(name) void c11_dap_handle_##name(py_Ref arguments, c11_sbuf*); DAP_COMMAND_LIST(DECLARE_HANDLE_FN) @@ -125,13 +126,48 @@ void c11_dap_handle_stackTrace(py_Ref arguments, c11_sbuf* buffer) { c11_sbuf__write_char(buffer, ','); } +void c11_dap_handle_evaluate(py_Ref arguments, c11_sbuf* buffer) { + int res = py_dict_getitem_by_str(arguments, "expression"); + if(res <= 0) { + py_printexc(); + c11__abort("[DEBUGGER ERROR] no expression found in evaluate request"); + } + + // [eval, nil, expression, globals, locals] + // vectorcall would pop the above 5 items + // so we don't need to pop them manually + py_Ref py_eval = py_pushtmp(); + py_pushnil(); + py_Ref expression = py_pushtmp(); + py_assign(expression, py_retval()); + py_assign(py_eval, py_getbuiltin(py_name("eval"))); + py_newglobals(py_pushtmp()); + py_newlocals(py_pushtmp()); + bool ok = py_vectorcall(3, 0); + + char* result = NULL; + c11_sbuf__write_cstr(buffer, "\"body\":"); + if(!ok) { + result = py_formatexc(); + } else { + py_str(py_retval()); + result = c11_strdup(py_tostr(py_retval())); + } + + c11_sv result_sv = {.data = result, .size = strlen(result)}; + pk_sprintf(buffer, "{\"result\":%Q,\"variablesReference\":0}", result_sv); + PK_FREE((void*)result); + c11_sbuf__write_char(buffer, ','); +} + void c11_dap_handle_scopes(py_Ref arguments, c11_sbuf* buffer) { int res = py_dict_getitem_by_str(arguments, "frameId"); if(res <= 0) { if(res == 0) { - printf("[DEBUGGER ERROR] no frameID found\n"); + c11__abort("[DEBUGGER ERROR] no frameID found in scopes request"); } else { py_printexc(); + c11__abort("[DEBUGGER ERROR] an error occurred while parsing request frameId"); } return; } @@ -170,9 +206,9 @@ const char* c11_dap_handle_request(const char* message) { int res = py_dict_getitem_by_str(py_request, "command"); if(res == -1) { py_printexc(); - return NULL; + c11__abort("[DEBUGGER ERROR] an error occurred while parsing request"); } else if(res == 0) { - return "cannot find attribute command"; + c11__abort("[DEBUGGER ERROR] no command found in request"); } py_assign(py_command, py_retval()); const char* command = py_tostr(py_command); @@ -180,14 +216,14 @@ const char* c11_dap_handle_request(const char* message) { res = py_dict_getitem_by_str(py_request, "arguments"); if(res == -1) { py_printexc(); - return NULL; + c11__abort("[DEBUGGER ERROR] an error occurred while parsing request arguments"); } py_assign(py_arguments, py_retval()); res = py_dict_getitem_by_str(py_request, "seq"); if(res == -1) { py_printexc(); - return NULL; + c11__abort("[DEBUGGER ERROR] an error occurred while parsing request sequence number"); } int request_seq = (res == 1) ? py_toint(py_retval()) : 0; @@ -218,25 +254,47 @@ const char* c11_dap_handle_request(const char* message) { } void c11_dap_send_event(const char* event_name, const char* body_json) { - char json[256]; - int json_len = snprintf(json, - sizeof(json), - "{\"seq\":%d,\"type\":\"event\",\"event\":\"%s\",\"body\":%s}", - server.dap_next_seq++, - event_name, - body_json); - + c11_sbuf buffer; char header[64]; + c11_sbuf__ctor(&buffer); + pk_sprintf(&buffer, + "{\"seq\":%d,\"type\":\"event\",\"event\":\"%s\",\"body\":%s}", + server.dap_next_seq++, + event_name, + body_json); + c11_string* json = c11_sbuf__submit(&buffer); + int json_len = json->size; int header_len = snprintf(header, sizeof(header), "Content-Length: %d\r\n\r\n", json_len); - // printf("[DEBUGGER INFO] send event %s\n", json); c11_socket_send(server.toclient, header, header_len); - c11_socket_send(server.toclient, json, json_len); + c11_socket_send(server.toclient, json->data, json_len); + c11_string__delete(json); +} + +void c11_dap_send_output_event(const char* category, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + c11_sbuf output; + c11_sbuf__ctor(&output); + pk_vsprintf(&output, fmt, args); + va_end(args); + + c11_sbuf buffer; + c11_string* output_json = c11_sbuf__submit(&output); + c11_sv sv_output = {.data = output_json->data, .size = output_json->size}; + c11_sbuf__ctor(&buffer); + pk_sprintf(&buffer, "{\"category\":\"%s\",\"output\":%Q}", category, sv_output); + c11_string* body_json = c11_sbuf__submit(&buffer); + c11_dap_send_event("output", body_json->data); + c11_string__delete(body_json); + c11_string__delete(output_json); } void c11_dap_send_stop_event() { c11_dap_send_event("stopped", "{\"threadId\":1,\"allThreadsStopped\":true}"); } + + void c11_dap_send_exited_event(int exitCode) { char body[64]; snprintf(body, sizeof(body), "{\"exitCode\":%d}", exitCode); @@ -329,21 +387,24 @@ void c11_dap_init_server(const char* hostname, unsigned short port) { server.isclientready = false; c11_socket_bind(server.server, hostname, port); c11_socket_listen(server.server, 0); + // c11_dap_send_output_event("console", "[DEBUGGER INFO] : listen on %s:%hu\n",hostname,port); printf("[DEBUGGER INFO] : listen on %s:%hu\n", hostname, port); } void c11_dap_waitforclient(const char* hostname, unsigned short port) { server.toclient = c11_socket_accept(server.server, NULL, NULL); - printf("[DEBUGGER INFO] : connected a client\n"); } inline static void c11_dap_handle_message() { const char* message = c11_dap_read_message(); if(message == NULL) { return; } - // printf("[DEBUGGER INFO] read request %s\n", message); + // c11_dap_send_output_event("console", "[DEBUGGER LOG] : 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) { + // c11_dap_send_output_event("console", + // "[DEBUGGER LOG] : 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); @@ -365,15 +426,15 @@ void c11_dap_configure_debugger() { return; } } - printf("[DEBUGGER INFO] : configure done\n"); + // c11_dap_send_output_event("console", "[DEBUGGER INFO] : client configure done\n"); } void c11_dap_tracefunc(py_Frame* frame, enum py_TraceEvent event) { py_sys_settrace(NULL, false); C11_DEBUGGER_STATUS result = c11_debugger_on_trace(frame, event); if(result == C11_DEBUGGER_EXIT) { + // c11_dap_send_output_event("console", "[DEBUGGER INFO] : program exit\n"); c11_dap_send_exited_event(0); - printf("[DEBUGGER INFO] : program exit\n"); exit(0); } if(result != C11_DEBUGGER_SUCCESS) { @@ -409,7 +470,7 @@ void py_debugger_waitforattach(const char* hostname, unsigned short port) { c11_dap_configure_debugger(); if(!server.isconfiguredone) { c11_socket_close(server.toclient); - printf("[DEBUGGER INFO] : An clinet is ready\n"); + // c11_dap_send_output_event("console", "[DEBUGGER INFO] : An clinet is ready\n"); } } c11_socket_set_block(server.toclient, 0);