]>
Commit | Line | Data |
---|---|---|
1e59de90 TL |
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | } |