]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * Duktape debugger | |
3 | */ | |
4 | ||
5 | #include "duk_internal.h" | |
6 | ||
7 | #if defined(DUK_USE_DEBUGGER_SUPPORT) | |
8 | ||
9 | /* | |
10 | * Helper structs | |
11 | */ | |
12 | ||
13 | typedef union { | |
14 | void *p; | |
15 | duk_uint_t b[1]; | |
16 | /* Use b[] to access the size of the union, which is strictly not | |
17 | * correct. Can't use fixed size unless there's feature detection | |
18 | * for pointer byte size. | |
19 | */ | |
20 | } duk__ptr_union; | |
21 | ||
22 | /* | |
23 | * Detach handling | |
24 | */ | |
25 | ||
26 | #define DUK__SET_CONN_BROKEN(thr) do { \ | |
27 | /* For now shared handler is fine. */ \ | |
28 | duk_debug_do_detach((thr)->heap); \ | |
29 | } while (0) | |
30 | ||
31 | DUK_INTERNAL void duk_debug_do_detach(duk_heap *heap) { | |
32 | /* Can be called muliple times with no harm. */ | |
33 | ||
34 | heap->dbg_read_cb = NULL; | |
35 | heap->dbg_write_cb = NULL; | |
36 | heap->dbg_peek_cb = NULL; | |
37 | heap->dbg_read_flush_cb = NULL; | |
38 | heap->dbg_write_flush_cb = NULL; | |
39 | if (heap->dbg_detached_cb) { | |
40 | heap->dbg_detached_cb(heap->dbg_udata); | |
41 | } | |
42 | heap->dbg_detached_cb = NULL; | |
43 | heap->dbg_udata = NULL; | |
44 | heap->dbg_processing = 0; | |
45 | heap->dbg_paused = 0; | |
46 | heap->dbg_state_dirty = 0; | |
47 | heap->dbg_force_restart = 0; | |
48 | heap->dbg_step_type = 0; | |
49 | heap->dbg_step_thread = NULL; | |
50 | heap->dbg_step_csindex = 0; | |
51 | heap->dbg_step_startline = 0; | |
52 | ||
53 | /* Ensure there are no stale active breakpoint pointers. | |
54 | * Breakpoint list is currently kept - we could empty it | |
55 | * here but we'd need to handle refcounts correctly, and | |
56 | * we'd need a 'thr' reference for that. | |
57 | * | |
58 | * XXX: clear breakpoint on either attach or detach? | |
59 | */ | |
60 | heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL; | |
61 | } | |
62 | ||
63 | /* | |
64 | * Debug connection peek and flush primitives | |
65 | */ | |
66 | ||
67 | DUK_INTERNAL duk_bool_t duk_debug_read_peek(duk_hthread *thr) { | |
68 | duk_heap *heap; | |
69 | ||
70 | DUK_ASSERT(thr != NULL); | |
71 | heap = thr->heap; | |
72 | DUK_ASSERT(heap != NULL); | |
73 | ||
74 | if (heap->dbg_read_cb == NULL) { | |
75 | DUK_D(DUK_DPRINT("attempt to peek in detached state, return zero (= no data)")); | |
76 | return 0; | |
77 | } | |
78 | if (heap->dbg_peek_cb == NULL) { | |
79 | DUK_DD(DUK_DDPRINT("no peek callback, return zero (= no data)")); | |
80 | return 0; | |
81 | } | |
82 | ||
83 | return (duk_bool_t) (heap->dbg_peek_cb(heap->dbg_udata) > 0); | |
84 | } | |
85 | ||
86 | DUK_INTERNAL void duk_debug_read_flush(duk_hthread *thr) { | |
87 | duk_heap *heap; | |
88 | ||
89 | DUK_ASSERT(thr != NULL); | |
90 | heap = thr->heap; | |
91 | DUK_ASSERT(heap != NULL); | |
92 | ||
93 | if (heap->dbg_read_cb == NULL) { | |
94 | DUK_D(DUK_DPRINT("attempt to read flush in detached state, ignore")); | |
95 | return; | |
96 | } | |
97 | if (heap->dbg_read_flush_cb == NULL) { | |
98 | DUK_DD(DUK_DDPRINT("no read flush callback, ignore")); | |
99 | return; | |
100 | } | |
101 | ||
102 | heap->dbg_read_flush_cb(heap->dbg_udata); | |
103 | } | |
104 | ||
105 | DUK_INTERNAL void duk_debug_write_flush(duk_hthread *thr) { | |
106 | duk_heap *heap; | |
107 | ||
108 | DUK_ASSERT(thr != NULL); | |
109 | heap = thr->heap; | |
110 | DUK_ASSERT(heap != NULL); | |
111 | ||
112 | if (heap->dbg_read_cb == NULL) { | |
113 | DUK_D(DUK_DPRINT("attempt to write flush in detached state, ignore")); | |
114 | return; | |
115 | } | |
116 | if (heap->dbg_write_flush_cb == NULL) { | |
117 | DUK_DD(DUK_DDPRINT("no write flush callback, ignore")); | |
118 | return; | |
119 | } | |
120 | ||
121 | heap->dbg_write_flush_cb(heap->dbg_udata); | |
122 | } | |
123 | ||
124 | /* | |
125 | * Debug connection skip primitives | |
126 | */ | |
127 | ||
128 | /* Skip fully. */ | |
129 | DUK_INTERNAL void duk_debug_skip_bytes(duk_hthread *thr, duk_size_t length) { | |
130 | duk_uint8_t dummy[64]; | |
131 | duk_size_t now; | |
132 | ||
133 | DUK_ASSERT(thr != NULL); | |
134 | ||
135 | while (length > 0) { | |
136 | now = (length > sizeof(dummy) ? sizeof(dummy) : length); | |
137 | duk_debug_read_bytes(thr, dummy, now); | |
138 | length -= now; | |
139 | } | |
140 | } | |
141 | ||
142 | DUK_INTERNAL void duk_debug_skip_byte(duk_hthread *thr) { | |
143 | DUK_ASSERT(thr != NULL); | |
144 | ||
145 | (void) duk_debug_read_byte(thr); | |
146 | } | |
147 | ||
148 | /* | |
149 | * Debug connection read primitives | |
150 | */ | |
151 | ||
152 | /* Read fully. */ | |
153 | DUK_INTERNAL void duk_debug_read_bytes(duk_hthread *thr, duk_uint8_t *data, duk_size_t length) { | |
154 | duk_heap *heap; | |
155 | duk_uint8_t *p; | |
156 | duk_size_t left; | |
157 | duk_size_t got; | |
158 | ||
159 | DUK_ASSERT(thr != NULL); | |
160 | heap = thr->heap; | |
161 | DUK_ASSERT(heap != NULL); | |
162 | ||
163 | if (heap->dbg_read_cb == NULL) { | |
164 | DUK_D(DUK_DPRINT("attempt to read %ld bytes in detached state, return zero data", (long) length)); | |
165 | goto fail; | |
166 | } | |
167 | ||
168 | p = data; | |
169 | for (;;) { | |
170 | left = (duk_size_t) ((data + length) - p); | |
171 | if (left == 0) { | |
172 | break; | |
173 | } | |
174 | DUK_ASSERT(heap->dbg_read_cb != NULL); | |
175 | DUK_ASSERT(left >= 1); | |
176 | #if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE) | |
177 | left = 1; | |
178 | #endif | |
179 | got = heap->dbg_read_cb(heap->dbg_udata, (char *) p, left); | |
180 | if (got == 0 || got > left) { | |
181 | DUK_D(DUK_DPRINT("connection error during read, return zero data")); | |
182 | DUK__SET_CONN_BROKEN(thr); | |
183 | goto fail; | |
184 | } | |
185 | p += got; | |
186 | } | |
187 | return; | |
188 | ||
189 | fail: | |
190 | DUK_MEMZERO((void *) data, (size_t) length); | |
191 | } | |
192 | ||
193 | DUK_INTERNAL duk_uint8_t duk_debug_read_byte(duk_hthread *thr) { | |
194 | duk_heap *heap; | |
195 | duk_size_t got; | |
196 | duk_uint8_t x; | |
197 | ||
198 | DUK_ASSERT(thr != NULL); | |
199 | heap = thr->heap; | |
200 | DUK_ASSERT(heap != NULL); | |
201 | ||
202 | if (heap->dbg_read_cb == NULL) { | |
203 | DUK_D(DUK_DPRINT("attempt to read 1 bytes in detached state, return zero data")); | |
204 | return 0; | |
205 | } | |
206 | ||
207 | x = 0; /* just in case callback is broken and won't write 'x' */ | |
208 | DUK_ASSERT(heap->dbg_read_cb != NULL); | |
209 | got = heap->dbg_read_cb(heap->dbg_udata, (char *) (&x), 1); | |
210 | if (got != 1) { | |
211 | DUK_D(DUK_DPRINT("connection error during read, return zero data")); | |
212 | DUK__SET_CONN_BROKEN(thr); | |
213 | return 0; | |
214 | } | |
215 | ||
216 | return x; | |
217 | } | |
218 | ||
219 | DUK_LOCAL duk_uint32_t duk__debug_read_uint32_raw(duk_hthread *thr) { | |
220 | duk_uint8_t buf[4]; | |
221 | ||
222 | DUK_ASSERT(thr != NULL); | |
223 | ||
224 | duk_debug_read_bytes(thr, buf, 4); | |
225 | return ((duk_uint32_t) buf[0] << 24) | | |
226 | ((duk_uint32_t) buf[1] << 16) | | |
227 | ((duk_uint32_t) buf[2] << 8) | | |
228 | (duk_uint32_t) buf[3]; | |
229 | } | |
230 | ||
231 | DUK_LOCAL duk_uint32_t duk__debug_read_int32_raw(duk_hthread *thr) { | |
232 | return (duk_int32_t) duk__debug_read_uint32_raw(thr); | |
233 | } | |
234 | ||
235 | DUK_LOCAL duk_uint16_t duk__debug_read_uint16_raw(duk_hthread *thr) { | |
236 | duk_uint8_t buf[2]; | |
237 | ||
238 | DUK_ASSERT(thr != NULL); | |
239 | ||
240 | duk_debug_read_bytes(thr, buf, 2); | |
241 | return ((duk_uint16_t) buf[0] << 8) | | |
242 | (duk_uint16_t) buf[1]; | |
243 | } | |
244 | ||
245 | DUK_INTERNAL duk_int32_t duk_debug_read_int(duk_hthread *thr) { | |
246 | duk_small_uint_t x; | |
247 | duk_small_uint_t t; | |
248 | ||
249 | DUK_ASSERT(thr != NULL); | |
250 | ||
251 | x = duk_debug_read_byte(thr); | |
252 | if (x >= 0xc0) { | |
253 | t = duk_debug_read_byte(thr); | |
254 | return (duk_int32_t) (((x - 0xc0) << 8) + t); | |
255 | } else if (x >= 0x80) { | |
256 | return (duk_int32_t) (x - 0x80); | |
257 | } else if (x == 0x10) { | |
258 | return (duk_int32_t) duk__debug_read_uint32_raw(thr); | |
259 | } | |
260 | ||
261 | DUK_D(DUK_DPRINT("debug connection error: failed to decode int")); | |
262 | DUK__SET_CONN_BROKEN(thr); | |
263 | return 0; | |
264 | } | |
265 | ||
266 | DUK_LOCAL duk_hstring *duk__debug_read_hstring_raw(duk_hthread *thr, duk_uint32_t len) { | |
267 | duk_context *ctx = (duk_context *) thr; | |
268 | duk_uint8_t buf[31]; | |
269 | duk_uint8_t *p; | |
270 | ||
271 | if (len <= sizeof(buf)) { | |
272 | duk_debug_read_bytes(thr, buf, (duk_size_t) len); | |
273 | duk_push_lstring(ctx, (const char *) buf, (duk_size_t) len); | |
274 | } else { | |
275 | p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, (duk_size_t) len); | |
276 | DUK_ASSERT(p != NULL); | |
277 | duk_debug_read_bytes(thr, p, (duk_size_t) len); | |
278 | duk_to_string(ctx, -1); | |
279 | } | |
280 | ||
281 | return duk_require_hstring(ctx, -1); | |
282 | } | |
283 | ||
284 | DUK_INTERNAL duk_hstring *duk_debug_read_hstring(duk_hthread *thr) { | |
285 | duk_context *ctx = (duk_context *) thr; | |
286 | duk_small_uint_t x; | |
287 | duk_uint32_t len; | |
288 | ||
289 | DUK_ASSERT(thr != NULL); | |
290 | ||
291 | x = duk_debug_read_byte(thr); | |
292 | if (x >= 0x60 && x <= 0x7f) { | |
293 | /* For short strings, use a fixed temp buffer. */ | |
294 | len = (duk_uint32_t) (x - 0x60); | |
295 | } else if (x == 0x12) { | |
296 | len = (duk_uint32_t) duk__debug_read_uint16_raw(thr); | |
297 | } else if (x == 0x11) { | |
298 | len = (duk_uint32_t) duk__debug_read_uint32_raw(thr); | |
299 | } else { | |
300 | goto fail; | |
301 | } | |
302 | ||
303 | return duk__debug_read_hstring_raw(thr, len); | |
304 | ||
305 | fail: | |
306 | DUK_D(DUK_DPRINT("debug connection error: failed to decode int")); | |
307 | DUK__SET_CONN_BROKEN(thr); | |
308 | duk_push_hstring_stridx(thr, DUK_STRIDX_EMPTY_STRING); /* always push some string */ | |
309 | return duk_require_hstring(ctx, -1); | |
310 | } | |
311 | ||
312 | DUK_LOCAL duk_hbuffer *duk__debug_read_hbuffer_raw(duk_hthread *thr, duk_uint32_t len) { | |
313 | duk_context *ctx = (duk_context *) thr; | |
314 | duk_uint8_t *p; | |
315 | ||
316 | p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, (duk_size_t) len); | |
317 | DUK_ASSERT(p != NULL); | |
318 | duk_debug_read_bytes(thr, p, (duk_size_t) len); | |
319 | ||
320 | return duk_require_hbuffer(ctx, -1); | |
321 | } | |
322 | ||
323 | DUK_LOCAL const void *duk__debug_read_pointer_raw(duk_hthread *thr) { | |
324 | duk_small_uint_t x; | |
325 | volatile duk__ptr_union pu; | |
326 | ||
327 | DUK_ASSERT(thr != NULL); | |
328 | ||
329 | x = duk_debug_read_byte(thr); | |
330 | if (x != sizeof(pu)) { | |
331 | goto fail; | |
332 | } | |
333 | duk_debug_read_bytes(thr, (duk_uint8_t *) &pu.p, sizeof(pu)); | |
334 | #if defined(DUK_USE_INTEGER_LE) | |
335 | duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu)); | |
336 | #endif | |
337 | return (const void *) pu.p; | |
338 | ||
339 | fail: | |
340 | DUK_D(DUK_DPRINT("debug connection error: failed to decode pointer")); | |
341 | DUK__SET_CONN_BROKEN(thr); | |
342 | return (const void *) NULL; | |
343 | } | |
344 | ||
345 | DUK_LOCAL duk_double_t duk__debug_read_double_raw(duk_hthread *thr) { | |
346 | duk_double_union du; | |
347 | ||
348 | DUK_ASSERT(sizeof(du.uc) == 8); | |
349 | duk_debug_read_bytes(thr, (duk_uint8_t *) du.uc, sizeof(du.uc)); | |
350 | DUK_DBLUNION_DOUBLE_NTOH(&du); | |
351 | return du.d; | |
352 | } | |
353 | ||
354 | DUK_INTERNAL void duk_debug_read_tval(duk_hthread *thr) { | |
355 | duk_context *ctx = (duk_context *) thr; | |
356 | duk_uint8_t x; | |
357 | duk_uint_t t; | |
358 | duk_uint32_t len; | |
359 | ||
360 | DUK_ASSERT(thr != NULL); | |
361 | ||
362 | x = duk_debug_read_byte(thr); | |
363 | ||
364 | if (x >= 0xc0) { | |
365 | t = (duk_uint_t) (x - 0xc0); | |
366 | t = (t << 8) + duk_debug_read_byte(thr); | |
367 | duk_push_uint(ctx, (duk_uint_t) t); | |
368 | return; | |
369 | } | |
370 | if (x >= 0x80) { | |
371 | duk_push_uint(ctx, (duk_uint_t) (x - 0x80)); | |
372 | return; | |
373 | } | |
374 | if (x >= 0x60) { | |
375 | len = (duk_uint32_t) (x - 0x60); | |
376 | duk__debug_read_hstring_raw(thr, len); | |
377 | return; | |
378 | } | |
379 | ||
380 | switch (x) { | |
381 | case 0x10: { | |
382 | duk_int32_t i = duk__debug_read_int32_raw(thr); | |
383 | duk_push_i32(ctx, i); | |
384 | break; | |
385 | } | |
386 | case 0x11: | |
387 | len = duk__debug_read_uint32_raw(thr); | |
388 | duk__debug_read_hstring_raw(thr, len); | |
389 | break; | |
390 | case 0x12: | |
391 | len = duk__debug_read_uint16_raw(thr); | |
392 | duk__debug_read_hstring_raw(thr, len); | |
393 | break; | |
394 | case 0x13: | |
395 | len = duk__debug_read_uint32_raw(thr); | |
396 | duk__debug_read_hbuffer_raw(thr, len); | |
397 | break; | |
398 | case 0x14: | |
399 | len = duk__debug_read_uint16_raw(thr); | |
400 | duk__debug_read_hbuffer_raw(thr, len); | |
401 | break; | |
402 | case 0x15: | |
403 | duk_push_unused(ctx); | |
404 | break; | |
405 | case 0x16: | |
406 | duk_push_undefined(ctx); | |
407 | break; | |
408 | case 0x17: | |
409 | duk_push_null(ctx); | |
410 | break; | |
411 | case 0x18: | |
412 | duk_push_true(ctx); | |
413 | break; | |
414 | case 0x19: | |
415 | duk_push_false(ctx); | |
416 | break; | |
417 | case 0x1a: { | |
418 | duk_double_t d; | |
419 | d = duk__debug_read_double_raw(thr); | |
420 | duk_push_number(ctx, d); | |
421 | break; | |
422 | } | |
423 | case 0x1b: | |
424 | /* XXX: not needed for now, so not implemented */ | |
425 | DUK_D(DUK_DPRINT("reading object values unimplemented")); | |
426 | goto fail; | |
427 | case 0x1c: { | |
428 | const void *ptr; | |
429 | ptr = duk__debug_read_pointer_raw(thr); | |
430 | duk_push_pointer(thr, (void *) ptr); | |
431 | break; | |
432 | } | |
433 | case 0x1d: | |
434 | /* XXX: not needed for now, so not implemented */ | |
435 | DUK_D(DUK_DPRINT("reading lightfunc values unimplemented")); | |
436 | goto fail; | |
437 | case 0x1e: { | |
438 | duk_heaphdr *h; | |
439 | h = (duk_heaphdr *) duk__debug_read_pointer_raw(thr); | |
440 | duk_push_heapptr(thr, (void *) h); | |
441 | break; | |
442 | } | |
443 | default: | |
444 | goto fail; | |
445 | } | |
446 | ||
447 | return; | |
448 | ||
449 | fail: | |
450 | DUK_D(DUK_DPRINT("debug connection error: failed to decode tval")); | |
451 | DUK__SET_CONN_BROKEN(thr); | |
452 | } | |
453 | ||
454 | /* | |
455 | * Debug connection write primitives | |
456 | */ | |
457 | ||
458 | /* Write fully. */ | |
459 | DUK_INTERNAL void duk_debug_write_bytes(duk_hthread *thr, const duk_uint8_t *data, duk_size_t length) { | |
460 | duk_heap *heap; | |
461 | const duk_uint8_t *p; | |
462 | duk_size_t left; | |
463 | duk_size_t got; | |
464 | ||
465 | DUK_ASSERT(thr != NULL); | |
466 | DUK_ASSERT(length == 0 || data != NULL); | |
467 | heap = thr->heap; | |
468 | DUK_ASSERT(heap != NULL); | |
469 | ||
470 | if (heap->dbg_write_cb == NULL) { | |
471 | DUK_D(DUK_DPRINT("attempt to write %ld bytes in detached state, ignore", (long) length)); | |
472 | return; | |
473 | } | |
474 | if (length == 0) { | |
475 | /* Avoid doing an actual write callback with length == 0, | |
476 | * because that's reserved for a write flush. | |
477 | */ | |
478 | return; | |
479 | } | |
480 | DUK_ASSERT(data != NULL); | |
481 | ||
482 | p = data; | |
483 | for (;;) { | |
484 | left = (duk_size_t) ((data + length) - p); | |
485 | if (left == 0) { | |
486 | break; | |
487 | } | |
488 | DUK_ASSERT(heap->dbg_write_cb != NULL); | |
489 | DUK_ASSERT(left >= 1); | |
490 | #if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE) | |
491 | left = 1; | |
492 | #endif | |
493 | got = heap->dbg_write_cb(heap->dbg_udata, (const char *) p, left); | |
494 | if (got == 0 || got > left) { | |
495 | DUK_D(DUK_DPRINT("connection error during write")); | |
496 | DUK__SET_CONN_BROKEN(thr); | |
497 | return; | |
498 | } | |
499 | p += got; | |
500 | } | |
501 | } | |
502 | ||
503 | DUK_INTERNAL void duk_debug_write_byte(duk_hthread *thr, duk_uint8_t x) { | |
504 | duk_heap *heap; | |
505 | duk_size_t got; | |
506 | ||
507 | DUK_ASSERT(thr != NULL); | |
508 | heap = thr->heap; | |
509 | DUK_ASSERT(heap != NULL); | |
510 | ||
511 | if (heap->dbg_write_cb == NULL) { | |
512 | DUK_D(DUK_DPRINT("attempt to write 1 bytes in detached state, ignore")); | |
513 | return; | |
514 | } | |
515 | ||
516 | DUK_ASSERT(heap->dbg_write_cb != NULL); | |
517 | got = heap->dbg_write_cb(heap->dbg_udata, (const char *) (&x), 1); | |
518 | if (got != 1) { | |
519 | DUK_D(DUK_DPRINT("connection error during write")); | |
520 | DUK__SET_CONN_BROKEN(thr); | |
521 | } | |
522 | } | |
523 | ||
524 | DUK_INTERNAL void duk_debug_write_unused(duk_hthread *thr) { | |
525 | duk_debug_write_byte(thr, 0x15); | |
526 | } | |
527 | ||
528 | DUK_INTERNAL void duk_debug_write_undefined(duk_hthread *thr) { | |
529 | duk_debug_write_byte(thr, 0x16); | |
530 | } | |
531 | ||
532 | /* Write signed 32-bit integer. */ | |
533 | DUK_INTERNAL void duk_debug_write_int(duk_hthread *thr, duk_int32_t x) { | |
534 | duk_uint8_t buf[5]; | |
535 | duk_size_t len; | |
536 | ||
537 | DUK_ASSERT(thr != NULL); | |
538 | ||
539 | if (x >= 0 && x <= 0x3fL) { | |
540 | buf[0] = (duk_uint8_t) (0x80 + x); | |
541 | len = 1; | |
542 | } else if (x >= 0 && x <= 0x3fffL) { | |
543 | buf[0] = (duk_uint8_t) (0xc0 + (x >> 8)); | |
544 | buf[1] = (duk_uint8_t) (x & 0xff); | |
545 | len = 2; | |
546 | } else { | |
547 | /* Signed integers always map to 4 bytes now. */ | |
548 | buf[0] = (duk_uint8_t) 0x10; | |
549 | buf[1] = (duk_uint8_t) ((x >> 24) & 0xff); | |
550 | buf[2] = (duk_uint8_t) ((x >> 16) & 0xff); | |
551 | buf[3] = (duk_uint8_t) ((x >> 8) & 0xff); | |
552 | buf[4] = (duk_uint8_t) (x & 0xff); | |
553 | len = 5; | |
554 | } | |
555 | duk_debug_write_bytes(thr, buf, len); | |
556 | } | |
557 | ||
558 | /* Write unsigned 32-bit integer. */ | |
559 | DUK_INTERNAL void duk_debug_write_uint(duk_hthread *thr, duk_uint32_t x) { | |
560 | /* XXX: there's currently no need to support full 32-bit unsigned | |
561 | * integer range in practice. If that becomes necessary, add a new | |
562 | * dvalue type or encode as an IEEE double. | |
563 | */ | |
564 | duk_debug_write_int(thr, (duk_int32_t) x); | |
565 | } | |
566 | ||
567 | DUK_INTERNAL void duk_debug_write_strbuf(duk_hthread *thr, const char *data, duk_size_t length, duk_uint8_t marker_base) { | |
568 | duk_uint8_t buf[5]; | |
569 | duk_size_t buflen; | |
570 | ||
571 | DUK_ASSERT(thr != NULL); | |
572 | DUK_ASSERT(length == 0 || data != NULL); | |
573 | ||
574 | if (length <= 0x1fUL && marker_base == 0x11) { | |
575 | /* For strings, special form for short lengths. */ | |
576 | buf[0] = (duk_uint8_t) (0x60 + length); | |
577 | buflen = 1; | |
578 | } else if (length <= 0xffffUL) { | |
579 | buf[0] = (duk_uint8_t) (marker_base + 1); | |
580 | buf[1] = (duk_uint8_t) (length >> 8); | |
581 | buf[2] = (duk_uint8_t) (length & 0xff); | |
582 | buflen = 3; | |
583 | } else { | |
584 | buf[0] = (duk_uint8_t) marker_base; | |
585 | buf[1] = (duk_uint8_t) (length >> 24); | |
586 | buf[2] = (duk_uint8_t) ((length >> 16) & 0xff); | |
587 | buf[3] = (duk_uint8_t) ((length >> 8) & 0xff); | |
588 | buf[4] = (duk_uint8_t) (length & 0xff); | |
589 | buflen = 5; | |
590 | } | |
591 | ||
592 | duk_debug_write_bytes(thr, (const duk_uint8_t *) buf, buflen); | |
593 | duk_debug_write_bytes(thr, (const duk_uint8_t *) data, length); | |
594 | } | |
595 | ||
596 | DUK_INTERNAL void duk_debug_write_string(duk_hthread *thr, const char *data, duk_size_t length) { | |
597 | duk_debug_write_strbuf(thr, data, length, 0x11); | |
598 | } | |
599 | ||
600 | DUK_INTERNAL void duk_debug_write_cstring(duk_hthread *thr, const char *data) { | |
601 | DUK_ASSERT(thr != NULL); | |
602 | ||
603 | duk_debug_write_string(thr, | |
604 | data, | |
605 | data ? DUK_STRLEN(data) : 0); | |
606 | } | |
607 | ||
608 | DUK_INTERNAL void duk_debug_write_hstring(duk_hthread *thr, duk_hstring *h) { | |
609 | DUK_ASSERT(thr != NULL); | |
610 | ||
611 | /* XXX: differentiate null pointer from empty string? */ | |
612 | duk_debug_write_string(thr, | |
613 | (h != NULL ? (const char *) DUK_HSTRING_GET_DATA(h) : NULL), | |
614 | (h != NULL ? (duk_size_t) DUK_HSTRING_GET_BYTELEN(h) : 0)); | |
615 | } | |
616 | ||
617 | DUK_INTERNAL void duk_debug_write_buffer(duk_hthread *thr, const char *data, duk_size_t length) { | |
618 | duk_debug_write_strbuf(thr, data, length, 0x13); | |
619 | } | |
620 | ||
621 | DUK_INTERNAL void duk_debug_write_hbuffer(duk_hthread *thr, duk_hbuffer *h) { | |
622 | DUK_ASSERT(thr != NULL); | |
623 | ||
624 | duk_debug_write_buffer(thr, | |
625 | (h != NULL ? (const char *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h) : NULL), | |
626 | (h != NULL ? (duk_size_t) DUK_HBUFFER_GET_SIZE(h) : 0)); | |
627 | } | |
628 | ||
629 | DUK_LOCAL void duk__debug_write_pointer_raw(duk_hthread *thr, const void *ptr, duk_uint8_t ibyte) { | |
630 | duk_uint8_t buf[2]; | |
631 | volatile duk__ptr_union pu; | |
632 | ||
633 | DUK_ASSERT(thr != NULL); | |
634 | DUK_ASSERT(sizeof(ptr) >= 1 && sizeof(ptr) <= 16); | |
635 | /* ptr may be NULL */ | |
636 | ||
637 | buf[0] = ibyte; | |
638 | buf[1] = sizeof(pu); | |
639 | duk_debug_write_bytes(thr, buf, 2); | |
640 | pu.p = (void *) ptr; | |
641 | #if defined(DUK_USE_INTEGER_LE) | |
642 | duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu)); | |
643 | #endif | |
644 | duk_debug_write_bytes(thr, (const duk_uint8_t *) &pu.p, (duk_size_t) sizeof(pu)); | |
645 | } | |
646 | ||
647 | DUK_INTERNAL void duk_debug_write_pointer(duk_hthread *thr, const void *ptr) { | |
648 | duk__debug_write_pointer_raw(thr, ptr, 0x1c); | |
649 | } | |
650 | ||
651 | #if defined(DUK_USE_DEBUGGER_DUMPHEAP) | |
652 | DUK_INTERNAL void duk_debug_write_heapptr(duk_hthread *thr, duk_heaphdr *h) { | |
653 | duk__debug_write_pointer_raw(thr, (const void *) h, 0x1e); | |
654 | } | |
655 | #endif /* DUK_USE_DEBUGGER_DUMPHEAP */ | |
656 | ||
657 | DUK_INTERNAL void duk_debug_write_hobject(duk_hthread *thr, duk_hobject *obj) { | |
658 | duk_uint8_t buf[3]; | |
659 | volatile duk__ptr_union pu; | |
660 | ||
661 | DUK_ASSERT(thr != NULL); | |
662 | DUK_ASSERT(sizeof(obj) >= 1 && sizeof(obj) <= 16); | |
663 | DUK_ASSERT(obj != NULL); | |
664 | ||
665 | buf[0] = 0x1b; | |
666 | buf[1] = (duk_uint8_t) DUK_HOBJECT_GET_CLASS_NUMBER(obj); | |
667 | buf[2] = sizeof(pu); | |
668 | duk_debug_write_bytes(thr, buf, 3); | |
669 | pu.p = (void *) obj; | |
670 | #if defined(DUK_USE_INTEGER_LE) | |
671 | duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu)); | |
672 | #endif | |
673 | duk_debug_write_bytes(thr, (const duk_uint8_t *) &pu.p, (duk_size_t) sizeof(pu)); | |
674 | } | |
675 | ||
676 | DUK_INTERNAL void duk_debug_write_tval(duk_hthread *thr, duk_tval *tv) { | |
677 | duk_c_function lf_func; | |
678 | duk_small_uint_t lf_flags; | |
679 | duk_uint8_t buf[4]; | |
680 | duk_double_union du; | |
681 | ||
682 | DUK_ASSERT(thr != NULL); | |
683 | DUK_ASSERT(tv != NULL); | |
684 | ||
685 | switch (DUK_TVAL_GET_TAG(tv)) { | |
686 | case DUK_TAG_UNDEFINED: | |
687 | duk_debug_write_byte(thr, | |
688 | DUK_TVAL_IS_UNDEFINED_UNUSED(tv) ? 0x15 : 0x16); | |
689 | break; | |
690 | case DUK_TAG_NULL: | |
691 | duk_debug_write_byte(thr, 0x17); | |
692 | break; | |
693 | case DUK_TAG_BOOLEAN: | |
694 | DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv) == 0 || | |
695 | DUK_TVAL_GET_BOOLEAN(tv) == 1); | |
696 | duk_debug_write_byte(thr, DUK_TVAL_GET_BOOLEAN(tv) ? 0x18 : 0x19); | |
697 | break; | |
698 | case DUK_TAG_POINTER: | |
699 | duk_debug_write_pointer(thr, (const void *) DUK_TVAL_GET_POINTER(tv)); | |
700 | break; | |
701 | case DUK_TAG_LIGHTFUNC: | |
702 | DUK_TVAL_GET_LIGHTFUNC(tv, lf_func, lf_flags); | |
703 | buf[0] = 0x1d; | |
704 | buf[1] = (duk_uint8_t) (lf_flags >> 8); | |
705 | buf[2] = (duk_uint8_t) (lf_flags & 0xff); | |
706 | buf[3] = sizeof(lf_func); | |
707 | duk_debug_write_bytes(thr, buf, 4); | |
708 | duk_debug_write_bytes(thr, (const duk_uint8_t *) &lf_func, sizeof(lf_func)); | |
709 | break; | |
710 | case DUK_TAG_STRING: | |
711 | duk_debug_write_hstring(thr, DUK_TVAL_GET_STRING(tv)); | |
712 | break; | |
713 | case DUK_TAG_OBJECT: | |
714 | duk_debug_write_hobject(thr, DUK_TVAL_GET_OBJECT(tv)); | |
715 | break; | |
716 | case DUK_TAG_BUFFER: | |
717 | duk_debug_write_hbuffer(thr, DUK_TVAL_GET_BUFFER(tv)); | |
718 | break; | |
719 | #if defined(DUK_USE_FASTINT) | |
720 | case DUK_TAG_FASTINT: | |
721 | #endif | |
722 | default: | |
723 | /* Numbers are normalized to big (network) endian. */ | |
724 | DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); | |
725 | du.d = DUK_TVAL_GET_NUMBER(tv); | |
726 | DUK_DBLUNION_DOUBLE_HTON(&du); | |
727 | ||
728 | duk_debug_write_byte(thr, 0x1a); | |
729 | duk_debug_write_bytes(thr, (const duk_uint8_t *) du.uc, sizeof(du.uc)); | |
730 | } | |
731 | } | |
732 | ||
733 | #if defined(DUK_USE_DEBUGGER_DUMPHEAP) | |
734 | /* Variant for writing duk_tvals so that any heap allocated values are | |
735 | * written out as tagged heap pointers. | |
736 | */ | |
737 | DUK_LOCAL void duk__debug_write_tval_heapptr(duk_hthread *thr, duk_tval *tv) { | |
738 | if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { | |
739 | duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv); | |
740 | duk_debug_write_heapptr(thr, h); | |
741 | } else { | |
742 | duk_debug_write_tval(thr, tv); | |
743 | } | |
744 | } | |
745 | #endif /* DUK_USE_DEBUGGER_DUMPHEAP */ | |
746 | ||
747 | /* | |
748 | * Debug connection message write helpers | |
749 | */ | |
750 | ||
751 | #if 0 /* unused */ | |
752 | DUK_INTERNAL void duk_debug_write_request(duk_hthread *thr, duk_small_uint_t command) { | |
753 | duk_debug_write_byte(thr, DUK_DBG_MARKER_REQUEST); | |
754 | duk_debug_write_int(thr, command); | |
755 | } | |
756 | #endif | |
757 | ||
758 | DUK_INTERNAL void duk_debug_write_reply(duk_hthread *thr) { | |
759 | duk_debug_write_byte(thr, DUK_DBG_MARKER_REPLY); | |
760 | } | |
761 | ||
762 | DUK_INTERNAL void duk_debug_write_error_eom(duk_hthread *thr, duk_small_uint_t err_code, const char *msg) { | |
763 | /* Allow NULL 'msg' */ | |
764 | duk_debug_write_byte(thr, DUK_DBG_MARKER_ERROR); | |
765 | duk_debug_write_int(thr, (duk_int32_t) err_code); | |
766 | duk_debug_write_cstring(thr, msg); | |
767 | duk_debug_write_eom(thr); | |
768 | } | |
769 | ||
770 | DUK_INTERNAL void duk_debug_write_notify(duk_hthread *thr, duk_small_uint_t command) { | |
771 | duk_debug_write_byte(thr, DUK_DBG_MARKER_NOTIFY); | |
772 | duk_debug_write_int(thr, command); | |
773 | } | |
774 | ||
775 | DUK_INTERNAL void duk_debug_write_eom(duk_hthread *thr) { | |
776 | duk_debug_write_byte(thr, DUK_DBG_MARKER_EOM); | |
777 | ||
778 | /* As an initial implementation, write flush after every EOM (and the | |
779 | * version identifier). A better implementation would flush only when | |
780 | * Duktape is finished processing messages so that a flush only happens | |
781 | * after all outbound messages are finished on that occasion. | |
782 | */ | |
783 | duk_debug_write_flush(thr); | |
784 | } | |
785 | ||
786 | /* | |
787 | * Status message and helpers | |
788 | */ | |
789 | ||
790 | DUK_INTERNAL duk_uint_fast32_t duk_debug_curr_line(duk_hthread *thr) { | |
791 | duk_context *ctx = (duk_context *) thr; | |
792 | duk_activation *act; | |
793 | duk_uint_fast32_t line; | |
794 | duk_uint_fast32_t pc; | |
795 | ||
796 | if (thr->callstack_top == 0) { | |
797 | return 0; | |
798 | } | |
799 | act = thr->callstack + thr->callstack_top - 1; | |
800 | ||
801 | /* We're conceptually between two opcodes; act->pc indicates the next | |
802 | * instruction to be executed. This is usually the correct pc/line to | |
803 | * indicate in Status. (For the 'debugger' statement this now reports | |
804 | * the pc/line after the debugger statement because the debugger opcode | |
805 | * has already been executed.) | |
806 | */ | |
807 | ||
808 | pc = duk_hthread_get_act_curr_pc(thr, act); | |
809 | ||
810 | /* XXX: this should be optimized to be a raw query and avoid valstack | |
811 | * operations if possible. | |
812 | */ | |
813 | duk_push_hobject(ctx, act->func); | |
814 | line = duk_hobject_pc2line_query(ctx, -1, pc); | |
815 | duk_pop(ctx); | |
816 | return line; | |
817 | } | |
818 | ||
819 | DUK_INTERNAL void duk_debug_send_status(duk_hthread *thr) { | |
820 | duk_context *ctx = (duk_context *) thr; | |
821 | duk_activation *act; | |
822 | ||
823 | duk_debug_write_notify(thr, DUK_DBG_CMD_STATUS); | |
824 | duk_debug_write_int(thr, thr->heap->dbg_paused); | |
825 | ||
826 | DUK_ASSERT_DISABLE(thr->callstack_top >= 0); /* unsigned */ | |
827 | if (thr->callstack_top == 0) { | |
828 | duk_debug_write_undefined(thr); | |
829 | duk_debug_write_undefined(thr); | |
830 | duk_debug_write_int(thr, 0); | |
831 | duk_debug_write_int(thr, 0); | |
832 | } else { | |
833 | act = thr->callstack + thr->callstack_top - 1; | |
834 | duk_push_hobject(ctx, act->func); | |
835 | duk_get_prop_string(ctx, -1, "fileName"); | |
836 | duk_safe_to_string(ctx, -1); | |
837 | duk_debug_write_hstring(thr, duk_require_hstring(ctx, -1)); | |
838 | duk_get_prop_string(ctx, -2, "name"); | |
839 | duk_safe_to_string(ctx, -1); | |
840 | duk_debug_write_hstring(thr, duk_require_hstring(ctx, -1)); | |
841 | duk_pop_3(ctx); | |
842 | /* Report next pc/line to be executed. */ | |
843 | duk_debug_write_uint(thr, (duk_uint32_t) duk_debug_curr_line(thr)); | |
844 | duk_debug_write_uint(thr, (duk_uint32_t) duk_hthread_get_act_curr_pc(thr, act)); | |
845 | } | |
846 | ||
847 | duk_debug_write_eom(thr); | |
848 | } | |
849 | ||
850 | /* | |
851 | * Debug message processing | |
852 | */ | |
853 | ||
854 | /* Skip dvalue. */ | |
855 | DUK_LOCAL duk_bool_t duk__debug_skip_dvalue(duk_hthread *thr) { | |
856 | duk_uint8_t x; | |
857 | duk_uint32_t len; | |
858 | ||
859 | x = duk_debug_read_byte(thr); | |
860 | ||
861 | if (x >= 0xc0) { | |
862 | duk_debug_skip_byte(thr); | |
863 | return 0; | |
864 | } | |
865 | if (x >= 0x80) { | |
866 | return 0; | |
867 | } | |
868 | if (x >= 0x60) { | |
869 | duk_debug_skip_bytes(thr, x - 0x60); | |
870 | return 0; | |
871 | } | |
872 | switch(x) { | |
873 | case 0x00: | |
874 | return 1; /* Return 1: got EOM */ | |
875 | case 0x01: | |
876 | case 0x02: | |
877 | case 0x03: | |
878 | case 0x04: | |
879 | break; | |
880 | case 0x10: | |
881 | (void) duk__debug_read_uint32_raw(thr); | |
882 | break; | |
883 | case 0x11: | |
884 | case 0x13: | |
885 | len = duk__debug_read_uint32_raw(thr); | |
886 | duk_debug_skip_bytes(thr, len); | |
887 | break; | |
888 | case 0x12: | |
889 | case 0x14: | |
890 | len = duk__debug_read_uint16_raw(thr); | |
891 | duk_debug_skip_bytes(thr, len); | |
892 | break; | |
893 | case 0x15: | |
894 | case 0x16: | |
895 | case 0x17: | |
896 | case 0x18: | |
897 | case 0x19: | |
898 | break; | |
899 | case 0x1a: | |
900 | duk_debug_skip_bytes(thr, 8); | |
901 | break; | |
902 | case 0x1b: | |
903 | duk_debug_skip_byte(thr); | |
904 | len = duk_debug_read_byte(thr); | |
905 | duk_debug_skip_bytes(thr, len); | |
906 | break; | |
907 | case 0x1c: | |
908 | len = duk_debug_read_byte(thr); | |
909 | duk_debug_skip_bytes(thr, len); | |
910 | break; | |
911 | case 0x1d: | |
912 | duk_debug_skip_bytes(thr, 2); | |
913 | len = duk_debug_read_byte(thr); | |
914 | duk_debug_skip_bytes(thr, len); | |
915 | break; | |
916 | default: | |
917 | goto fail; | |
918 | } | |
919 | ||
920 | return 0; | |
921 | ||
922 | fail: | |
923 | DUK__SET_CONN_BROKEN(thr); | |
924 | return 1; /* Pretend like we got EOM */ | |
925 | } | |
926 | ||
927 | /* Skip dvalues to EOM. */ | |
928 | DUK_LOCAL void duk__debug_skip_to_eom(duk_hthread *thr) { | |
929 | for (;;) { | |
930 | if (duk__debug_skip_dvalue(thr)) { | |
931 | break; | |
932 | } | |
933 | } | |
934 | } | |
935 | ||
936 | /* | |
937 | * Process incoming debug requests | |
938 | */ | |
939 | ||
940 | DUK_LOCAL void duk__debug_handle_basic_info(duk_hthread *thr, duk_heap *heap) { | |
941 | DUK_UNREF(heap); | |
942 | DUK_D(DUK_DPRINT("debug command version")); | |
943 | ||
944 | duk_debug_write_reply(thr); | |
945 | duk_debug_write_int(thr, DUK_VERSION); | |
946 | duk_debug_write_cstring(thr, DUK_GIT_DESCRIBE); | |
947 | duk_debug_write_cstring(thr, DUK_USE_TARGET_INFO); | |
948 | #if defined(DUK_USE_DOUBLE_LE) | |
949 | duk_debug_write_int(thr, 1); | |
950 | #elif defined(DUK_USE_DOUBLE_ME) | |
951 | duk_debug_write_int(thr, 2); | |
952 | #elif defined(DUK_USE_DOUBLE_BE) | |
953 | duk_debug_write_int(thr, 3); | |
954 | #else | |
955 | duk_debug_write_int(thr, 0); | |
956 | #endif | |
957 | duk_debug_write_eom(thr); | |
958 | } | |
959 | ||
960 | DUK_LOCAL void duk__debug_handle_trigger_status(duk_hthread *thr, duk_heap *heap) { | |
961 | DUK_UNREF(heap); | |
962 | DUK_D(DUK_DPRINT("debug command triggerstatus")); | |
963 | ||
964 | duk_debug_write_reply(thr); | |
965 | duk_debug_write_eom(thr); | |
966 | heap->dbg_state_dirty = 1; | |
967 | } | |
968 | ||
969 | DUK_LOCAL void duk__debug_handle_pause(duk_hthread *thr, duk_heap *heap) { | |
970 | DUK_D(DUK_DPRINT("debug command pause")); | |
971 | ||
972 | DUK_HEAP_SET_PAUSED(heap); | |
973 | duk_debug_write_reply(thr); | |
974 | duk_debug_write_eom(thr); | |
975 | } | |
976 | ||
977 | DUK_LOCAL void duk__debug_handle_resume(duk_hthread *thr, duk_heap *heap) { | |
978 | DUK_D(DUK_DPRINT("debug command resume")); | |
979 | ||
980 | DUK_HEAP_CLEAR_PAUSED(heap); | |
981 | duk_debug_write_reply(thr); | |
982 | duk_debug_write_eom(thr); | |
983 | } | |
984 | ||
985 | DUK_LOCAL void duk__debug_handle_step(duk_hthread *thr, duk_heap *heap, duk_int32_t cmd) { | |
986 | duk_small_uint_t step_type; | |
987 | duk_uint_fast32_t line; | |
988 | ||
989 | if (cmd == DUK_DBG_CMD_STEPINTO) { | |
990 | step_type = DUK_STEP_TYPE_INTO; | |
991 | } else if (cmd == DUK_DBG_CMD_STEPOVER) { | |
992 | step_type = DUK_STEP_TYPE_OVER; | |
993 | } else { | |
994 | DUK_ASSERT(cmd == DUK_DBG_CMD_STEPOUT); | |
995 | step_type = DUK_STEP_TYPE_OUT; | |
996 | } | |
997 | ||
998 | DUK_D(DUK_DPRINT("debug command stepinto/stepover/stepout: %d", (int) cmd)); | |
999 | line = duk_debug_curr_line(thr); | |
1000 | if (line > 0) { | |
1001 | heap->dbg_paused = 0; | |
1002 | heap->dbg_step_type = step_type; | |
1003 | heap->dbg_step_thread = thr; | |
1004 | heap->dbg_step_csindex = thr->callstack_top - 1; | |
1005 | heap->dbg_step_startline = line; | |
1006 | heap->dbg_state_dirty = 1; | |
1007 | } else { | |
1008 | DUK_D(DUK_DPRINT("cannot determine current line, stepinto/stepover/stepout ignored")); | |
1009 | } | |
1010 | duk_debug_write_reply(thr); | |
1011 | duk_debug_write_eom(thr); | |
1012 | } | |
1013 | ||
1014 | DUK_LOCAL void duk__debug_handle_list_break(duk_hthread *thr, duk_heap *heap) { | |
1015 | duk_small_int_t i; | |
1016 | ||
1017 | DUK_D(DUK_DPRINT("debug command listbreak")); | |
1018 | duk_debug_write_reply(thr); | |
1019 | for (i = 0; i < (duk_small_int_t) heap->dbg_breakpoint_count; i++) { | |
1020 | duk_debug_write_hstring(thr, heap->dbg_breakpoints[i].filename); | |
1021 | duk_debug_write_uint(thr, (duk_uint32_t) heap->dbg_breakpoints[i].line); | |
1022 | } | |
1023 | duk_debug_write_eom(thr); | |
1024 | } | |
1025 | ||
1026 | DUK_LOCAL void duk__debug_handle_add_break(duk_hthread *thr, duk_heap *heap) { | |
1027 | duk_context *ctx = (duk_context *) thr; | |
1028 | duk_hstring *filename; | |
1029 | duk_uint32_t linenumber; | |
1030 | duk_small_int_t idx; | |
1031 | ||
1032 | DUK_UNREF(heap); | |
1033 | ||
1034 | filename = duk_debug_read_hstring(thr); | |
1035 | linenumber = (duk_uint32_t) duk_debug_read_int(thr); | |
1036 | DUK_D(DUK_DPRINT("debug command addbreak: %!O:%ld", (duk_hobject *) filename, (long) linenumber)); | |
1037 | idx = duk_debug_add_breakpoint(thr, filename, linenumber); | |
1038 | if (idx >= 0) { | |
1039 | duk_debug_write_reply(thr); | |
1040 | duk_debug_write_int(thr, (duk_int32_t) idx); | |
1041 | duk_debug_write_eom(thr); | |
1042 | } else { | |
1043 | duk_debug_write_error_eom(thr, DUK_DBG_ERR_TOOMANY, "no space for breakpoint"); | |
1044 | } | |
1045 | duk_pop(ctx); | |
1046 | } | |
1047 | ||
1048 | DUK_LOCAL void duk__debug_handle_del_break(duk_hthread *thr, duk_heap *heap) { | |
1049 | duk_small_uint_t idx; | |
1050 | ||
1051 | DUK_UNREF(heap); | |
1052 | ||
1053 | DUK_D(DUK_DPRINT("debug command delbreak")); | |
1054 | idx = (duk_small_uint_t) duk_debug_read_int(thr); | |
1055 | if (duk_debug_remove_breakpoint(thr, idx)) { | |
1056 | duk_debug_write_reply(thr); | |
1057 | duk_debug_write_eom(thr); | |
1058 | } else { | |
1059 | duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid breakpoint index"); | |
1060 | } | |
1061 | } | |
1062 | ||
1063 | DUK_LOCAL void duk__debug_handle_get_var(duk_hthread *thr, duk_heap *heap) { | |
1064 | duk_context *ctx = (duk_context *) thr; | |
1065 | duk_hstring *str; | |
1066 | duk_bool_t rc; | |
1067 | ||
1068 | DUK_UNREF(heap); | |
1069 | DUK_D(DUK_DPRINT("debug command getvar")); | |
1070 | ||
1071 | str = duk_debug_read_hstring(thr); /* push to stack */ | |
1072 | DUK_ASSERT(str != NULL); | |
1073 | ||
1074 | if (thr->callstack_top > 0) { | |
1075 | rc = duk_js_getvar_activation(thr, | |
1076 | thr->callstack + thr->callstack_top - 1, | |
1077 | str, | |
1078 | 0); | |
1079 | } else { | |
1080 | /* No activation, no variable access. Could also pretend | |
1081 | * we're in the global program context and read stuff off | |
1082 | * the global object. | |
1083 | */ | |
1084 | DUK_D(DUK_DPRINT("callstack empty, no activation -> ignore getvar")); | |
1085 | rc = 0; | |
1086 | } | |
1087 | ||
1088 | duk_debug_write_reply(thr); | |
1089 | if (rc) { | |
1090 | duk_debug_write_int(thr, 1); | |
1091 | duk_debug_write_tval(thr, duk_require_tval(ctx, -2)); | |
1092 | duk_pop_2(ctx); | |
1093 | } else { | |
1094 | duk_debug_write_int(thr, 0); | |
1095 | duk_debug_write_unused(thr); | |
1096 | } | |
1097 | duk_pop(ctx); | |
1098 | duk_debug_write_eom(thr); | |
1099 | } | |
1100 | ||
1101 | DUK_LOCAL void duk__debug_handle_put_var(duk_hthread *thr, duk_heap *heap) { | |
1102 | duk_context *ctx = (duk_context *) thr; | |
1103 | duk_hstring *str; | |
1104 | duk_tval *tv; | |
1105 | ||
1106 | DUK_UNREF(heap); | |
1107 | DUK_D(DUK_DPRINT("debug command putvar")); | |
1108 | ||
1109 | str = duk_debug_read_hstring(thr); /* push to stack */ | |
1110 | DUK_ASSERT(str != NULL); | |
1111 | duk_debug_read_tval(thr); /* push to stack */ | |
1112 | tv = duk_require_tval(ctx, -1); | |
1113 | ||
1114 | if (thr->callstack_top > 0) { | |
1115 | duk_js_putvar_activation(thr, | |
1116 | thr->callstack + thr->callstack_top - 1, | |
1117 | str, | |
1118 | tv, | |
1119 | 0); | |
1120 | } else { | |
1121 | DUK_D(DUK_DPRINT("callstack empty, no activation -> ignore putvar")); | |
1122 | } | |
1123 | duk_pop_2(ctx); | |
1124 | ||
1125 | /* XXX: Current putvar implementation doesn't have a success flag, | |
1126 | * add one and send to debug client? | |
1127 | */ | |
1128 | duk_debug_write_reply(thr); | |
1129 | duk_debug_write_eom(thr); | |
1130 | } | |
1131 | ||
1132 | DUK_LOCAL void duk__debug_handle_get_call_stack(duk_hthread *thr, duk_heap *heap) { | |
1133 | duk_context *ctx = (duk_context *) thr; | |
1134 | duk_hthread *curr_thr = thr; | |
1135 | duk_activation *curr_act; | |
1136 | duk_uint_fast32_t pc; | |
1137 | duk_uint_fast32_t line; | |
1138 | duk_size_t i; | |
1139 | ||
1140 | DUK_UNREF(heap); | |
1141 | ||
1142 | duk_debug_write_reply(thr); | |
1143 | while (curr_thr != NULL) { | |
1144 | i = curr_thr->callstack_top; | |
1145 | while (i > 0) { | |
1146 | i--; | |
1147 | curr_act = curr_thr->callstack + i; | |
1148 | ||
1149 | /* PC/line semantics here are: | |
1150 | * - For callstack top we're conceptually between two | |
1151 | * opcodes and current PC indicates next line to | |
1152 | * execute, so report that (matches Status). | |
1153 | * - For other activations we're conceptually still | |
1154 | * executing the instruction at PC-1, so report that | |
1155 | * (matches error stacktrace behavior). | |
1156 | * - See: https://github.com/svaarala/duktape/issues/281 | |
1157 | */ | |
1158 | ||
1159 | /* XXX: optimize to use direct reads, i.e. avoid | |
1160 | * value stack operations. | |
1161 | */ | |
1162 | duk_push_tval(ctx, &curr_act->tv_func); | |
1163 | duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME); | |
1164 | duk_safe_to_string(ctx, -1); | |
1165 | duk_debug_write_hstring(thr, duk_get_hstring(ctx, -1)); | |
1166 | duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME); | |
1167 | duk_safe_to_string(ctx, -1); | |
1168 | duk_debug_write_hstring(thr, duk_get_hstring(ctx, -1)); | |
1169 | pc = duk_hthread_get_act_curr_pc(thr, curr_act); | |
1170 | if (i != curr_thr->callstack_top - 1 && pc > 0) { | |
1171 | pc--; | |
1172 | } | |
1173 | line = duk_hobject_pc2line_query(ctx, -3, pc); | |
1174 | duk_debug_write_uint(thr, (duk_uint32_t) line); | |
1175 | duk_debug_write_uint(thr, (duk_uint32_t) pc); | |
1176 | duk_pop_3(ctx); | |
1177 | } | |
1178 | curr_thr = curr_thr->resumer; | |
1179 | } | |
1180 | duk_debug_write_eom(thr); | |
1181 | } | |
1182 | ||
1183 | DUK_LOCAL void duk__debug_handle_get_locals(duk_hthread *thr, duk_heap *heap) { | |
1184 | duk_context *ctx = (duk_context *) thr; | |
1185 | duk_activation *curr_act; | |
1186 | duk_hstring *varname; | |
1187 | ||
1188 | DUK_UNREF(heap); | |
1189 | ||
1190 | duk_debug_write_reply(thr); | |
1191 | if (thr->callstack_top == 0) { | |
1192 | goto callstack_empty; | |
1193 | } | |
1194 | curr_act = thr->callstack + thr->callstack_top - 1; | |
1195 | ||
1196 | /* XXX: several nice-to-have improvements here: | |
1197 | * - Use direct reads avoiding value stack operations | |
1198 | * - Avoid triggering getters, indicate getter values to debug client | |
1199 | * - If side effects are possible, add error catching | |
1200 | */ | |
1201 | ||
1202 | duk_push_tval(ctx, &curr_act->tv_func); | |
1203 | duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VARMAP); | |
1204 | if (duk_is_object(ctx, -1)) { | |
1205 | duk_enum(ctx, -1, 0 /*enum_flags*/); | |
1206 | while (duk_next(ctx, -1 /*enum_index*/, 0 /*get_value*/)) { | |
1207 | varname = duk_get_hstring(ctx, -1); | |
1208 | DUK_ASSERT(varname != NULL); | |
1209 | ||
1210 | duk_js_getvar_activation(thr, curr_act, varname, 0 /*throw_flag*/); | |
1211 | /* [ ... func varmap enum key value this ] */ | |
1212 | duk_debug_write_hstring(thr, duk_get_hstring(ctx, -3)); | |
1213 | duk_debug_write_tval(thr, duk_get_tval(ctx, -2)); | |
1214 | duk_pop_3(ctx); /* -> [ ... func varmap enum ] */ | |
1215 | } | |
1216 | duk_pop(ctx); | |
1217 | } else { | |
1218 | DUK_D(DUK_DPRINT("varmap is not an object in GetLocals, ignore")); | |
1219 | } | |
1220 | duk_pop_2(ctx); | |
1221 | ||
1222 | callstack_empty: | |
1223 | duk_debug_write_eom(thr); | |
1224 | } | |
1225 | ||
1226 | DUK_LOCAL void duk__debug_handle_eval(duk_hthread *thr, duk_heap *heap) { | |
1227 | duk_context *ctx = (duk_context *) thr; | |
1228 | ||
1229 | duk_small_uint_t call_flags; | |
1230 | duk_int_t call_ret; | |
1231 | duk_small_int_t eval_err; | |
1232 | #if defined(DUK_USE_ASSERTIONS) | |
1233 | duk_idx_t entry_top; | |
1234 | #endif | |
1235 | ||
1236 | DUK_UNREF(heap); | |
1237 | ||
1238 | DUK_D(DUK_DPRINT("debug command eval")); | |
1239 | ||
1240 | /* The eval code must be executed within the current (topmost) | |
1241 | * activation. For now, use global object eval() function, with | |
1242 | * the eval considered a 'direct call to eval'. | |
1243 | */ | |
1244 | ||
1245 | #if defined(DUK_USE_ASSERTIONS) | |
1246 | entry_top = duk_get_top(ctx); | |
1247 | #endif | |
1248 | ||
1249 | duk_push_c_function(ctx, duk_bi_global_object_eval, 1 /*nargs*/); | |
1250 | duk_push_undefined(ctx); /* 'this' binding shouldn't matter here */ | |
1251 | (void) duk_debug_read_hstring(thr); | |
1252 | ||
1253 | /* [ ... eval "eval" eval_input ] */ | |
1254 | ||
1255 | call_flags = DUK_CALL_FLAG_PROTECTED; | |
1256 | if (thr->callstack_top >= 1) { | |
1257 | duk_activation *act; | |
1258 | duk_hobject *fun; | |
1259 | ||
1260 | act = thr->callstack + thr->callstack_top - 1; | |
1261 | fun = DUK_ACT_GET_FUNC(act); | |
1262 | if (fun && DUK_HOBJECT_IS_COMPILEDFUNCTION(fun)) { | |
1263 | /* Direct eval requires that there's a current | |
1264 | * activation and it is an Ecmascript function. | |
1265 | * When Eval is executed from e.g. cooperate API | |
1266 | * call we'll need to an indirect eval instead. | |
1267 | */ | |
1268 | call_flags |= DUK_CALL_FLAG_DIRECT_EVAL; | |
1269 | } | |
1270 | } | |
1271 | ||
1272 | call_ret = duk_handle_call(thr, 1 /*num_stack_args*/, call_flags); | |
1273 | ||
1274 | if (call_ret == DUK_EXEC_SUCCESS) { | |
1275 | eval_err = 0; | |
1276 | /* Use result value as is. */ | |
1277 | } else { | |
1278 | /* For errors a string coerced result is most informative | |
1279 | * right now, as the debug client doesn't have the capability | |
1280 | * to traverse the error object. | |
1281 | */ | |
1282 | eval_err = 1; | |
1283 | duk_safe_to_string(ctx, -1); | |
1284 | } | |
1285 | ||
1286 | /* [ ... result ] */ | |
1287 | ||
1288 | duk_debug_write_reply(thr); | |
1289 | duk_debug_write_int(thr, (duk_int32_t) eval_err); | |
1290 | duk_debug_write_tval(thr, duk_require_tval(ctx, -1)); | |
1291 | duk_debug_write_eom(thr); | |
1292 | duk_pop(ctx); | |
1293 | ||
1294 | DUK_ASSERT(duk_get_top(ctx) == entry_top); | |
1295 | } | |
1296 | ||
1297 | DUK_LOCAL void duk__debug_handle_detach(duk_hthread *thr, duk_heap *heap) { | |
1298 | DUK_UNREF(heap); | |
1299 | DUK_D(DUK_DPRINT("debug command detach")); | |
1300 | ||
1301 | duk_debug_write_reply(thr); | |
1302 | duk_debug_write_eom(thr); | |
1303 | ||
1304 | DUK_D(DUK_DPRINT("debug connection detached, mark broken")); | |
1305 | DUK__SET_CONN_BROKEN(thr); | |
1306 | } | |
1307 | ||
1308 | #if defined(DUK_USE_DEBUGGER_DUMPHEAP) | |
1309 | DUK_LOCAL void duk__debug_dump_heaphdr(duk_hthread *thr, duk_heap *heap, duk_heaphdr *hdr) { | |
1310 | DUK_UNREF(heap); | |
1311 | ||
1312 | duk_debug_write_heapptr(thr, hdr); | |
1313 | duk_debug_write_uint(thr, (duk_uint32_t) DUK_HEAPHDR_GET_TYPE(hdr)); | |
1314 | duk_debug_write_uint(thr, (duk_uint32_t) DUK_HEAPHDR_GET_FLAGS_RAW(hdr)); | |
1315 | #if defined(DUK_USE_REFERENCE_COUNTING) | |
1316 | duk_debug_write_uint(thr, (duk_uint32_t) DUK_HEAPHDR_GET_REFCOUNT(hdr)); | |
1317 | #else | |
1318 | duk_debug_write_int(thr, (duk_int32_t) -1); | |
1319 | #endif | |
1320 | ||
1321 | switch (DUK_HEAPHDR_GET_TYPE(hdr)) { | |
1322 | case DUK_HTYPE_STRING: { | |
1323 | duk_hstring *h = (duk_hstring *) hdr; | |
1324 | ||
1325 | duk_debug_write_uint(thr, (duk_int32_t) DUK_HSTRING_GET_BYTELEN(h)); | |
1326 | duk_debug_write_uint(thr, (duk_int32_t) DUK_HSTRING_GET_CHARLEN(h)); | |
1327 | duk_debug_write_uint(thr, (duk_int32_t) DUK_HSTRING_GET_HASH(h)); | |
1328 | duk_debug_write_hstring(thr, h); | |
1329 | break; | |
1330 | } | |
1331 | case DUK_HTYPE_OBJECT: { | |
1332 | duk_hobject *h = (duk_hobject *) hdr; | |
1333 | duk_hstring *k; | |
1334 | duk_uint_fast32_t i; | |
1335 | ||
1336 | duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_CLASS_NUMBER(h)); | |
1337 | duk_debug_write_heapptr(thr, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(heap, h)); | |
1338 | duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_ESIZE(h)); | |
1339 | duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_ENEXT(h)); | |
1340 | duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_ASIZE(h)); | |
1341 | duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_HSIZE(h)); | |
1342 | ||
1343 | for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) { | |
1344 | duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_E_GET_FLAGS(heap, h, i)); | |
1345 | k = DUK_HOBJECT_E_GET_KEY(heap, h, i); | |
1346 | duk_debug_write_heapptr(thr, (duk_heaphdr *) k); | |
1347 | if (k == NULL) { | |
1348 | duk_debug_write_int(thr, 0); /* isAccessor */ | |
1349 | duk_debug_write_unused(thr); | |
1350 | continue; | |
1351 | } | |
1352 | if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, h, i)) { | |
1353 | duk_debug_write_int(thr, 1); /* isAccessor */ | |
1354 | duk_debug_write_heapptr(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.get); | |
1355 | duk_debug_write_heapptr(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.set); | |
1356 | } else { | |
1357 | duk_debug_write_int(thr, 0); /* isAccessor */ | |
1358 | ||
1359 | duk__debug_write_tval_heapptr(thr, &DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->v); | |
1360 | } | |
1361 | } | |
1362 | ||
1363 | for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) { | |
1364 | /* Note: array dump will include elements beyond | |
1365 | * 'length'. | |
1366 | */ | |
1367 | duk__debug_write_tval_heapptr(thr, DUK_HOBJECT_A_GET_VALUE_PTR(heap, h, i)); | |
1368 | } | |
1369 | break; | |
1370 | } | |
1371 | case DUK_HTYPE_BUFFER: { | |
1372 | duk_hbuffer *h = (duk_hbuffer *) hdr; | |
1373 | ||
1374 | duk_debug_write_uint(thr, (duk_uint32_t) DUK_HBUFFER_GET_SIZE(h)); | |
1375 | duk_debug_write_buffer(thr, (const char *) DUK_HBUFFER_GET_DATA_PTR(heap, h), (duk_size_t) DUK_HBUFFER_GET_SIZE(h)); | |
1376 | break; | |
1377 | } | |
1378 | default: { | |
1379 | DUK_D(DUK_DPRINT("invalid htype: %d", (int) DUK_HEAPHDR_GET_TYPE(hdr))); | |
1380 | } | |
1381 | } | |
1382 | } | |
1383 | ||
1384 | DUK_LOCAL void duk__debug_dump_heap_allocated(duk_hthread *thr, duk_heap *heap) { | |
1385 | duk_heaphdr *hdr; | |
1386 | ||
1387 | hdr = heap->heap_allocated; | |
1388 | while (hdr != NULL) { | |
1389 | duk__debug_dump_heaphdr(thr, heap, hdr); | |
1390 | hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); | |
1391 | } | |
1392 | } | |
1393 | ||
1394 | #if defined(DUK_USE_STRTAB_CHAIN) | |
1395 | DUK_LOCAL void duk__debug_dump_strtab_chain(duk_hthread *thr, duk_heap *heap) { | |
1396 | duk_uint_fast32_t i, j; | |
1397 | duk_strtab_entry *e; | |
1398 | #if defined(DUK_USE_HEAPPTR16) | |
1399 | duk_uint16_t *lst; | |
1400 | #else | |
1401 | duk_hstring **lst; | |
1402 | #endif | |
1403 | duk_hstring *h; | |
1404 | ||
1405 | for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { | |
1406 | e = heap->strtable + i; | |
1407 | if (e->listlen > 0) { | |
1408 | #if defined(DUK_USE_HEAPPTR16) | |
1409 | lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); | |
1410 | #else | |
1411 | lst = e->u.strlist; | |
1412 | #endif | |
1413 | DUK_ASSERT(lst != NULL); | |
1414 | ||
1415 | for (j = 0; j < e->listlen; j++) { | |
1416 | #if defined(DUK_USE_HEAPPTR16) | |
1417 | h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, lst[j]); | |
1418 | #else | |
1419 | h = lst[j]; | |
1420 | #endif | |
1421 | if (h != NULL) { | |
1422 | duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h); | |
1423 | } | |
1424 | } | |
1425 | } else { | |
1426 | #if defined(DUK_USE_HEAPPTR16) | |
1427 | h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.str16); | |
1428 | #else | |
1429 | h = e->u.str; | |
1430 | #endif | |
1431 | if (h != NULL) { | |
1432 | duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h); | |
1433 | } | |
1434 | } | |
1435 | } | |
1436 | } | |
1437 | #endif /* DUK_USE_STRTAB_CHAIN */ | |
1438 | ||
1439 | #if defined(DUK_USE_STRTAB_PROBE) | |
1440 | DUK_LOCAL void duk__debug_dump_strtab_probe(duk_hthread *thr, duk_heap *heap) { | |
1441 | duk_uint32_t i; | |
1442 | duk_hstring *h; | |
1443 | ||
1444 | for (i = 0; i < heap->st_size; i++) { | |
1445 | #if defined(DUK_USE_HEAPPTR16) | |
1446 | h = DUK_USE_HEAPPTR_DEC16(heap->strtable16[i]); | |
1447 | #else | |
1448 | h = heap->strtable[i]; | |
1449 | #endif | |
1450 | if (h == NULL || h == DUK_STRTAB_DELETED_MARKER(heap)) { | |
1451 | continue; | |
1452 | } | |
1453 | ||
1454 | duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h); | |
1455 | } | |
1456 | } | |
1457 | #endif /* DUK_USE_STRTAB_PROBE */ | |
1458 | ||
1459 | DUK_LOCAL void duk__debug_handle_dump_heap(duk_hthread *thr, duk_heap *heap) { | |
1460 | DUK_D(DUK_DPRINT("debug command dumpheap")); | |
1461 | ||
1462 | duk_debug_write_reply(thr); | |
1463 | duk__debug_dump_heap_allocated(thr, heap); | |
1464 | #if defined(DUK_USE_STRTAB_CHAIN) | |
1465 | duk__debug_dump_strtab_chain(thr, heap); | |
1466 | #endif | |
1467 | #if defined(DUK_USE_STRTAB_PROBE) | |
1468 | duk__debug_dump_strtab_probe(thr, heap); | |
1469 | #endif | |
1470 | duk_debug_write_eom(thr); | |
1471 | } | |
1472 | #endif /* DUK_USE_DEBUGGER_DUMPHEAP */ | |
1473 | ||
1474 | DUK_LOCAL void duk__debug_handle_get_bytecode(duk_hthread *thr, duk_heap *heap) { | |
1475 | duk_activation *act; | |
1476 | duk_hcompiledfunction *fun; | |
1477 | duk_size_t i, n; | |
1478 | duk_tval *tv; | |
1479 | duk_hobject **fn; | |
1480 | ||
1481 | DUK_UNREF(heap); | |
1482 | ||
1483 | DUK_D(DUK_DPRINT("debug command getbytecode")); | |
1484 | ||
1485 | duk_debug_write_reply(thr); | |
1486 | if (thr->callstack_top == 0) { | |
1487 | fun = NULL; | |
1488 | } else { | |
1489 | act = thr->callstack + thr->callstack_top - 1; | |
1490 | fun = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act); | |
1491 | if (!DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) fun)) { | |
1492 | fun = NULL; | |
1493 | } | |
1494 | } | |
1495 | DUK_ASSERT(fun == NULL || DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) fun)); | |
1496 | ||
1497 | if (fun) { | |
1498 | n = DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(heap, fun); | |
1499 | duk_debug_write_int(thr, (int) n); | |
1500 | tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(heap, fun); | |
1501 | for (i = 0; i < n; i++) { | |
1502 | duk_debug_write_tval(thr, tv); | |
1503 | tv++; | |
1504 | } | |
1505 | ||
1506 | n = DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(heap, fun); | |
1507 | duk_debug_write_int(thr, (int) n); | |
1508 | fn = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(heap, fun); | |
1509 | for (i = 0; i < n; i++) { | |
1510 | duk_debug_write_hobject(thr, *fn); | |
1511 | fn++; | |
1512 | } | |
1513 | ||
1514 | duk_debug_write_string(thr, | |
1515 | (const char *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(heap, fun), | |
1516 | (duk_size_t) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(heap, fun)); | |
1517 | } else { | |
1518 | duk_debug_write_int(thr, 0); | |
1519 | duk_debug_write_int(thr, 0); | |
1520 | duk_debug_write_cstring(thr, ""); | |
1521 | } | |
1522 | duk_debug_write_eom(thr); | |
1523 | } | |
1524 | ||
1525 | DUK_LOCAL void duk__debug_process_message(duk_hthread *thr) { | |
1526 | duk_context *ctx = (duk_context *) thr; | |
1527 | duk_heap *heap; | |
1528 | duk_uint8_t x; | |
1529 | duk_int32_t cmd; | |
1530 | ||
1531 | DUK_ASSERT(thr != NULL); | |
1532 | heap = thr->heap; | |
1533 | DUK_ASSERT(heap != NULL); | |
1534 | DUK_UNREF(ctx); | |
1535 | ||
1536 | x = duk_debug_read_byte(thr); | |
1537 | switch (x) { | |
1538 | case DUK_DBG_MARKER_REQUEST: { | |
1539 | cmd = duk_debug_read_int(thr); | |
1540 | switch (cmd) { | |
1541 | case DUK_DBG_CMD_BASICINFO: { | |
1542 | duk__debug_handle_basic_info(thr, heap); | |
1543 | break; | |
1544 | } | |
1545 | case DUK_DBG_CMD_TRIGGERSTATUS: { | |
1546 | duk__debug_handle_trigger_status(thr, heap); | |
1547 | break; | |
1548 | } | |
1549 | case DUK_DBG_CMD_PAUSE: { | |
1550 | duk__debug_handle_pause(thr, heap); | |
1551 | break; | |
1552 | } | |
1553 | case DUK_DBG_CMD_RESUME: { | |
1554 | duk__debug_handle_resume(thr, heap); | |
1555 | break; | |
1556 | } | |
1557 | case DUK_DBG_CMD_STEPINTO: | |
1558 | case DUK_DBG_CMD_STEPOVER: | |
1559 | case DUK_DBG_CMD_STEPOUT: { | |
1560 | duk__debug_handle_step(thr, heap, cmd); | |
1561 | break; | |
1562 | } | |
1563 | case DUK_DBG_CMD_LISTBREAK: { | |
1564 | duk__debug_handle_list_break(thr, heap); | |
1565 | break; | |
1566 | } | |
1567 | case DUK_DBG_CMD_ADDBREAK: { | |
1568 | duk__debug_handle_add_break(thr, heap); | |
1569 | break; | |
1570 | } | |
1571 | case DUK_DBG_CMD_DELBREAK: { | |
1572 | duk__debug_handle_del_break(thr, heap); | |
1573 | break; | |
1574 | } | |
1575 | case DUK_DBG_CMD_GETVAR: { | |
1576 | duk__debug_handle_get_var(thr, heap); | |
1577 | break; | |
1578 | } | |
1579 | case DUK_DBG_CMD_PUTVAR: { | |
1580 | duk__debug_handle_put_var(thr, heap); | |
1581 | break; | |
1582 | } | |
1583 | case DUK_DBG_CMD_GETCALLSTACK: { | |
1584 | duk__debug_handle_get_call_stack(thr, heap); | |
1585 | break; | |
1586 | } | |
1587 | case DUK_DBG_CMD_GETLOCALS: { | |
1588 | duk__debug_handle_get_locals(thr, heap); | |
1589 | break; | |
1590 | } | |
1591 | case DUK_DBG_CMD_EVAL: { | |
1592 | duk__debug_handle_eval(thr, heap); | |
1593 | break; | |
1594 | } | |
1595 | case DUK_DBG_CMD_DETACH: { | |
1596 | duk__debug_handle_detach(thr, heap); | |
1597 | break; | |
1598 | } | |
1599 | #if defined(DUK_USE_DEBUGGER_DUMPHEAP) | |
1600 | case DUK_DBG_CMD_DUMPHEAP: { | |
1601 | duk__debug_handle_dump_heap(thr, heap); | |
1602 | break; | |
1603 | } | |
1604 | #endif /* DUK_USE_DEBUGGER_DUMPHEAP */ | |
1605 | case DUK_DBG_CMD_GETBYTECODE: { | |
1606 | duk__debug_handle_get_bytecode(thr, heap); | |
1607 | break; | |
1608 | } | |
1609 | default: { | |
1610 | DUK_D(DUK_DPRINT("debug command unsupported: %d", (int) cmd)); | |
1611 | duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "unsupported command"); | |
1612 | } | |
1613 | } /* switch cmd */ | |
1614 | break; | |
1615 | } | |
1616 | case DUK_DBG_MARKER_REPLY: { | |
1617 | DUK_D(DUK_DPRINT("debug reply, skipping")); | |
1618 | break; | |
1619 | } | |
1620 | case DUK_DBG_MARKER_ERROR: { | |
1621 | DUK_D(DUK_DPRINT("debug error, skipping")); | |
1622 | break; | |
1623 | } | |
1624 | case DUK_DBG_MARKER_NOTIFY: { | |
1625 | DUK_D(DUK_DPRINT("debug notify, skipping")); | |
1626 | break; | |
1627 | } | |
1628 | default: { | |
1629 | DUK_D(DUK_DPRINT("invalid initial byte, drop connection: %d", (int) x)); | |
1630 | goto fail; | |
1631 | } | |
1632 | } /* switch initial byte */ | |
1633 | ||
1634 | duk__debug_skip_to_eom(thr); | |
1635 | return; | |
1636 | ||
1637 | fail: | |
1638 | DUK__SET_CONN_BROKEN(thr); | |
1639 | return; | |
1640 | } | |
1641 | ||
1642 | DUK_INTERNAL duk_bool_t duk_debug_process_messages(duk_hthread *thr, duk_bool_t no_block) { | |
1643 | duk_context *ctx = (duk_context *) thr; | |
1644 | #if defined(DUK_USE_ASSERTIONS) | |
1645 | duk_idx_t entry_top; | |
1646 | #endif | |
1647 | duk_bool_t retval = 0; | |
1648 | ||
1649 | DUK_ASSERT(thr != NULL); | |
1650 | DUK_UNREF(ctx); | |
1651 | #if defined(DUK_USE_ASSERTIONS) | |
1652 | entry_top = duk_get_top(ctx); | |
1653 | #endif | |
1654 | ||
1655 | DUK_DD(DUK_DDPRINT("top at entry: %ld", (long) duk_get_top(ctx))); | |
1656 | ||
1657 | for (;;) { | |
1658 | /* Process messages until we're no longer paused or we peek | |
1659 | * and see there's nothing to read right now. | |
1660 | */ | |
1661 | DUK_DD(DUK_DDPRINT("top at loop top: %ld", (long) duk_get_top(ctx))); | |
1662 | ||
1663 | if (thr->heap->dbg_read_cb == NULL) { | |
1664 | DUK_D(DUK_DPRINT("debug connection broken, stop processing messages")); | |
1665 | break; | |
1666 | } else if (!thr->heap->dbg_paused || no_block) { | |
1667 | if (!duk_debug_read_peek(thr)) { | |
1668 | DUK_D(DUK_DPRINT("processing debug message, peek indicated no data, stop processing")); | |
1669 | break; | |
1670 | } | |
1671 | DUK_D(DUK_DPRINT("processing debug message, peek indicated there is data, handle it")); | |
1672 | } else { | |
1673 | DUK_D(DUK_DPRINT("paused, process debug message, blocking if necessary")); | |
1674 | } | |
1675 | ||
1676 | duk__debug_process_message(thr); | |
1677 | if (thr->heap->dbg_state_dirty) { | |
1678 | /* Executed something that may have affected status, | |
1679 | * resend. | |
1680 | */ | |
1681 | duk_debug_send_status(thr); | |
1682 | thr->heap->dbg_state_dirty = 0; | |
1683 | } | |
1684 | retval = 1; /* processed one or more messages */ | |
1685 | } | |
1686 | ||
1687 | /* As an initial implementation, read flush after exiting the message | |
1688 | * loop. | |
1689 | */ | |
1690 | duk_debug_read_flush(thr); | |
1691 | ||
1692 | DUK_DD(DUK_DDPRINT("top at exit: %ld", (long) duk_get_top(ctx))); | |
1693 | ||
1694 | #if defined(DUK_USE_ASSERTIONS) | |
1695 | /* Easy to get wrong, so assert for it. */ | |
1696 | DUK_ASSERT(entry_top == duk_get_top(ctx)); | |
1697 | #endif | |
1698 | ||
1699 | return retval; | |
1700 | } | |
1701 | ||
1702 | /* | |
1703 | * Breakpoint management | |
1704 | */ | |
1705 | ||
1706 | DUK_INTERNAL duk_small_int_t duk_debug_add_breakpoint(duk_hthread *thr, duk_hstring *filename, duk_uint32_t line) { | |
1707 | duk_heap *heap; | |
1708 | duk_breakpoint *b; | |
1709 | ||
1710 | /* Caller must trigger recomputation of active breakpoint list. To | |
1711 | * ensure stale values are not used if that doesn't happen, clear the | |
1712 | * active breakpoint list here. | |
1713 | */ | |
1714 | ||
1715 | DUK_ASSERT(thr != NULL); | |
1716 | DUK_ASSERT(filename != NULL); | |
1717 | heap = thr->heap; | |
1718 | DUK_ASSERT(heap != NULL); | |
1719 | ||
1720 | if (heap->dbg_breakpoint_count >= DUK_HEAP_MAX_BREAKPOINTS) { | |
1721 | DUK_D(DUK_DPRINT("failed to add breakpoint for %O:%ld, all breakpoint slots used", | |
1722 | (duk_heaphdr *) filename, (long) line)); | |
1723 | return -1; | |
1724 | } | |
1725 | heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL; | |
1726 | b = heap->dbg_breakpoints + (heap->dbg_breakpoint_count++); | |
1727 | b->filename = filename; | |
1728 | b->line = line; | |
1729 | DUK_HSTRING_INCREF(thr, filename); | |
1730 | ||
1731 | return heap->dbg_breakpoint_count - 1; /* index */ | |
1732 | } | |
1733 | ||
1734 | DUK_INTERNAL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_uint_t breakpoint_index) { | |
1735 | duk_heap *heap; | |
1736 | duk_hstring *h; | |
1737 | duk_breakpoint *b; | |
1738 | duk_size_t move_size; | |
1739 | ||
1740 | /* Caller must trigger recomputation of active breakpoint list. To | |
1741 | * ensure stale values are not used if that doesn't happen, clear the | |
1742 | * active breakpoint list here. | |
1743 | */ | |
1744 | ||
1745 | DUK_ASSERT(thr != NULL); | |
1746 | heap = thr->heap; | |
1747 | DUK_ASSERT(heap != NULL); | |
1748 | DUK_ASSERT_DISABLE(breakpoint_index >= 0); /* unsigned */ | |
1749 | ||
1750 | if (breakpoint_index >= heap->dbg_breakpoint_count) { | |
1751 | DUK_D(DUK_DPRINT("invalid breakpoint index: %ld", (long) breakpoint_index)); | |
1752 | return 0; | |
1753 | } | |
1754 | b = heap->dbg_breakpoints + breakpoint_index; | |
1755 | ||
1756 | h = b->filename; | |
1757 | DUK_ASSERT(h != NULL); | |
1758 | ||
1759 | move_size = sizeof(duk_breakpoint) * (heap->dbg_breakpoint_count - breakpoint_index - 1); | |
1760 | if (move_size > 0) { | |
1761 | DUK_MEMMOVE((void *) b, | |
1762 | (void *) (b + 1), | |
1763 | move_size); | |
1764 | } | |
1765 | heap->dbg_breakpoint_count--; | |
1766 | heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL; | |
1767 | ||
1768 | DUK_HSTRING_DECREF(thr, h); /* side effects */ | |
1769 | ||
1770 | /* Breakpoint entries above the used area are left as garbage. */ | |
1771 | ||
1772 | return 1; | |
1773 | } | |
1774 | ||
1775 | #undef DUK__SET_CONN_BROKEN | |
1776 | ||
1777 | #else /* DUK_USE_DEBUGGER_SUPPORT */ | |
1778 | ||
1779 | /* No debugger support. */ | |
1780 | ||
1781 | #endif /* DUK_USE_DEBUGGER_SUPPORT */ |