blob: 7830ec203aadef7fdccf1e1fcb35083bd858eaf2 [file] [log] [blame]
/*
* Example program using the dvalue debug transport.
*/
#include <stdio.h>
#include <stdlib.h>
#include "duktape.h"
#include "duk_trans_dvalue.h"
void my_cooperate(duk_trans_dvalue_ctx *ctx, int block) {
static int first_blocked = 1;
if (!block) {
/* Duktape is not blocked; you can cooperate with e.g. a user
* interface here and send dvalues to Duktape, but don't block.
*/
return;
}
/* Duktape is blocked on a read and won't continue until debug
* command(s) are sent.
*
* Normally you'd enter your own event loop here, and process
* events until something needs to be sent to Duktape. For
* example, the user might press a "Step over" button in the
* UI which would cause dvalues to be sent. You can then
* return from this callback.
*
* The code below sends some example messages for testing the
* dvalue handling of the transport.
*
* If you create dvalues manually and send them using
* duk_trans_dvalue_send(), you must free the dvalues after
* the send call returns using duk_dvalue_free().
*/
if (first_blocked) {
char *tmp;
int i;
/* First time Duktape becomes blocked, send DumpHeap which
* exercises a lot of parsing code.
*
* NOTE: Valgrind may complain about reading uninitialized
* bytes. This is caused by the DumpHeap command writing out
* verbatim duk_tval values which are intentionally not
* always fully initialized for performance reasons.
*/
first_blocked = 0;
fprintf(stderr, "Duktape is blocked, send DumpHeap\n");
fflush(stderr);
duk_trans_dvalue_send_req(ctx);
duk_trans_dvalue_send_integer(ctx, 0x20); /* DumpHeap */
duk_trans_dvalue_send_eom(ctx);
/* Also send a dummy TriggerStatus request with trailing dvalues
* ignored by Duktape; Duktape will parse the dvalues to be able to
* skip them, so that the dvalue encoding is exercised.
*/
tmp = malloc(100000); /* long buffer, >= 65536 chars */
for (i = 0; i < 100000; i++) {
tmp[i] = (char) i;
}
duk_trans_dvalue_send_req(ctx);
duk_trans_dvalue_send_integer(ctx, 0x11); /* TriggerStatus */
duk_trans_dvalue_send_string(ctx, "dummy"); /* short, <= 31 chars */
duk_trans_dvalue_send_string(ctx, "123456789012345678901234567890foobar"); /* medium, >= 32 chars */
duk_trans_dvalue_send_lstring(ctx, (const char *) tmp, 65535UL);
duk_trans_dvalue_send_lstring(ctx, (const char *) tmp, 65536UL);
duk_trans_dvalue_send_lstring(ctx, (const char *) tmp, 100000UL);
duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 255U);
duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 65535UL);
duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 65536UL);
duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 100000UL);
duk_trans_dvalue_send_unused(ctx);
duk_trans_dvalue_send_undefined(ctx);
duk_trans_dvalue_send_null(ctx);
duk_trans_dvalue_send_true(ctx);
duk_trans_dvalue_send_false(ctx);
duk_trans_dvalue_send_number(ctx, 123.456);
duk_trans_dvalue_send_object(ctx, 12 /*classnum*/, (const char *) tmp, 8); /* fake ptr len */
duk_trans_dvalue_send_pointer(ctx, (const char *) tmp, 8); /* fake ptr len */
duk_trans_dvalue_send_lightfunc(ctx, 0xdabc /*lf_flags*/, (const char *) tmp, 8); /* fake ptr len */
duk_trans_dvalue_send_heapptr(ctx, (const char *) tmp, 8); /* fake ptr len */
duk_trans_dvalue_send_eom(ctx);
}
fprintf(stderr, "Duktape is blocked, send Eval and StepInto to resume execution\n");
fflush(stderr);
/* duk_trans_dvalue_send_req_cmd() sends a REQ dvalue followed by
* an integer dvalue (command) for convenience.
*/
duk_trans_dvalue_send_req_cmd(ctx, 0x1e); /* 0x1e = Eval */
duk_trans_dvalue_send_string(ctx, "evalMe");
duk_trans_dvalue_send_eom(ctx);
duk_trans_dvalue_send_req_cmd(ctx, 0x14); /* 0x14 = StepOver */
duk_trans_dvalue_send_eom(ctx);
}
void my_received(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv) {
char buf[DUK_DVALUE_TOSTRING_BUFLEN];
(void) ctx;
duk_dvalue_to_string(dv, buf);
fprintf(stderr, "Received dvalue: %s\n", buf);
fflush(stderr);
/* Here a normal debug client would wait for dvalues until an EOM
* dvalue was received (which completes a debug message). The
* debug message would then be handled, possibly causing UI changes
* and/or causing debug commands to be sent to Duktape.
*
* The callback is responsible for eventually freeing the dvalue.
* Here we free it immediately, but an actual client would probably
* gather dvalues into an array or linked list to handle when the
* debug message was complete.
*/
duk_dvalue_free(dv);
}
void my_handshake(duk_trans_dvalue_ctx *ctx, const char *line) {
(void) ctx;
/* The Duktape handshake line is given in 'line' (without LF).
* The 'line' argument can be accessed for the duration of the
* callback (read only). Don't free 'line' here, the transport
* handles that.
*/
fprintf(stderr, "Received handshake line: '%s'\n", line);
fflush(stderr);
}
void my_detached(duk_trans_dvalue_ctx *ctx) {
(void) ctx;
/* Detached call forwarded as is. */
fprintf(stderr, "Debug transport detached\n");
fflush(stderr);
}
int main(int argc, char *argv[]) {
duk_context *ctx;
duk_trans_dvalue_ctx *trans_ctx;
int exitval = 0;
(void) argc; (void) argv; /* suppress warning */
ctx = duk_create_heap_default();
if (!ctx) {
fprintf(stderr, "Failed to create Duktape heap\n");
fflush(stderr);
exitval = 1;
goto cleanup;
}
trans_ctx = duk_trans_dvalue_init();
if (!trans_ctx) {
fprintf(stderr, "Failed to create debug transport context\n");
fflush(stderr);
exitval = 1;
goto cleanup;
}
trans_ctx->cooperate = my_cooperate;
trans_ctx->received = my_received;
trans_ctx->handshake = my_handshake;
trans_ctx->detached = my_detached;
/* Attach debugger; this will fail with a fatal error here unless
* debugger support is compiled in. To fail more gracefully, call
* this under a duk_safe_call() to catch the error.
*/
duk_debugger_attach(ctx,
duk_trans_dvalue_read_cb,
duk_trans_dvalue_write_cb,
duk_trans_dvalue_peek_cb,
duk_trans_dvalue_read_flush_cb,
duk_trans_dvalue_write_flush_cb,
duk_trans_dvalue_detached_cb,
(void *) trans_ctx);
fprintf(stderr, "Debugger attached, running eval\n");
fflush(stderr);
/* Evaluate simple test code, callbacks will "step over" until end.
*
* The test code here is just for exercising the debug transport.
* The 'evalMe' variable is evaluated (using debugger command Eval)
* before every step to force different dvalues to be carried over
* the transport.
*/
duk_eval_string(ctx,
"var evalMe;\n"
"\n"
"print('Hello world!');\n"
"[ undefined, null, true, false, 123, -123, 123.1, 0, -0, 1/0, 0/0, -1/0, \n"
" 'foo', Duktape.Buffer('bar'), Duktape.Pointer('dummy'), Math.cos, \n"
"].forEach(function (val) {\n"
" print(val);\n"
" evalMe = val;\n"
"});\n"
"\n"
"var str = 'xxx'\n"
"for (i = 0; i < 10; i++) {\n"
" print(i, str);\n"
" evalMe = str;\n"
" evalMe = Duktape.Buffer(str);\n"
" str = str + str;\n"
"}\n"
);
duk_pop(ctx);
duk_debugger_detach(ctx);
cleanup:
if (trans_ctx) {
duk_trans_dvalue_free(trans_ctx);
trans_ctx = NULL;
}
if (ctx) {
duk_destroy_heap(ctx);
}
return exitval;
}