issue #82 improved handling of buffer/message generation

pull/93/merge
Dibyendu Majumdar 8 years ago
parent 7ee9f3216a
commit 578dd58e3b

@ -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;

@ -369,6 +369,7 @@ extern void vscode_make_success_response(ProtocolMessage *req,
ProtocolMessage *res, int restype);
extern void vscode_serialize_response(char *buf, size_t buflen,
ProtocolMessage *res);
extern void vscode_serialize_response_new(membuff_t *mb, ProtocolMessage *res);
extern void vscode_serialize_event(char *buf, size_t buflen,
ProtocolMessage *res);
extern void vscode_make_initialized_event(ProtocolMessage *res);
@ -390,7 +391,7 @@ extern void vscode_send_error_response(ProtocolMessage *req,
extern void vscode_send_success_response(ProtocolMessage *req,
ProtocolMessage *res, int responseType,
FILE *out, FILE *log);
extern int vscode_get_request(FILE *in, ProtocolMessage *req, FILE *log);
extern int vscode_get_request(FILE *in, membuff_t *mb, ProtocolMessage *req, FILE *log);
extern void vscode_json_stringify(const char *src, char *dest, size_t len);
/* guaranteed null termination */

@ -88,6 +88,7 @@ static int sourceOnStackCount = 0;
static int stepping_mode = DEBUGGER_STEPPING_IN; /* default */
static int stepping_stacklevel = -1; /* This tracks the stack level from where a step over or step out was requested */
static lua_State *stepping_lua_State = NULL; /* Tracks the Lua State that requested a step over or step out */
static membuff_t readbuf;
/*
* Generate response to InitializeRequest
@ -833,7 +834,7 @@ static void debugger(lua_State *L, lua_Debug *ar, FILE *in, FILE *out) {
bool get_command = true;
int command = VSCODE_UNKNOWN_REQUEST;
while (get_command &&
(command = vscode_get_request(in, &req, my_logger)) != VSCODE_EOF) {
(command = vscode_get_request(in, &readbuf, &req, my_logger)) != VSCODE_EOF) {
switch (command) {
case VSCODE_INITIALIZE_REQUEST: {
handle_initialize_request(&req, &res, out);
@ -993,9 +994,10 @@ int main(int argc, char **argv) {
fprintf(stderr, "Big endian architecture not supported\n");
exit(1);
}
membuff_init(&readbuf, 0);
/* For debugging purposes we log the interaction */
#ifdef _WIN32
my_logger = fopen("/temp/out1.txt", "w");
my_logger = fopen("/d/temp/out1.txt", "w");
#else
my_logger = fopen("/tmp/out1.txt", "w");
#endif
@ -1023,5 +1025,6 @@ int main(int argc, char **argv) {
debugger(L, NULL, stdin, stdout);
lua_close(L);
fclose(my_logger);
membuff_free(&readbuf);
return 0;
}

@ -25,23 +25,31 @@ int test_initreq() {
ProtocolMessage res;
vscode_make_error_response(&msg, &res, VSCODE_INITIALIZE_RESPONSE,
"unable to initialize");
char buf[1024];
vscode_serialize_response(buf, sizeof buf, &res);
//char buf[1024];
membuff_t mb;
membuff_init(&mb, 0);
vscode_serialize_response_new(&mb, &res);
//printf(buf);
const char *expected =
"{\"type\":\"response\",\"seq\":1,\"command\":\"initialize\",\"request_seq\":1,"
"\"success\":false,\"message\":\"unable to initialize\"}";
if (strcmp(expected, buf) != 0) return 1;
if (strcmp(expected, mb.buf) != 0) {
membuff_free(&mb);
return 1;
}
vscode_make_success_response(&msg, &res, VSCODE_INITIALIZE_RESPONSE);
vscode_serialize_response(buf, sizeof buf, &res);
membuff_rewindpos(&mb);
vscode_serialize_response_new(&mb, &res);
const char *expected2 =
"{\"type\":\"response\",\"seq\":2,\"command\":\"initialize\",\"request_seq\":1,"
"\"success\":true,\"body\":{\"supportsConfigurationDoneRequest\":false,"
"\"supportsFunctionBreakpoints\":false,"
"\"supportsConditionalBreakpoints\":false,\"supportsEvaluateForHovers\":"
"false}}";
if (strcmp(expected2, buf) != 0) return 1;
if (strcmp(expected2, mb.buf) != 0) {
membuff_free(&mb);
return 1;
}
char testdata2[] = "{\"type\":\"request\",\"seq\":2,\"command\":\"setBreakpoints\",\"arguments\":{\"source\":{\"path\":\"c:\\\\github\\\\ravi\\\\ravi-tests\\\\simple.lua\"},\"lines\" : [6],\"breakpoints\" : [{\"line\":6}]} }";
msgtype = vscode_parse_message(testdata2, strlen(testdata2), &msg, stderr);
if (msgtype != VSCODE_SET_BREAKPOINTS_REQUEST || msg.seq != 2 ||
@ -50,8 +58,10 @@ int test_initreq() {
msg.u.Request.request_type != VSCODE_SET_BREAKPOINTS_REQUEST ||
strcmp(msg.u.Request.u.SetBreakpointsRequest.source.path, "c:/github/ravi/ravi-tests/simple.lua") != 0 ||
msg.u.Request.u.SetBreakpointsRequest.breakpoints[0].line != 6) {
return 1;
membuff_free(&mb);
return 1;
}
membuff_free(&mb);
return 0;
}
@ -66,11 +76,38 @@ int test_json_stringify() {
return 0;
}
int test_membuff() {
membuff_t mb;
membuff_init(&mb, 0);
if (mb.allocated_size != 0 || mb.pos != 0 || mb.buf != NULL)
return 1;
membuff_resize(&mb, 5);
if (mb.allocated_size != 5 || mb.pos != 0 || mb.buf == NULL)
return 1;
const char *string1 = "hello world!";
size_t len = strlen(string1);
membuff_add_string(&mb, string1);
if (mb.allocated_size != len + 1 || mb.pos != len || strcmp(mb.buf, string1) != 0)
return 1;
const char *string2 = "hello world!true42";
len = strlen(string2);
membuff_add_bool(&mb, true);
membuff_add_int(&mb, 42);
if (mb.allocated_size != len + 1 || mb.pos != len || strcmp(mb.buf, string2) != 0)
return 1;
membuff_rewindpos(&mb);
if (mb.allocated_size != len + 1 || mb.pos != 0 || strcmp(mb.buf, string2) != 0)
return 1;
membuff_free(&mb);
return 0;
}
int main(void) {
setbuf(stdout, NULL);
int rc = 0;
rc += test_initreq();
rc += test_json_stringify();
rc += test_membuff();
if (rc == 0) printf("OK\n");
return rc == 0 ? 0 : 1;
}

Loading…
Cancel
Save