1 /*
2 * Example program using the dvalue debug transport.
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7
8 #include "duktape.h"
9 #include "duk_trans_dvalue.h"
10
my_cooperate(duk_trans_dvalue_ctx * ctx,int block)11 void my_cooperate(duk_trans_dvalue_ctx *ctx, int block) {
12 static int first_blocked = 1;
13
14 if (!block) {
15 /* Duktape is not blocked; you can cooperate with e.g. a user
16 * interface here and send dvalues to Duktape, but don't block.
17 */
18 return;
19 }
20
21 /* Duktape is blocked on a read and won't continue until debug
22 * command(s) are sent.
23 *
24 * Normally you'd enter your own event loop here, and process
25 * events until something needs to be sent to Duktape. For
26 * example, the user might press a "Step over" button in the
27 * UI which would cause dvalues to be sent. You can then
28 * return from this callback.
29 *
30 * The code below sends some example messages for testing the
31 * dvalue handling of the transport.
32 *
33 * If you create dvalues manually and send them using
34 * duk_trans_dvalue_send(), you must free the dvalues after
35 * the send call returns using duk_dvalue_free().
36 */
37
38 if (first_blocked) {
39 char *tmp;
40 int i;
41
42 /* First time Duktape becomes blocked, send DumpHeap which
43 * exercises a lot of parsing code.
44 *
45 * NOTE: Valgrind may complain about reading uninitialized
46 * bytes. This is caused by the DumpHeap command writing out
47 * verbatim duk_tval values which are intentionally not
48 * always fully initialized for performance reasons.
49 */
50 first_blocked = 0;
51
52 fprintf(stderr, "Duktape is blocked, send DumpHeap\n");
53 fflush(stderr);
54
55 duk_trans_dvalue_send_req(ctx);
56 duk_trans_dvalue_send_integer(ctx, 0x20); /* DumpHeap */
57 duk_trans_dvalue_send_eom(ctx);
58
59 /* Also send a dummy TriggerStatus request with trailing dvalues
60 * ignored by Duktape; Duktape will parse the dvalues to be able to
61 * skip them, so that the dvalue encoding is exercised.
62 */
63
64 tmp = malloc(100000); /* long buffer, >= 65536 chars */
65 for (i = 0; i < 100000; i++) {
66 tmp[i] = (char) i;
67 }
68 duk_trans_dvalue_send_req(ctx);
69 duk_trans_dvalue_send_integer(ctx, 0x11); /* TriggerStatus */
70 duk_trans_dvalue_send_string(ctx, "dummy"); /* short, <= 31 chars */
71 duk_trans_dvalue_send_string(ctx, "123456789012345678901234567890foobar"); /* medium, >= 32 chars */
72 duk_trans_dvalue_send_lstring(ctx, (const char *) tmp, 65535UL);
73 duk_trans_dvalue_send_lstring(ctx, (const char *) tmp, 65536UL);
74 duk_trans_dvalue_send_lstring(ctx, (const char *) tmp, 100000UL);
75 duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 255U);
76 duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 65535UL);
77 duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 65536UL);
78 duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 100000UL);
79 duk_trans_dvalue_send_unused(ctx);
80 duk_trans_dvalue_send_undefined(ctx);
81 duk_trans_dvalue_send_null(ctx);
82 duk_trans_dvalue_send_true(ctx);
83 duk_trans_dvalue_send_false(ctx);
84 duk_trans_dvalue_send_number(ctx, 123.456);
85 duk_trans_dvalue_send_object(ctx, 12 /*classnum*/, (const char *) tmp, 8); /* fake ptr len */
86 duk_trans_dvalue_send_pointer(ctx, (const char *) tmp, 8); /* fake ptr len */
87 duk_trans_dvalue_send_lightfunc(ctx, 0xdabc /*lf_flags*/, (const char *) tmp, 8); /* fake ptr len */
88 duk_trans_dvalue_send_heapptr(ctx, (const char *) tmp, 8); /* fake ptr len */
89
90 duk_trans_dvalue_send_eom(ctx);
91 }
92
93 fprintf(stderr, "Duktape is blocked, send Eval and StepInto to resume execution\n");
94 fflush(stderr);
95
96 /* duk_trans_dvalue_send_req_cmd() sends a REQ dvalue followed by
97 * an integer dvalue (command) for convenience.
98 */
99
100 duk_trans_dvalue_send_req_cmd(ctx, 0x1e); /* 0x1e = Eval */
101 duk_trans_dvalue_send_string(ctx, "evalMe");
102 duk_trans_dvalue_send_eom(ctx);
103
104 duk_trans_dvalue_send_req_cmd(ctx, 0x14); /* 0x14 = StepOver */
105 duk_trans_dvalue_send_eom(ctx);
106 }
107
my_received(duk_trans_dvalue_ctx * ctx,duk_dvalue * dv)108 void my_received(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv) {
109 char buf[DUK_DVALUE_TOSTRING_BUFLEN];
110 (void) ctx;
111
112 duk_dvalue_to_string(dv, buf);
113 fprintf(stderr, "Received dvalue: %s\n", buf);
114 fflush(stderr);
115
116 /* Here a normal debug client would wait for dvalues until an EOM
117 * dvalue was received (which completes a debug message). The
118 * debug message would then be handled, possibly causing UI changes
119 * and/or causing debug commands to be sent to Duktape.
120 *
121 * The callback is responsible for eventually freeing the dvalue.
122 * Here we free it immediately, but an actual client would probably
123 * gather dvalues into an array or linked list to handle when the
124 * debug message was complete.
125 */
126
127 duk_dvalue_free(dv);
128 }
129
my_handshake(duk_trans_dvalue_ctx * ctx,const char * line)130 void my_handshake(duk_trans_dvalue_ctx *ctx, const char *line) {
131 (void) ctx;
132
133 /* The Duktape handshake line is given in 'line' (without LF).
134 * The 'line' argument can be accessed for the duration of the
135 * callback (read only). Don't free 'line' here, the transport
136 * handles that.
137 */
138
139 fprintf(stderr, "Received handshake line: '%s'\n", line);
140 fflush(stderr);
141 }
142
my_detached(duk_trans_dvalue_ctx * ctx)143 void my_detached(duk_trans_dvalue_ctx *ctx) {
144 (void) ctx;
145
146 /* Detached call forwarded as is. */
147
148 fprintf(stderr, "Debug transport detached\n");
149 fflush(stderr);
150 }
151
main(int argc,char * argv[])152 int main(int argc, char *argv[]) {
153 duk_context *ctx;
154 duk_trans_dvalue_ctx *trans_ctx;
155 int exitval = 0;
156
157 (void) argc; (void) argv; /* suppress warning */
158
159 ctx = duk_create_heap_default();
160 if (!ctx) {
161 fprintf(stderr, "Failed to create Duktape heap\n");
162 fflush(stderr);
163 exitval = 1;
164 goto cleanup;
165 }
166
167 trans_ctx = duk_trans_dvalue_init();
168 if (!trans_ctx) {
169 fprintf(stderr, "Failed to create debug transport context\n");
170 fflush(stderr);
171 exitval = 1;
172 goto cleanup;
173 }
174 trans_ctx->cooperate = my_cooperate;
175 trans_ctx->received = my_received;
176 trans_ctx->handshake = my_handshake;
177 trans_ctx->detached = my_detached;
178
179 /* Attach debugger; this will fail with a fatal error here unless
180 * debugger support is compiled in. To fail more gracefully, call
181 * this under a duk_safe_call() to catch the error.
182 */
183 duk_debugger_attach(ctx,
184 duk_trans_dvalue_read_cb,
185 duk_trans_dvalue_write_cb,
186 duk_trans_dvalue_peek_cb,
187 duk_trans_dvalue_read_flush_cb,
188 duk_trans_dvalue_write_flush_cb,
189 duk_trans_dvalue_detached_cb,
190 (void *) trans_ctx);
191
192 fprintf(stderr, "Debugger attached, running eval\n");
193 fflush(stderr);
194
195 /* Evaluate simple test code, callbacks will "step over" until end.
196 *
197 * The test code here is just for exercising the debug transport.
198 * The 'evalMe' variable is evaluated (using debugger command Eval)
199 * before every step to force different dvalues to be carried over
200 * the transport.
201 */
202
203 duk_eval_string(ctx,
204 "var evalMe;\n"
205 "\n"
206 "print('Hello world!');\n"
207 "[ undefined, null, true, false, 123, -123, 123.1, 0, -0, 1/0, 0/0, -1/0, \n"
208 " 'foo', Duktape.Buffer('bar'), Duktape.Pointer('dummy'), Math.cos, \n"
209 "].forEach(function (val) {\n"
210 " print(val);\n"
211 " evalMe = val;\n"
212 "});\n"
213 "\n"
214 "var str = 'xxx'\n"
215 "for (i = 0; i < 10; i++) {\n"
216 " print(i, str);\n"
217 " evalMe = str;\n"
218 " evalMe = Duktape.Buffer(str);\n"
219 " str = str + str;\n"
220 "}\n"
221 );
222 duk_pop(ctx);
223
224 duk_debugger_detach(ctx);
225
226 cleanup:
227 if (trans_ctx) {
228 duk_trans_dvalue_free(trans_ctx);
229 trans_ctx = NULL;
230 }
231 if (ctx) {
232 duk_destroy_heap(ctx);
233 }
234
235 return exitval;
236 }
237