|
|
|
@ -1,5 +1,6 @@
|
|
|
|
|
#include "protocol.h"
|
|
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
@ -40,6 +41,7 @@ void membuff_add_string(membuff_t *mb, const char *str) {
|
|
|
|
|
size_t len = strlen(str);
|
|
|
|
|
size_t required_size = mb->pos + len + 1; /* extra byte for NULL terminator */
|
|
|
|
|
membuff_resize(mb, required_size);
|
|
|
|
|
assert(mb->allocated_size - mb->pos > len);
|
|
|
|
|
vscode_string_copy(&mb->buf[mb->pos], str, mb->allocated_size-mb->pos);
|
|
|
|
|
mb->pos += len;
|
|
|
|
|
}
|
|
|
|
@ -737,6 +739,151 @@ void vscode_serialize_response(char *buf, size_t buflen, ProtocolMessage *res) {
|
|
|
|
|
vscode_string_copy(buf, temp, buflen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void vscode_serialize_response_new(membuff_t *mb, ProtocolMessage *res) {
|
|
|
|
|
membuff_rewindpos(mb);
|
|
|
|
|
membuff_resize(mb, 1);
|
|
|
|
|
mb->buf[0] = 0;
|
|
|
|
|
if (res->type != VSCODE_RESPONSE) return;
|
|
|
|
|
membuff_add_string(mb, "{\"type\":\"response\",\"seq\":");
|
|
|
|
|
membuff_add_longlong(mb, res->seq);
|
|
|
|
|
membuff_add_string(mb, ",\"command\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Response.command);
|
|
|
|
|
membuff_add_string(mb, "\",\"request_seq\":");
|
|
|
|
|
membuff_add_longlong(mb, res->u.Response.request_seq);
|
|
|
|
|
membuff_add_string(mb, ",\"success\":");
|
|
|
|
|
membuff_add_bool(mb, res->u.Response.success);
|
|
|
|
|
if (res->u.Response.message[0]) {
|
|
|
|
|
membuff_add_string(mb, ",\"message\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Response.message);
|
|
|
|
|
membuff_add_string(mb, "\"");
|
|
|
|
|
}
|
|
|
|
|
if (!res->u.Response.success) {
|
|
|
|
|
membuff_add_string(mb, "}");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
switch (res->u.Response.response_type) {
|
|
|
|
|
case VSCODE_INITIALIZE_RESPONSE: {
|
|
|
|
|
membuff_add_string(mb, ",\"body\":{\"supportsConfigurationDoneRequest\":");
|
|
|
|
|
membuff_add_bool(mb, res->u.Response.u.InitializeResponse.body.supportsConfigurationDoneRequest);
|
|
|
|
|
membuff_add_string(mb, ",\"supportsFunctionBreakpoints\":");
|
|
|
|
|
membuff_add_bool(mb, res->u.Response.u.InitializeResponse.body.supportsFunctionBreakpoints);
|
|
|
|
|
membuff_add_string(mb, ",\"supportsConditionalBreakpoints\":");
|
|
|
|
|
membuff_add_bool(mb, res->u.Response.u.InitializeResponse.body.supportsConditionalBreakpoints);
|
|
|
|
|
membuff_add_string(mb, ",\"supportsEvaluateForHovers\":");
|
|
|
|
|
membuff_add_bool(mb, res->u.Response.u.InitializeResponse.body.supportsEvaluateForHovers);
|
|
|
|
|
membuff_add_string(mb, "}");
|
|
|
|
|
} break;
|
|
|
|
|
case VSCODE_THREAD_RESPONSE: {
|
|
|
|
|
membuff_add_string(mb, ",\"body\":{\"threads\":[{\"name\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Response.u.ThreadResponse.threads[0].name);
|
|
|
|
|
membuff_add_string(mb, "\",\"id\":");
|
|
|
|
|
membuff_add_int(mb, res->u.Response.u.ThreadResponse.threads[0].id);
|
|
|
|
|
membuff_add_string(mb, "}]}");
|
|
|
|
|
} break;
|
|
|
|
|
case VSCODE_STACK_TRACE_RESPONSE: {
|
|
|
|
|
membuff_add_string(mb, ",\"body\":{\"totalFrames\":");
|
|
|
|
|
membuff_add_int(mb, res->u.Response.u.StackTraceResponse.totalFrames);
|
|
|
|
|
membuff_add_string(mb, ",\"stackFrames\":[");
|
|
|
|
|
int d = 0;
|
|
|
|
|
for (; d < res->u.Response.u.StackTraceResponse.totalFrames; d++) {
|
|
|
|
|
if (d) {
|
|
|
|
|
membuff_add_string(mb, ",");
|
|
|
|
|
}
|
|
|
|
|
if (res->u.Response.u.StackTraceResponse.stackFrames[d]
|
|
|
|
|
.source.sourceReference == 0) {
|
|
|
|
|
membuff_add_string(mb, "{\"id\":");
|
|
|
|
|
membuff_add_int(mb, d);
|
|
|
|
|
membuff_add_string(mb, ",\"name\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Response.u.StackTraceResponse.stackFrames[d].name);
|
|
|
|
|
membuff_add_string(mb, "\",\"column\":1,\"line\":");
|
|
|
|
|
membuff_add_int(mb, res->u.Response.u.StackTraceResponse.stackFrames[d].line);
|
|
|
|
|
membuff_add_string(mb, ",\"source\":{\"name\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Response.u.StackTraceResponse.stackFrames[d].source.name);
|
|
|
|
|
membuff_add_string(mb, "\",\"path\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Response.u.StackTraceResponse.stackFrames[d].source.path);
|
|
|
|
|
membuff_add_string(mb, "\",\"sourceReference\":0}}");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
membuff_add_string(mb, "{\"id\":");
|
|
|
|
|
membuff_add_int(mb, d);
|
|
|
|
|
membuff_add_string(mb, ",\"name\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Response.u.StackTraceResponse.stackFrames[d].name);
|
|
|
|
|
membuff_add_string(mb, "\",\"column\":1,\"line\":");
|
|
|
|
|
membuff_add_int(mb, res->u.Response.u.StackTraceResponse.stackFrames[d].line);
|
|
|
|
|
membuff_add_string(mb, ",\"source\":{\"name\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Response.u.StackTraceResponse.stackFrames[d].source.name);
|
|
|
|
|
membuff_add_string(mb, "\",\"sourceReference\":");
|
|
|
|
|
membuff_add_longlong(mb, res->u.Response.u.StackTraceResponse.stackFrames[d].source.sourceReference);
|
|
|
|
|
membuff_add_string(mb, "}}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
membuff_add_string(mb, "]}");
|
|
|
|
|
} break;
|
|
|
|
|
case VSCODE_SCOPES_RESPONSE: {
|
|
|
|
|
membuff_add_string(mb, ",\"body\":{\"scopes\":[");
|
|
|
|
|
for (int d = 0; d < MAX_SCOPES; d++) {
|
|
|
|
|
if (!res->u.Response.u.ScopesResponse.scopes[d].name[0]) break;
|
|
|
|
|
if (d) {
|
|
|
|
|
membuff_add_string(mb, ",");
|
|
|
|
|
}
|
|
|
|
|
membuff_add_string(mb, "{\"name\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Response.u.ScopesResponse.scopes[d].name);
|
|
|
|
|
membuff_add_string(mb, "\",\"variablesReference\":");
|
|
|
|
|
membuff_add_int(mb, res->u.Response.u.ScopesResponse.scopes[d].variablesReference);
|
|
|
|
|
membuff_add_string(mb, ",\"expensive\":");
|
|
|
|
|
membuff_add_bool(mb, res->u.Response.u.ScopesResponse.scopes[d].expensive);
|
|
|
|
|
membuff_add_string(mb, "}");
|
|
|
|
|
}
|
|
|
|
|
membuff_add_string(mb, "]}");
|
|
|
|
|
} break;
|
|
|
|
|
case VSCODE_VARIABLES_RESPONSE: {
|
|
|
|
|
membuff_add_string(mb, ",\"body\":{\"variables\":[");
|
|
|
|
|
for (int d = 0; d < MAX_VARIABLES; d++) {
|
|
|
|
|
if (!res->u.Response.u.VariablesResponse.variables[d].name[0]) break;
|
|
|
|
|
if (d) {
|
|
|
|
|
membuff_add_string(mb, ",");
|
|
|
|
|
}
|
|
|
|
|
membuff_add_string(mb, "{\"name\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Response.u.VariablesResponse.variables[d].name);
|
|
|
|
|
membuff_add_string(mb, "\",\"variablesReference\":");
|
|
|
|
|
membuff_add_int(mb, res->u.Response.u.VariablesResponse.variables[d].variablesReference);
|
|
|
|
|
membuff_add_string(mb, ",\"value\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Response.u.VariablesResponse.variables[d].value);
|
|
|
|
|
membuff_add_string(mb, "\"}");
|
|
|
|
|
}
|
|
|
|
|
membuff_add_string(mb, "]}");
|
|
|
|
|
} break;
|
|
|
|
|
case VSCODE_SET_BREAKPOINTS_RESPONSE: {
|
|
|
|
|
membuff_add_string(mb, ",\"body\":{\"breakpoints\":[");
|
|
|
|
|
for (int d = 0; d < MAX_BREAKPOINTS; d++) {
|
|
|
|
|
if (!res->u.Response.u.SetBreakpointsResponse.breakpoints[d]
|
|
|
|
|
.source.path[0])
|
|
|
|
|
break;
|
|
|
|
|
if (d) {
|
|
|
|
|
membuff_add_string(mb, ",");
|
|
|
|
|
}
|
|
|
|
|
membuff_add_string(mb, "{\"verified\":");
|
|
|
|
|
membuff_add_bool(mb, res->u.Response.u.SetBreakpointsResponse.breakpoints[d].verified);
|
|
|
|
|
membuff_add_string(mb, ",\"source\":{\"path\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Response.u.SetBreakpointsResponse.breakpoints[d].source.path);
|
|
|
|
|
membuff_add_string(mb, "\"},\"line\":");
|
|
|
|
|
membuff_add_int(mb, res->u.Response.u.SetBreakpointsResponse.breakpoints[d].line);
|
|
|
|
|
membuff_add_string(mb, "}");
|
|
|
|
|
}
|
|
|
|
|
membuff_add_string(mb, "]}");
|
|
|
|
|
} break;
|
|
|
|
|
case VSCODE_SOURCE_RESPONSE: {
|
|
|
|
|
char buf[SOURCE_LEN * 2];
|
|
|
|
|
vscode_json_stringify(res->u.Response.u.SourceResponse.content, buf,
|
|
|
|
|
sizeof buf);
|
|
|
|
|
membuff_add_string(mb, ",\"body\":{\"content\":\"");
|
|
|
|
|
membuff_add_string(mb, buf);
|
|
|
|
|
membuff_add_string(mb, "\"}");
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
membuff_add_string(mb, "}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create a serialized form of the event in VSCode
|
|
|
|
|
* wire protocol format (see protocol.h)
|
|
|
|
@ -780,7 +927,51 @@ void vscode_serialize_event(char *buf, size_t buflen, ProtocolMessage *res) {
|
|
|
|
|
vscode_string_copy(buf, temp, buflen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void vscode_send(ProtocolMessage *msg, FILE *out, FILE *log) {
|
|
|
|
|
/*
|
|
|
|
|
* Create a serialized form of the event in VSCode
|
|
|
|
|
* wire protocol format (see protocol.h)
|
|
|
|
|
*/
|
|
|
|
|
void vscode_serialize_event_new(membuff_t *mb, ProtocolMessage *res) {
|
|
|
|
|
membuff_rewindpos(mb);
|
|
|
|
|
membuff_resize(mb, 1);
|
|
|
|
|
mb->buf[0] = 0;
|
|
|
|
|
if (res->type != VSCODE_EVENT) return;
|
|
|
|
|
membuff_add_string(mb, "{\"type\":\"event\",\"seq\":");
|
|
|
|
|
membuff_add_longlong(mb, res->seq);
|
|
|
|
|
membuff_add_string(mb, ",\"event\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Event.event);
|
|
|
|
|
membuff_add_string(mb, "\"");
|
|
|
|
|
if (res->u.Event.event_type == VSCODE_OUTPUT_EVENT) {
|
|
|
|
|
membuff_add_string(mb, ",\"body\":{\"category\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Event.u.OutputEvent.category);
|
|
|
|
|
membuff_add_string(mb, "\",\"output\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Event.u.OutputEvent.output);
|
|
|
|
|
membuff_add_string(mb, "\"}");
|
|
|
|
|
}
|
|
|
|
|
else if (res->u.Event.event_type == VSCODE_THREAD_EVENT) {
|
|
|
|
|
membuff_add_string(mb, ",\"body\":{\"reason\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Event.u.ThreadEvent.reason);
|
|
|
|
|
membuff_add_string(mb, "\",\"threadId\":");
|
|
|
|
|
membuff_add_int(mb, res->u.Event.u.ThreadEvent.threadId);
|
|
|
|
|
membuff_add_string(mb, "}");
|
|
|
|
|
}
|
|
|
|
|
else if (res->u.Event.event_type == VSCODE_STOPPED_EVENT) {
|
|
|
|
|
membuff_add_string(mb, ",\"body\":{\"reason\":\"");
|
|
|
|
|
membuff_add_string(mb, res->u.Event.u.StoppedEvent.reason);
|
|
|
|
|
membuff_add_string(mb, "\",\"threadId\":");
|
|
|
|
|
membuff_add_int(mb, res->u.Event.u.StoppedEvent.threadId);
|
|
|
|
|
membuff_add_string(mb, "}");
|
|
|
|
|
}
|
|
|
|
|
else if (res->u.Event.event_type == VSCODE_TERMINATED_EVENT) {
|
|
|
|
|
membuff_add_string(mb, ",\"body\":{\"restart\":");
|
|
|
|
|
membuff_add_bool(mb, res->u.Event.u.TerminatedEvent.restart);
|
|
|
|
|
membuff_add_string(mb, "}");
|
|
|
|
|
}
|
|
|
|
|
membuff_add_string(mb, "}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void vscode_send_old(ProtocolMessage *msg, FILE *out, FILE *log) {
|
|
|
|
|
char buf[4096];
|
|
|
|
|
char json_buf[sizeof buf + 30];
|
|
|
|
|
if (msg->type == VSCODE_EVENT)
|
|
|
|
@ -795,6 +986,25 @@ void vscode_send(ProtocolMessage *msg, FILE *out, FILE *log) {
|
|
|
|
|
fprintf(out, "%s", json_buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void vscode_send(ProtocolMessage *msg, FILE *out, FILE *log) {
|
|
|
|
|
membuff_t mb;
|
|
|
|
|
membuff_init(&mb, 8 * 1024);
|
|
|
|
|
char json_buf[30];
|
|
|
|
|
if (msg->type == VSCODE_EVENT)
|
|
|
|
|
vscode_serialize_event_new(&mb, msg);
|
|
|
|
|
else if (msg->type == VSCODE_RESPONSE)
|
|
|
|
|
vscode_serialize_response_new(&mb, msg);
|
|
|
|
|
else
|
|
|
|
|
return;
|
|
|
|
|
assert(mb.pos == strlen(mb.buf));
|
|
|
|
|
snprintf(json_buf, sizeof json_buf, "Content-Length: %d\r\n\r\n",
|
|
|
|
|
(int)strlen(mb.buf));
|
|
|
|
|
fprintf(log, "%s%s\n", json_buf, mb.buf);
|
|
|
|
|
fprintf(out, "%s%s", json_buf, mb.buf);
|
|
|
|
|
membuff_free(&mb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Send VSCode a StoppedEvent
|
|
|
|
|
* The event indicates that the execution of the debuggee has stopped due to some
|
|
|
|
@ -853,38 +1063,37 @@ void vscode_send_success_response(ProtocolMessage *req, ProtocolMessage *res,
|
|
|
|
|
* We currently don't bother checking the
|
|
|
|
|
* \r\n\r\n sequence for incoming messages
|
|
|
|
|
*/
|
|
|
|
|
int vscode_get_request(FILE *in, ProtocolMessage *req, FILE *log) {
|
|
|
|
|
char buf[4096] = {0};
|
|
|
|
|
if (fgets(buf, sizeof buf, in) != NULL) {
|
|
|
|
|
buf[sizeof buf - 1] = 0; /* NULL terminate - just in case */
|
|
|
|
|
const char *bufp = strstr(buf, "Content-Length: ");
|
|
|
|
|
if (bufp != NULL) {
|
|
|
|
|
bufp += 16;
|
|
|
|
|
int vscode_get_request(FILE *in, membuff_t *mb, ProtocolMessage *req, FILE *log) {
|
|
|
|
|
char tbuf[32] = {0};
|
|
|
|
|
if (fgets(tbuf, sizeof tbuf-1, in) != NULL) {
|
|
|
|
|
tbuf[sizeof tbuf - 1] = 0; /* NULL terminate - just in case */
|
|
|
|
|
if (strncmp(tbuf, "Content-Length: ", 16) == 0) {
|
|
|
|
|
char *bufp = tbuf+16;
|
|
|
|
|
/* get the message length */
|
|
|
|
|
int len = atoi(bufp);
|
|
|
|
|
if (len >= (int)(sizeof buf)) {
|
|
|
|
|
/* FIXME */
|
|
|
|
|
if (len <= 0) {
|
|
|
|
|
fprintf(log,
|
|
|
|
|
"FATAL ERROR - Content-Length = %d is greater than bufsize\n",
|
|
|
|
|
"FATAL ERROR - Content-Length = %d is invalid\n",
|
|
|
|
|
len);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
buf[0] = 0;
|
|
|
|
|
/* skip blank line - actually \r\n */
|
|
|
|
|
if (fgets(buf, sizeof buf, stdin) == NULL) {
|
|
|
|
|
fprintf(log, "FATAL ERROR - cannot read %d bytes\n", len);
|
|
|
|
|
if (fgets(tbuf, sizeof tbuf, stdin) == NULL) {
|
|
|
|
|
fprintf(log, "FATAL ERROR - error reading second newline after Content-Length:\n", len);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
/* Now read exact number of characters */
|
|
|
|
|
buf[0] = 0;
|
|
|
|
|
if (fread(buf, len, 1, stdin) != 1) {
|
|
|
|
|
membuff_resize(mb, len + 1); /* Allow for terminating 0 */
|
|
|
|
|
membuff_rewindpos(mb);
|
|
|
|
|
mb->buf[0] = 0;
|
|
|
|
|
if (fread(mb->buf, len, 1, stdin) != 1) {
|
|
|
|
|
fprintf(log, "FATAL ERROR - cannot read %d bytes\n", len);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
buf[len] = 0;
|
|
|
|
|
fprintf(log, "Content-Length: %d\r\n\r\n%s", len, buf);
|
|
|
|
|
mb->buf[len] = 0;
|
|
|
|
|
fprintf(log, "Content-Length: %d\r\n\r\n%s", len, mb->buf);
|
|
|
|
|
fflush(log);
|
|
|
|
|
return vscode_parse_message(buf, sizeof buf, req, log);
|
|
|
|
|
return vscode_parse_message(mb->buf, len, req, log);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return VSCODE_EOF;
|
|
|
|
|