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