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 <blueloveth@foxmail.com>
This commit is contained in:
lightovernight 2025-08-19 20:59:39 +08:00 committed by GitHub
parent 352556cbea
commit b3bbe3be58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 104 additions and 25 deletions

View File

@ -1,5 +1,6 @@
#include "pocketpy/interpreter/frame.h" #include "pocketpy/interpreter/frame.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include <ctype.h>
#include "pocketpy/debugger/core.h" #include "pocketpy/debugger/core.h"
@ -109,6 +110,7 @@ void c11_debugger_set_step_mode(C11_STEP_MODE mode) {
debugger.keep_suspend = false; debugger.keep_suspend = false;
} }
int c11_debugger_setbreakpoint(const char* filename, int lineno) { int c11_debugger_setbreakpoint(const char* filename, int lineno) {
c11_debugger_breakpoint breakpoint = {.sourcename = c11_strdup(filename), .lineno = lineno}; c11_debugger_breakpoint breakpoint = {.sourcename = c11_strdup(filename), .lineno = lineno};
c11_vector__push(c11_debugger_breakpoint, &debugger.breakpoints, breakpoint); 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; 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() { int c11_debugger_should_pause() {
if(debugger.current_event == TRACE_EVENT_POP) return false; if(debugger.current_event == TRACE_EVENT_POP) return false;
bool should_pause = false; bool should_pause = false;
@ -153,7 +170,7 @@ int c11_debugger_should_pause() {
} }
if(debugger.step_mode == C11_STEP_CONTINUE) { if(debugger.step_mode == C11_STEP_CONTINUE) {
c11__foreach(c11_debugger_breakpoint, &debugger.breakpoints, bp) { 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) { debugger.current_line == bp->lineno) {
should_pause = true; should_pause = true;
break; break;
@ -166,6 +183,7 @@ int c11_debugger_should_pause() {
int c11_debugger_should_keep_pause(void) { return debugger.keep_suspend; } int c11_debugger_should_keep_pause(void) { return debugger.keep_suspend; }
inline static c11_sv sv_from_cstr(const char* str) { inline static c11_sv sv_from_cstr(const char* str) {
c11_sv sv = {.data = str, .size = strlen(str)}; c11_sv sv = {.data = str, .size = strlen(str)};
return sv; return sv;
@ -295,7 +313,7 @@ bool c11_debugger_unfold_var(int var_id, c11_sbuf* buffer) {
// 5. dump & write // 5. dump & write
if(!py_json_dumps(dap_obj, 0)) { 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(); py_printexc();
return false; return false;
} }

View File

@ -20,7 +20,8 @@
X(variables) \ X(variables) \
X(threads) \ X(threads) \
X(configurationDone) \ X(configurationDone) \
X(ready) X(ready) \
X(evaluate)
#define DECLARE_HANDLE_FN(name) void c11_dap_handle_##name(py_Ref arguments, c11_sbuf*); #define DECLARE_HANDLE_FN(name) void c11_dap_handle_##name(py_Ref arguments, c11_sbuf*);
DAP_COMMAND_LIST(DECLARE_HANDLE_FN) 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, ','); 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) { void c11_dap_handle_scopes(py_Ref arguments, c11_sbuf* buffer) {
int res = py_dict_getitem_by_str(arguments, "frameId"); int res = py_dict_getitem_by_str(arguments, "frameId");
if(res <= 0) { if(res <= 0) {
if(res == 0) { if(res == 0) {
printf("[DEBUGGER ERROR] no frameID found\n"); c11__abort("[DEBUGGER ERROR] no frameID found in scopes request");
} else { } else {
py_printexc(); py_printexc();
c11__abort("[DEBUGGER ERROR] an error occurred while parsing request frameId");
} }
return; return;
} }
@ -170,9 +206,9 @@ const char* c11_dap_handle_request(const char* message) {
int res = py_dict_getitem_by_str(py_request, "command"); int res = py_dict_getitem_by_str(py_request, "command");
if(res == -1) { if(res == -1) {
py_printexc(); py_printexc();
return NULL; c11__abort("[DEBUGGER ERROR] an error occurred while parsing request");
} else if(res == 0) { } else if(res == 0) {
return "cannot find attribute command"; c11__abort("[DEBUGGER ERROR] no command found in request");
} }
py_assign(py_command, py_retval()); py_assign(py_command, py_retval());
const char* command = py_tostr(py_command); 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"); res = py_dict_getitem_by_str(py_request, "arguments");
if(res == -1) { if(res == -1) {
py_printexc(); py_printexc();
return NULL; c11__abort("[DEBUGGER ERROR] an error occurred while parsing request arguments");
} }
py_assign(py_arguments, py_retval()); py_assign(py_arguments, py_retval());
res = py_dict_getitem_by_str(py_request, "seq"); res = py_dict_getitem_by_str(py_request, "seq");
if(res == -1) { if(res == -1) {
py_printexc(); 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; 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) { void c11_dap_send_event(const char* event_name, const char* body_json) {
char json[256]; c11_sbuf buffer;
int json_len = snprintf(json,
sizeof(json),
"{\"seq\":%d,\"type\":\"event\",\"event\":\"%s\",\"body\":%s}",
server.dap_next_seq++,
event_name,
body_json);
char header[64]; 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); 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, 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() { void c11_dap_send_stop_event() {
c11_dap_send_event("stopped", "{\"threadId\":1,\"allThreadsStopped\":true}"); c11_dap_send_event("stopped", "{\"threadId\":1,\"allThreadsStopped\":true}");
} }
void c11_dap_send_exited_event(int exitCode) { void c11_dap_send_exited_event(int exitCode) {
char body[64]; char body[64];
snprintf(body, sizeof(body), "{\"exitCode\":%d}", exitCode); 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; server.isclientready = false;
c11_socket_bind(server.server, hostname, port); c11_socket_bind(server.server, hostname, port);
c11_socket_listen(server.server, 0); 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); printf("[DEBUGGER INFO] : listen on %s:%hu\n", hostname, port);
} }
void c11_dap_waitforclient(const char* hostname, unsigned short port) { void c11_dap_waitforclient(const char* hostname, unsigned short port) {
server.toclient = c11_socket_accept(server.server, NULL, NULL); server.toclient = c11_socket_accept(server.server, NULL, NULL);
printf("[DEBUGGER INFO] : connected a client\n");
} }
inline static void c11_dap_handle_message() { inline static void c11_dap_handle_message() {
const char* message = c11_dap_read_message(); const char* message = c11_dap_read_message();
if(message == NULL) { return; } 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); const char* response_content = c11_dap_handle_request(message);
// if(response_content != NULL) { printf("[DEBUGGER INFO] send response %s\n", if(response_content != NULL) {
// response_content); } // c11_dap_send_output_event("console",
// "[DEBUGGER LOG] : send response %s\n",
// response_content);
}
c11_sbuf buffer; c11_sbuf buffer;
c11_sbuf__ctor(&buffer); c11_sbuf__ctor(&buffer);
pk_sprintf(&buffer, "Content-Length: %d\r\n\r\n%s", strlen(response_content), response_content); 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; 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) { void c11_dap_tracefunc(py_Frame* frame, enum py_TraceEvent event) {
py_sys_settrace(NULL, false); py_sys_settrace(NULL, false);
C11_DEBUGGER_STATUS result = c11_debugger_on_trace(frame, event); C11_DEBUGGER_STATUS result = c11_debugger_on_trace(frame, event);
if(result == C11_DEBUGGER_EXIT) { if(result == C11_DEBUGGER_EXIT) {
// c11_dap_send_output_event("console", "[DEBUGGER INFO] : program exit\n");
c11_dap_send_exited_event(0); c11_dap_send_exited_event(0);
printf("[DEBUGGER INFO] : program exit\n");
exit(0); exit(0);
} }
if(result != C11_DEBUGGER_SUCCESS) { if(result != C11_DEBUGGER_SUCCESS) {
@ -409,7 +470,7 @@ void py_debugger_waitforattach(const char* hostname, unsigned short port) {
c11_dap_configure_debugger(); c11_dap_configure_debugger();
if(!server.isconfiguredone) { if(!server.isconfiguredone) {
c11_socket_close(server.toclient); 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); c11_socket_set_block(server.toclient, 0);