1 /*
2 * Duktape debugger
3 */
4
5 #include "duk_internal.h"
6
7 #if defined(DUK_USE_DEBUGGER_SUPPORT)
8
9 /*
10 * Assert helpers
11 */
12
13 #if defined(DUK_USE_ASSERTIONS)
14 #define DUK__DBG_TPORT_ENTER() do { \
15 DUK_ASSERT(heap->dbg_calling_transport == 0); \
16 heap->dbg_calling_transport = 1; \
17 } while (0)
18 #define DUK__DBG_TPORT_EXIT() do { \
19 DUK_ASSERT(heap->dbg_calling_transport == 1); \
20 heap->dbg_calling_transport = 0; \
21 } while (0)
22 #else
23 #define DUK__DBG_TPORT_ENTER() do {} while (0)
24 #define DUK__DBG_TPORT_EXIT() do {} while (0)
25 #endif
26
27 /*
28 * Helper structs
29 */
30
31 typedef union {
32 void *p;
33 duk_uint_t b[1];
34 /* Use b[] to access the size of the union, which is strictly not
35 * correct. Can't use fixed size unless there's feature detection
36 * for pointer byte size.
37 */
38 } duk__ptr_union;
39
40 /*
41 * Detach handling
42 */
43
44 #define DUK__SET_CONN_BROKEN(thr,reason) do { \
45 /* For now shared handler is fine. */ \
46 duk__debug_do_detach1((thr)->heap, (reason)); \
47 } while (0)
48
duk__debug_do_detach1(duk_heap * heap,duk_int_t reason)49 DUK_LOCAL void duk__debug_do_detach1(duk_heap *heap, duk_int_t reason) {
50 /* Can be called multiple times with no harm. Mark the transport
51 * bad (dbg_read_cb == NULL) and clear state except for the detached
52 * callback and the udata field. The detached callback is delayed
53 * to the message loop so that it can be called between messages;
54 * this avoids corner cases related to immediate debugger reattach
55 * inside the detached callback.
56 */
57
58 if (heap->dbg_detaching) {
59 DUK_D(DUK_DPRINT("debugger already detaching, ignore detach1"));
60 return;
61 }
62
63 DUK_D(DUK_DPRINT("debugger transport detaching, marking transport broken"));
64
65 heap->dbg_detaching = 1; /* prevent multiple in-progress detaches */
66
67 if (heap->dbg_write_cb != NULL) {
68 duk_hthread *thr;
69
70 thr = heap->heap_thread;
71 DUK_ASSERT(thr != NULL);
72
73 duk_debug_write_notify(thr, DUK_DBG_CMD_DETACHING);
74 duk_debug_write_int(thr, reason);
75 duk_debug_write_eom(thr);
76 }
77
78 heap->dbg_read_cb = NULL;
79 heap->dbg_write_cb = NULL;
80 heap->dbg_peek_cb = NULL;
81 heap->dbg_read_flush_cb = NULL;
82 heap->dbg_write_flush_cb = NULL;
83 heap->dbg_request_cb = NULL;
84 /* heap->dbg_detached_cb: keep */
85 /* heap->dbg_udata: keep */
86 /* heap->dbg_processing: keep on purpose to avoid debugger re-entry in detaching state */
87 heap->dbg_state_dirty = 0;
88 heap->dbg_force_restart = 0;
89 heap->dbg_pause_flags = 0;
90 heap->dbg_pause_act = NULL;
91 heap->dbg_pause_startline = 0;
92 heap->dbg_have_next_byte = 0;
93 duk_debug_clear_paused(heap); /* XXX: some overlap with field inits above */
94 heap->dbg_state_dirty = 0; /* XXX: clear_paused sets dirty; rework? */
95
96 /* Ensure there are no stale active breakpoint pointers.
97 * Breakpoint list is currently kept - we could empty it
98 * here but we'd need to handle refcounts correctly, and
99 * we'd need a 'thr' reference for that.
100 *
101 * XXX: clear breakpoint on either attach or detach?
102 */
103 heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL;
104 }
105
duk__debug_do_detach2(duk_heap * heap)106 DUK_LOCAL void duk__debug_do_detach2(duk_heap *heap) {
107 duk_debug_detached_function detached_cb;
108 void *detached_udata;
109 duk_hthread *thr;
110
111 thr = heap->heap_thread;
112 if (thr == NULL) {
113 DUK_ASSERT(heap->dbg_detached_cb == NULL);
114 return;
115 }
116
117 /* Safe to call multiple times. */
118
119 detached_cb = heap->dbg_detached_cb;
120 detached_udata = heap->dbg_udata;
121 heap->dbg_detached_cb = NULL;
122 heap->dbg_udata = NULL;
123
124 if (detached_cb) {
125 /* Careful here: state must be wiped before the call
126 * so that we can cleanly handle a re-attach from
127 * inside the callback.
128 */
129 DUK_D(DUK_DPRINT("detached during message loop, delayed call to detached_cb"));
130 detached_cb(thr, detached_udata);
131 }
132
133 heap->dbg_detaching = 0;
134 }
135
duk_debug_do_detach(duk_heap * heap)136 DUK_INTERNAL void duk_debug_do_detach(duk_heap *heap) {
137 duk__debug_do_detach1(heap, 0);
138 duk__debug_do_detach2(heap);
139 }
140
141 /* Called on a read/write error: NULL all callbacks except the detached
142 * callback so that we never accidentally call them after a read/write
143 * error has been indicated. This is especially important for the transport
144 * I/O callbacks to fulfill guaranteed callback semantics.
145 */
duk__debug_null_most_callbacks(duk_hthread * thr)146 DUK_LOCAL void duk__debug_null_most_callbacks(duk_hthread *thr) {
147 duk_heap *heap;
148
149 DUK_ASSERT(thr != NULL);
150
151 heap = thr->heap;
152 DUK_D(DUK_DPRINT("transport read/write error, NULL all callbacks expected detached"));
153 heap->dbg_read_cb = NULL;
154 heap->dbg_write_cb = NULL; /* this is especially critical to avoid another write call in detach1() */
155 heap->dbg_peek_cb = NULL;
156 heap->dbg_read_flush_cb = NULL;
157 heap->dbg_write_flush_cb = NULL;
158 heap->dbg_request_cb = NULL;
159 /* keep heap->dbg_detached_cb */
160 }
161
162 /*
163 * Pause handling
164 */
165
duk__debug_set_pause_state(duk_hthread * thr,duk_heap * heap,duk_small_uint_t pause_flags)166 DUK_LOCAL void duk__debug_set_pause_state(duk_hthread *thr, duk_heap *heap, duk_small_uint_t pause_flags) {
167 duk_uint_fast32_t line;
168
169 line = duk_debug_curr_line(thr);
170 if (line == 0) {
171 /* No line info for current function. */
172 duk_small_uint_t updated_flags;
173
174 updated_flags = pause_flags & ~(DUK_PAUSE_FLAG_LINE_CHANGE);
175 DUK_D(DUK_DPRINT("no line info for current activation, disable line-based pause flags: 0x%08lx -> 0x%08lx",
176 (long) pause_flags, (long) updated_flags));
177 pause_flags = updated_flags;
178 }
179
180 heap->dbg_pause_flags = pause_flags;
181 heap->dbg_pause_act = thr->callstack_curr;
182 heap->dbg_pause_startline = (duk_uint32_t) line;
183 heap->dbg_state_dirty = 1;
184
185 DUK_D(DUK_DPRINT("set state for automatic pause triggers, flags=0x%08lx, act=%p, startline=%ld",
186 (long) heap->dbg_pause_flags, (void *) heap->dbg_pause_act,
187 (long) heap->dbg_pause_startline));
188 }
189
190 /*
191 * Debug connection peek and flush primitives
192 */
193
duk_debug_read_peek(duk_hthread * thr)194 DUK_INTERNAL duk_bool_t duk_debug_read_peek(duk_hthread *thr) {
195 duk_heap *heap;
196 duk_bool_t ret;
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 peek in detached state, return zero (= no data)"));
204 return 0;
205 }
206 if (heap->dbg_peek_cb == NULL) {
207 DUK_DD(DUK_DDPRINT("no peek callback, return zero (= no data)"));
208 return 0;
209 }
210
211 DUK__DBG_TPORT_ENTER();
212 ret = (duk_bool_t) (heap->dbg_peek_cb(heap->dbg_udata) > 0);
213 DUK__DBG_TPORT_EXIT();
214 return ret;
215 }
216
duk_debug_read_flush(duk_hthread * thr)217 DUK_INTERNAL void duk_debug_read_flush(duk_hthread *thr) {
218 duk_heap *heap;
219
220 DUK_ASSERT(thr != NULL);
221 heap = thr->heap;
222 DUK_ASSERT(heap != NULL);
223
224 if (heap->dbg_read_cb == NULL) {
225 DUK_D(DUK_DPRINT("attempt to read flush in detached state, ignore"));
226 return;
227 }
228 if (heap->dbg_read_flush_cb == NULL) {
229 DUK_DD(DUK_DDPRINT("no read flush callback, ignore"));
230 return;
231 }
232
233 DUK__DBG_TPORT_ENTER();
234 heap->dbg_read_flush_cb(heap->dbg_udata);
235 DUK__DBG_TPORT_EXIT();
236 }
237
duk_debug_write_flush(duk_hthread * thr)238 DUK_INTERNAL void duk_debug_write_flush(duk_hthread *thr) {
239 duk_heap *heap;
240
241 DUK_ASSERT(thr != NULL);
242 heap = thr->heap;
243 DUK_ASSERT(heap != NULL);
244
245 if (heap->dbg_read_cb == NULL) {
246 DUK_D(DUK_DPRINT("attempt to write flush in detached state, ignore"));
247 return;
248 }
249 if (heap->dbg_write_flush_cb == NULL) {
250 DUK_DD(DUK_DDPRINT("no write flush callback, ignore"));
251 return;
252 }
253
254 DUK__DBG_TPORT_ENTER();
255 heap->dbg_write_flush_cb(heap->dbg_udata);
256 DUK__DBG_TPORT_EXIT();
257 }
258
259 /*
260 * Debug connection skip primitives
261 */
262
263 /* Skip fully. */
duk_debug_skip_bytes(duk_hthread * thr,duk_size_t length)264 DUK_INTERNAL void duk_debug_skip_bytes(duk_hthread *thr, duk_size_t length) {
265 duk_uint8_t dummy[64];
266 duk_size_t now;
267
268 DUK_ASSERT(thr != NULL);
269
270 while (length > 0) {
271 now = (length > sizeof(dummy) ? sizeof(dummy) : length);
272 duk_debug_read_bytes(thr, dummy, now);
273 length -= now;
274 }
275 }
276
duk_debug_skip_byte(duk_hthread * thr)277 DUK_INTERNAL void duk_debug_skip_byte(duk_hthread *thr) {
278 DUK_ASSERT(thr != NULL);
279
280 (void) duk_debug_read_byte(thr);
281 }
282
283 /*
284 * Debug connection read primitives
285 */
286
287 /* Peek ahead in the stream one byte. */
duk_debug_peek_byte(duk_hthread * thr)288 DUK_INTERNAL uint8_t duk_debug_peek_byte(duk_hthread *thr) {
289 /* It is important not to call this if the last byte read was an EOM.
290 * Reading ahead in this scenario would cause unnecessary blocking if
291 * another message is not available.
292 */
293
294 duk_uint8_t x;
295
296 x = duk_debug_read_byte(thr);
297 thr->heap->dbg_have_next_byte = 1;
298 thr->heap->dbg_next_byte = x;
299 return x;
300 }
301
302 /* Read fully. */
duk_debug_read_bytes(duk_hthread * thr,duk_uint8_t * data,duk_size_t length)303 DUK_INTERNAL void duk_debug_read_bytes(duk_hthread *thr, duk_uint8_t *data, duk_size_t length) {
304 duk_heap *heap;
305 duk_uint8_t *p;
306 duk_size_t left;
307 duk_size_t got;
308
309 DUK_ASSERT(thr != NULL);
310 heap = thr->heap;
311 DUK_ASSERT(heap != NULL);
312 DUK_ASSERT(data != NULL);
313
314 if (heap->dbg_read_cb == NULL) {
315 DUK_D(DUK_DPRINT("attempt to read %ld bytes in detached state, return zero data", (long) length));
316 goto fail;
317 }
318
319 /* NOTE: length may be zero */
320 p = data;
321 if (length >= 1 && heap->dbg_have_next_byte) {
322 heap->dbg_have_next_byte = 0;
323 *p++ = heap->dbg_next_byte;
324 }
325 for (;;) {
326 left = (duk_size_t) ((data + length) - p);
327 if (left == 0) {
328 break;
329 }
330 DUK_ASSERT(heap->dbg_read_cb != NULL);
331 DUK_ASSERT(left >= 1);
332 #if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE)
333 left = 1;
334 #endif
335 DUK__DBG_TPORT_ENTER();
336 got = heap->dbg_read_cb(heap->dbg_udata, (char *) p, left);
337 DUK__DBG_TPORT_EXIT();
338
339 if (got == 0 || got > left) {
340 DUK_D(DUK_DPRINT("connection error during read, return zero data"));
341 duk__debug_null_most_callbacks(thr); /* avoid calling write callback in detach1() */
342 DUK__SET_CONN_BROKEN(thr, 1);
343 goto fail;
344 }
345 p += got;
346 }
347 return;
348
349 fail:
350 duk_memzero((void *) data, (size_t) length);
351 }
352
duk_debug_read_byte(duk_hthread * thr)353 DUK_INTERNAL duk_uint8_t duk_debug_read_byte(duk_hthread *thr) {
354 duk_uint8_t x;
355
356 x = 0; /* just in case callback is broken and won't write 'x' */
357 duk_debug_read_bytes(thr, &x, 1);
358 return x;
359 }
360
duk__debug_read_uint32_raw(duk_hthread * thr)361 DUK_LOCAL duk_uint32_t duk__debug_read_uint32_raw(duk_hthread *thr) {
362 duk_uint8_t buf[4];
363
364 DUK_ASSERT(thr != NULL);
365
366 duk_debug_read_bytes(thr, buf, 4);
367 return ((duk_uint32_t) buf[0] << 24) |
368 ((duk_uint32_t) buf[1] << 16) |
369 ((duk_uint32_t) buf[2] << 8) |
370 (duk_uint32_t) buf[3];
371 }
372
duk__debug_read_int32_raw(duk_hthread * thr)373 DUK_LOCAL duk_int32_t duk__debug_read_int32_raw(duk_hthread *thr) {
374 return (duk_int32_t) duk__debug_read_uint32_raw(thr);
375 }
376
duk__debug_read_uint16_raw(duk_hthread * thr)377 DUK_LOCAL duk_uint16_t duk__debug_read_uint16_raw(duk_hthread *thr) {
378 duk_uint8_t buf[2];
379
380 DUK_ASSERT(thr != NULL);
381
382 duk_debug_read_bytes(thr, buf, 2);
383 return ((duk_uint16_t) buf[0] << 8) |
384 (duk_uint16_t) buf[1];
385 }
386
duk_debug_read_int(duk_hthread * thr)387 DUK_INTERNAL duk_int32_t duk_debug_read_int(duk_hthread *thr) {
388 duk_small_uint_t x;
389 duk_small_uint_t t;
390
391 DUK_ASSERT(thr != NULL);
392
393 x = duk_debug_read_byte(thr);
394 if (x >= 0xc0) {
395 t = duk_debug_read_byte(thr);
396 return (duk_int32_t) (((x - 0xc0) << 8) + t);
397 } else if (x >= 0x80) {
398 return (duk_int32_t) (x - 0x80);
399 } else if (x == DUK_DBG_IB_INT4) {
400 return (duk_int32_t) duk__debug_read_uint32_raw(thr);
401 }
402
403 DUK_D(DUK_DPRINT("debug connection error: failed to decode int"));
404 DUK__SET_CONN_BROKEN(thr, 1);
405 return 0;
406 }
407
duk__debug_read_hstring_raw(duk_hthread * thr,duk_uint32_t len)408 DUK_LOCAL duk_hstring *duk__debug_read_hstring_raw(duk_hthread *thr, duk_uint32_t len) {
409 duk_uint8_t buf[31];
410 duk_uint8_t *p;
411
412 if (len <= sizeof(buf)) {
413 duk_debug_read_bytes(thr, buf, (duk_size_t) len);
414 duk_push_lstring(thr, (const char *) buf, (duk_size_t) len);
415 } else {
416 p = (duk_uint8_t *) duk_push_fixed_buffer(thr, (duk_size_t) len); /* zero for paranoia */
417 DUK_ASSERT(p != NULL);
418 duk_debug_read_bytes(thr, p, (duk_size_t) len);
419 (void) duk_buffer_to_string(thr, -1); /* Safety relies on debug client, which is OK. */
420 }
421
422 return duk_require_hstring(thr, -1);
423 }
424
duk_debug_read_hstring(duk_hthread * thr)425 DUK_INTERNAL duk_hstring *duk_debug_read_hstring(duk_hthread *thr) {
426 duk_small_uint_t x;
427 duk_uint32_t len;
428
429 DUK_ASSERT(thr != NULL);
430
431 x = duk_debug_read_byte(thr);
432 if (x >= 0x60 && x <= 0x7f) {
433 /* For short strings, use a fixed temp buffer. */
434 len = (duk_uint32_t) (x - 0x60);
435 } else if (x == DUK_DBG_IB_STR2) {
436 len = (duk_uint32_t) duk__debug_read_uint16_raw(thr);
437 } else if (x == DUK_DBG_IB_STR4) {
438 len = (duk_uint32_t) duk__debug_read_uint32_raw(thr);
439 } else {
440 goto fail;
441 }
442
443 return duk__debug_read_hstring_raw(thr, len);
444
445 fail:
446 DUK_D(DUK_DPRINT("debug connection error: failed to decode int"));
447 DUK__SET_CONN_BROKEN(thr, 1);
448 duk_push_hstring_empty(thr); /* always push some string */
449 return duk_require_hstring(thr, -1);
450 }
451
duk__debug_read_hbuffer_raw(duk_hthread * thr,duk_uint32_t len)452 DUK_LOCAL duk_hbuffer *duk__debug_read_hbuffer_raw(duk_hthread *thr, duk_uint32_t len) {
453 duk_uint8_t *p;
454
455 p = (duk_uint8_t *) duk_push_fixed_buffer(thr, (duk_size_t) len); /* zero for paranoia */
456 DUK_ASSERT(p != NULL);
457 duk_debug_read_bytes(thr, p, (duk_size_t) len);
458
459 return duk_require_hbuffer(thr, -1);
460 }
461
duk__debug_read_pointer_raw(duk_hthread * thr)462 DUK_LOCAL void *duk__debug_read_pointer_raw(duk_hthread *thr) {
463 duk_small_uint_t x;
464 duk__ptr_union pu;
465
466 DUK_ASSERT(thr != NULL);
467
468 x = duk_debug_read_byte(thr);
469 if (x != sizeof(pu)) {
470 goto fail;
471 }
472 duk_debug_read_bytes(thr, (duk_uint8_t *) &pu.p, sizeof(pu));
473 #if defined(DUK_USE_INTEGER_LE)
474 duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu));
475 #endif
476 return (void *) pu.p;
477
478 fail:
479 DUK_D(DUK_DPRINT("debug connection error: failed to decode pointer"));
480 DUK__SET_CONN_BROKEN(thr, 1);
481 return (void *) NULL;
482 }
483
duk__debug_read_double_raw(duk_hthread * thr)484 DUK_LOCAL duk_double_t duk__debug_read_double_raw(duk_hthread *thr) {
485 duk_double_union du;
486
487 DUK_ASSERT(sizeof(du.uc) == 8);
488 duk_debug_read_bytes(thr, (duk_uint8_t *) du.uc, sizeof(du.uc));
489 DUK_DBLUNION_DOUBLE_NTOH(&du);
490 return du.d;
491 }
492
493 #if 0
494 DUK_INTERNAL duk_heaphdr *duk_debug_read_heapptr(duk_hthread *thr) {
495 duk_small_uint_t x;
496
497 DUK_ASSERT(thr != NULL);
498
499 x = duk_debug_read_byte(thr);
500 if (x != DUK_DBG_IB_HEAPPTR) {
501 goto fail;
502 }
503
504 return (duk_heaphdr *) duk__debug_read_pointer_raw(thr);
505
506 fail:
507 DUK_D(DUK_DPRINT("debug connection error: failed to decode heapptr"));
508 DUK__SET_CONN_BROKEN(thr, 1);
509 return NULL;
510 }
511 #endif
512
duk_debug_read_any_ptr(duk_hthread * thr)513 DUK_INTERNAL duk_heaphdr *duk_debug_read_any_ptr(duk_hthread *thr) {
514 duk_small_uint_t x;
515
516 DUK_ASSERT(thr != NULL);
517
518 x = duk_debug_read_byte(thr);
519 switch (x) {
520 case DUK_DBG_IB_OBJECT:
521 case DUK_DBG_IB_POINTER:
522 case DUK_DBG_IB_HEAPPTR:
523 /* Accept any pointer-like value; for 'object' dvalue, read
524 * and ignore the class number.
525 */
526 if (x == DUK_DBG_IB_OBJECT) {
527 duk_debug_skip_byte(thr);
528 }
529 break;
530 default:
531 goto fail;
532 }
533
534 return (duk_heaphdr *) duk__debug_read_pointer_raw(thr);
535
536 fail:
537 DUK_D(DUK_DPRINT("debug connection error: failed to decode any pointer (object, pointer, heapptr)"));
538 DUK__SET_CONN_BROKEN(thr, 1);
539 return NULL;
540 }
541
duk_debug_read_tval(duk_hthread * thr)542 DUK_INTERNAL duk_tval *duk_debug_read_tval(duk_hthread *thr) {
543 duk_uint8_t x;
544 duk_uint_t t;
545 duk_uint32_t len;
546
547 DUK_ASSERT(thr != NULL);
548
549 x = duk_debug_read_byte(thr);
550
551 if (x >= 0xc0) {
552 t = (duk_uint_t) (x - 0xc0);
553 t = (t << 8) + duk_debug_read_byte(thr);
554 duk_push_uint(thr, (duk_uint_t) t);
555 goto return_ptr;
556 }
557 if (x >= 0x80) {
558 duk_push_uint(thr, (duk_uint_t) (x - 0x80));
559 goto return_ptr;
560 }
561 if (x >= 0x60) {
562 len = (duk_uint32_t) (x - 0x60);
563 duk__debug_read_hstring_raw(thr, len);
564 goto return_ptr;
565 }
566
567 switch (x) {
568 case DUK_DBG_IB_INT4: {
569 duk_int32_t i = duk__debug_read_int32_raw(thr);
570 duk_push_i32(thr, i);
571 break;
572 }
573 case DUK_DBG_IB_STR4: {
574 len = duk__debug_read_uint32_raw(thr);
575 duk__debug_read_hstring_raw(thr, len);
576 break;
577 }
578 case DUK_DBG_IB_STR2: {
579 len = duk__debug_read_uint16_raw(thr);
580 duk__debug_read_hstring_raw(thr, len);
581 break;
582 }
583 case DUK_DBG_IB_BUF4: {
584 len = duk__debug_read_uint32_raw(thr);
585 duk__debug_read_hbuffer_raw(thr, len);
586 break;
587 }
588 case DUK_DBG_IB_BUF2: {
589 len = duk__debug_read_uint16_raw(thr);
590 duk__debug_read_hbuffer_raw(thr, len);
591 break;
592 }
593 case DUK_DBG_IB_UNDEFINED: {
594 duk_push_undefined(thr);
595 break;
596 }
597 case DUK_DBG_IB_NULL: {
598 duk_push_null(thr);
599 break;
600 }
601 case DUK_DBG_IB_TRUE: {
602 duk_push_true(thr);
603 break;
604 }
605 case DUK_DBG_IB_FALSE: {
606 duk_push_false(thr);
607 break;
608 }
609 case DUK_DBG_IB_NUMBER: {
610 duk_double_t d;
611 d = duk__debug_read_double_raw(thr);
612 duk_push_number(thr, d);
613 break;
614 }
615 case DUK_DBG_IB_OBJECT: {
616 duk_heaphdr *h;
617 duk_debug_skip_byte(thr);
618 h = (duk_heaphdr *) duk__debug_read_pointer_raw(thr);
619 duk_push_heapptr(thr, (void *) h);
620 break;
621 }
622 case DUK_DBG_IB_POINTER: {
623 void *ptr;
624 ptr = duk__debug_read_pointer_raw(thr);
625 duk_push_pointer(thr, ptr);
626 break;
627 }
628 case DUK_DBG_IB_LIGHTFUNC: {
629 /* XXX: Not needed for now, so not implemented. Note that
630 * function pointers may have different size/layout than
631 * a void pointer.
632 */
633 DUK_D(DUK_DPRINT("reading lightfunc values unimplemented"));
634 goto fail;
635 }
636 case DUK_DBG_IB_HEAPPTR: {
637 duk_heaphdr *h;
638 h = (duk_heaphdr *) duk__debug_read_pointer_raw(thr);
639 duk_push_heapptr(thr, (void *) h);
640 break;
641 }
642 case DUK_DBG_IB_UNUSED: /* unused: not accepted in inbound messages */
643 default:
644 goto fail;
645 }
646
647 return_ptr:
648 return DUK_GET_TVAL_NEGIDX(thr, -1);
649
650 fail:
651 DUK_D(DUK_DPRINT("debug connection error: failed to decode tval"));
652 DUK__SET_CONN_BROKEN(thr, 1);
653 return NULL;
654 }
655
656 /*
657 * Debug connection write primitives
658 */
659
660 /* Write fully. */
duk_debug_write_bytes(duk_hthread * thr,const duk_uint8_t * data,duk_size_t length)661 DUK_INTERNAL void duk_debug_write_bytes(duk_hthread *thr, const duk_uint8_t *data, duk_size_t length) {
662 duk_heap *heap;
663 const duk_uint8_t *p;
664 duk_size_t left;
665 duk_size_t got;
666
667 DUK_ASSERT(thr != NULL);
668 DUK_ASSERT(length == 0 || data != NULL);
669 heap = thr->heap;
670 DUK_ASSERT(heap != NULL);
671
672 if (heap->dbg_write_cb == NULL) {
673 DUK_D(DUK_DPRINT("attempt to write %ld bytes in detached state, ignore", (long) length));
674 return;
675 }
676 if (length == 0) {
677 /* Avoid doing an actual write callback with length == 0,
678 * because that's reserved for a write flush.
679 */
680 return;
681 }
682 DUK_ASSERT(data != NULL);
683
684 p = data;
685 for (;;) {
686 left = (duk_size_t) ((data + length) - p);
687 if (left == 0) {
688 break;
689 }
690 DUK_ASSERT(heap->dbg_write_cb != NULL);
691 DUK_ASSERT(left >= 1);
692 #if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE)
693 left = 1;
694 #endif
695 DUK__DBG_TPORT_ENTER();
696 got = heap->dbg_write_cb(heap->dbg_udata, (const char *) p, left);
697 DUK__DBG_TPORT_EXIT();
698
699 if (got == 0 || got > left) {
700 duk__debug_null_most_callbacks(thr); /* avoid calling write callback in detach1() */
701 DUK_D(DUK_DPRINT("connection error during write"));
702 DUK__SET_CONN_BROKEN(thr, 1);
703 return;
704 }
705 p += got;
706 }
707 }
708
duk_debug_write_byte(duk_hthread * thr,duk_uint8_t x)709 DUK_INTERNAL void duk_debug_write_byte(duk_hthread *thr, duk_uint8_t x) {
710 duk_debug_write_bytes(thr, (const duk_uint8_t *) &x, 1);
711 }
712
duk_debug_write_unused(duk_hthread * thr)713 DUK_INTERNAL void duk_debug_write_unused(duk_hthread *thr) {
714 duk_debug_write_byte(thr, DUK_DBG_IB_UNUSED);
715 }
716
duk_debug_write_undefined(duk_hthread * thr)717 DUK_INTERNAL void duk_debug_write_undefined(duk_hthread *thr) {
718 duk_debug_write_byte(thr, DUK_DBG_IB_UNDEFINED);
719 }
720
721 #if defined(DUK_USE_DEBUGGER_INSPECT)
duk_debug_write_null(duk_hthread * thr)722 DUK_INTERNAL void duk_debug_write_null(duk_hthread *thr) {
723 duk_debug_write_byte(thr, DUK_DBG_IB_NULL);
724 }
725 #endif
726
duk_debug_write_boolean(duk_hthread * thr,duk_uint_t val)727 DUK_INTERNAL void duk_debug_write_boolean(duk_hthread *thr, duk_uint_t val) {
728 duk_debug_write_byte(thr, val ? DUK_DBG_IB_TRUE : DUK_DBG_IB_FALSE);
729 }
730
731 /* Write signed 32-bit integer. */
duk_debug_write_int(duk_hthread * thr,duk_int32_t x)732 DUK_INTERNAL void duk_debug_write_int(duk_hthread *thr, duk_int32_t x) {
733 duk_uint8_t buf[5];
734 duk_size_t len;
735
736 DUK_ASSERT(thr != NULL);
737
738 if (x >= 0 && x <= 0x3fL) {
739 buf[0] = (duk_uint8_t) (0x80 + x);
740 len = 1;
741 } else if (x >= 0 && x <= 0x3fffL) {
742 buf[0] = (duk_uint8_t) (0xc0 + (x >> 8));
743 buf[1] = (duk_uint8_t) (x & 0xff);
744 len = 2;
745 } else {
746 /* Signed integers always map to 4 bytes now. */
747 buf[0] = (duk_uint8_t) DUK_DBG_IB_INT4;
748 buf[1] = (duk_uint8_t) ((x >> 24) & 0xff);
749 buf[2] = (duk_uint8_t) ((x >> 16) & 0xff);
750 buf[3] = (duk_uint8_t) ((x >> 8) & 0xff);
751 buf[4] = (duk_uint8_t) (x & 0xff);
752 len = 5;
753 }
754 duk_debug_write_bytes(thr, buf, len);
755 }
756
757 /* Write unsigned 32-bit integer. */
duk_debug_write_uint(duk_hthread * thr,duk_uint32_t x)758 DUK_INTERNAL void duk_debug_write_uint(duk_hthread *thr, duk_uint32_t x) {
759 /* The debugger protocol doesn't support a plain integer encoding for
760 * the full 32-bit unsigned range (only 32-bit signed). For now,
761 * unsigned 32-bit values simply written as signed ones. This is not
762 * a concrete issue except for 32-bit heaphdr fields. Proper solutions
763 * would be to (a) write such integers as IEEE doubles or (b) add an
764 * unsigned 32-bit dvalue.
765 */
766 if (x >= 0x80000000UL) {
767 DUK_D(DUK_DPRINT("writing unsigned integer 0x%08lx as signed integer",
768 (long) x));
769 }
770 duk_debug_write_int(thr, (duk_int32_t) x);
771 }
772
duk_debug_write_strbuf(duk_hthread * thr,const char * data,duk_size_t length,duk_uint8_t marker_base)773 DUK_INTERNAL void duk_debug_write_strbuf(duk_hthread *thr, const char *data, duk_size_t length, duk_uint8_t marker_base) {
774 duk_uint8_t buf[5];
775 duk_size_t buflen;
776
777 DUK_ASSERT(thr != NULL);
778 DUK_ASSERT(length == 0 || data != NULL);
779
780 if (length <= 0x1fUL && marker_base == DUK_DBG_IB_STR4) {
781 /* For strings, special form for short lengths. */
782 buf[0] = (duk_uint8_t) (0x60 + length);
783 buflen = 1;
784 } else if (length <= 0xffffUL) {
785 buf[0] = (duk_uint8_t) (marker_base + 1);
786 buf[1] = (duk_uint8_t) (length >> 8);
787 buf[2] = (duk_uint8_t) (length & 0xff);
788 buflen = 3;
789 } else {
790 buf[0] = (duk_uint8_t) marker_base;
791 buf[1] = (duk_uint8_t) (length >> 24);
792 buf[2] = (duk_uint8_t) ((length >> 16) & 0xff);
793 buf[3] = (duk_uint8_t) ((length >> 8) & 0xff);
794 buf[4] = (duk_uint8_t) (length & 0xff);
795 buflen = 5;
796 }
797
798 duk_debug_write_bytes(thr, (const duk_uint8_t *) buf, buflen);
799 duk_debug_write_bytes(thr, (const duk_uint8_t *) data, length);
800 }
801
duk_debug_write_string(duk_hthread * thr,const char * data,duk_size_t length)802 DUK_INTERNAL void duk_debug_write_string(duk_hthread *thr, const char *data, duk_size_t length) {
803 duk_debug_write_strbuf(thr, data, length, DUK_DBG_IB_STR4);
804 }
805
duk_debug_write_cstring(duk_hthread * thr,const char * data)806 DUK_INTERNAL void duk_debug_write_cstring(duk_hthread *thr, const char *data) {
807 DUK_ASSERT(thr != NULL);
808
809 duk_debug_write_string(thr,
810 data,
811 data ? DUK_STRLEN(data) : 0);
812 }
813
duk_debug_write_hstring(duk_hthread * thr,duk_hstring * h)814 DUK_INTERNAL void duk_debug_write_hstring(duk_hthread *thr, duk_hstring *h) {
815 DUK_ASSERT(thr != NULL);
816
817 /* XXX: differentiate null pointer from empty string? */
818 duk_debug_write_string(thr,
819 (h != NULL ? (const char *) DUK_HSTRING_GET_DATA(h) : NULL),
820 (h != NULL ? (duk_size_t) DUK_HSTRING_GET_BYTELEN(h) : 0));
821 }
822
duk__debug_write_hstring_safe_top(duk_hthread * thr)823 DUK_LOCAL void duk__debug_write_hstring_safe_top(duk_hthread *thr) {
824 duk_debug_write_hstring(thr, duk_safe_to_hstring(thr, -1));
825 }
826
duk_debug_write_buffer(duk_hthread * thr,const char * data,duk_size_t length)827 DUK_INTERNAL void duk_debug_write_buffer(duk_hthread *thr, const char *data, duk_size_t length) {
828 duk_debug_write_strbuf(thr, data, length, DUK_DBG_IB_BUF4);
829 }
830
duk_debug_write_hbuffer(duk_hthread * thr,duk_hbuffer * h)831 DUK_INTERNAL void duk_debug_write_hbuffer(duk_hthread *thr, duk_hbuffer *h) {
832 DUK_ASSERT(thr != NULL);
833
834 duk_debug_write_buffer(thr,
835 (h != NULL ? (const char *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h) : NULL),
836 (h != NULL ? (duk_size_t) DUK_HBUFFER_GET_SIZE(h) : 0));
837 }
838
duk__debug_write_pointer_raw(duk_hthread * thr,void * ptr,duk_uint8_t ibyte)839 DUK_LOCAL void duk__debug_write_pointer_raw(duk_hthread *thr, void *ptr, duk_uint8_t ibyte) {
840 duk_uint8_t buf[2];
841 duk__ptr_union pu;
842
843 DUK_ASSERT(thr != NULL);
844 DUK_ASSERT(sizeof(ptr) >= 1 && sizeof(ptr) <= 16);
845 /* ptr may be NULL */
846
847 buf[0] = ibyte;
848 buf[1] = sizeof(pu);
849 duk_debug_write_bytes(thr, buf, 2);
850 pu.p = (void *) ptr;
851 #if defined(DUK_USE_INTEGER_LE)
852 duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu));
853 #endif
854 duk_debug_write_bytes(thr, (const duk_uint8_t *) &pu.p, (duk_size_t) sizeof(pu));
855 }
856
duk_debug_write_pointer(duk_hthread * thr,void * ptr)857 DUK_INTERNAL void duk_debug_write_pointer(duk_hthread *thr, void *ptr) {
858 duk__debug_write_pointer_raw(thr, ptr, DUK_DBG_IB_POINTER);
859 }
860
861 #if defined(DUK_USE_DEBUGGER_DUMPHEAP) || defined(DUK_USE_DEBUGGER_INSPECT)
duk_debug_write_heapptr(duk_hthread * thr,duk_heaphdr * h)862 DUK_INTERNAL void duk_debug_write_heapptr(duk_hthread *thr, duk_heaphdr *h) {
863 duk__debug_write_pointer_raw(thr, (void *) h, DUK_DBG_IB_HEAPPTR);
864 }
865 #endif /* DUK_USE_DEBUGGER_DUMPHEAP || DUK_USE_DEBUGGER_INSPECT */
866
duk_debug_write_hobject(duk_hthread * thr,duk_hobject * obj)867 DUK_INTERNAL void duk_debug_write_hobject(duk_hthread *thr, duk_hobject *obj) {
868 duk_uint8_t buf[3];
869 duk__ptr_union pu;
870
871 DUK_ASSERT(thr != NULL);
872 DUK_ASSERT(sizeof(obj) >= 1 && sizeof(obj) <= 16);
873 DUK_ASSERT(obj != NULL);
874
875 buf[0] = DUK_DBG_IB_OBJECT;
876 buf[1] = (duk_uint8_t) DUK_HOBJECT_GET_CLASS_NUMBER(obj);
877 buf[2] = sizeof(pu);
878 duk_debug_write_bytes(thr, buf, 3);
879 pu.p = (void *) obj;
880 #if defined(DUK_USE_INTEGER_LE)
881 duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu));
882 #endif
883 duk_debug_write_bytes(thr, (const duk_uint8_t *) &pu.p, (duk_size_t) sizeof(pu));
884 }
885
duk_debug_write_tval(duk_hthread * thr,duk_tval * tv)886 DUK_INTERNAL void duk_debug_write_tval(duk_hthread *thr, duk_tval *tv) {
887 duk_c_function lf_func;
888 duk_small_uint_t lf_flags;
889 duk_uint8_t buf[4];
890 duk_double_union du1;
891 duk_double_union du2;
892 duk_int32_t i32;
893
894 DUK_ASSERT(thr != NULL);
895 DUK_ASSERT(tv != NULL);
896
897 switch (DUK_TVAL_GET_TAG(tv)) {
898 case DUK_TAG_UNDEFINED:
899 duk_debug_write_byte(thr, DUK_DBG_IB_UNDEFINED);
900 break;
901 case DUK_TAG_UNUSED:
902 duk_debug_write_byte(thr, DUK_DBG_IB_UNUSED);
903 break;
904 case DUK_TAG_NULL:
905 duk_debug_write_byte(thr, DUK_DBG_IB_NULL);
906 break;
907 case DUK_TAG_BOOLEAN:
908 DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv) == 0 ||
909 DUK_TVAL_GET_BOOLEAN(tv) == 1);
910 duk_debug_write_boolean(thr, DUK_TVAL_GET_BOOLEAN(tv));
911 break;
912 case DUK_TAG_POINTER:
913 duk_debug_write_pointer(thr, (void *) DUK_TVAL_GET_POINTER(tv));
914 break;
915 case DUK_TAG_LIGHTFUNC:
916 DUK_TVAL_GET_LIGHTFUNC(tv, lf_func, lf_flags);
917 buf[0] = DUK_DBG_IB_LIGHTFUNC;
918 buf[1] = (duk_uint8_t) (lf_flags >> 8);
919 buf[2] = (duk_uint8_t) (lf_flags & 0xff);
920 buf[3] = sizeof(lf_func);
921 duk_debug_write_bytes(thr, buf, 4);
922 duk_debug_write_bytes(thr, (const duk_uint8_t *) &lf_func, sizeof(lf_func));
923 break;
924 case DUK_TAG_STRING:
925 duk_debug_write_hstring(thr, DUK_TVAL_GET_STRING(tv));
926 break;
927 case DUK_TAG_OBJECT:
928 duk_debug_write_hobject(thr, DUK_TVAL_GET_OBJECT(tv));
929 break;
930 case DUK_TAG_BUFFER:
931 duk_debug_write_hbuffer(thr, DUK_TVAL_GET_BUFFER(tv));
932 break;
933 #if defined(DUK_USE_FASTINT)
934 case DUK_TAG_FASTINT:
935 #endif
936 default:
937 /* Numbers are normalized to big (network) endian. We can
938 * (but are not required) to use integer dvalues when there's
939 * no loss of precision.
940 *
941 * XXX: share check with other code; this check is slow but
942 * reliable and doesn't require careful exponent/mantissa
943 * mask tricks as in the fastint downgrade code.
944 */
945 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
946 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
947 du1.d = DUK_TVAL_GET_NUMBER(tv);
948 i32 = (duk_int32_t) du1.d;
949 du2.d = (duk_double_t) i32;
950
951 DUK_DD(DUK_DDPRINT("i32=%ld du1=%02x%02x%02x%02x%02x%02x%02x%02x "
952 "du2=%02x%02x%02x%02x%02x%02x%02x%02x",
953 (long) i32,
954 (unsigned int) du1.uc[0], (unsigned int) du1.uc[1],
955 (unsigned int) du1.uc[2], (unsigned int) du1.uc[3],
956 (unsigned int) du1.uc[4], (unsigned int) du1.uc[5],
957 (unsigned int) du1.uc[6], (unsigned int) du1.uc[7],
958 (unsigned int) du2.uc[0], (unsigned int) du2.uc[1],
959 (unsigned int) du2.uc[2], (unsigned int) du2.uc[3],
960 (unsigned int) du2.uc[4], (unsigned int) du2.uc[5],
961 (unsigned int) du2.uc[6], (unsigned int) du2.uc[7]));
962
963 if (duk_memcmp((const void *) du1.uc, (const void *) du2.uc, sizeof(du1.uc)) == 0) {
964 duk_debug_write_int(thr, i32);
965 } else {
966 DUK_DBLUNION_DOUBLE_HTON(&du1);
967 duk_debug_write_byte(thr, DUK_DBG_IB_NUMBER);
968 duk_debug_write_bytes(thr, (const duk_uint8_t *) du1.uc, sizeof(du1.uc));
969 }
970 }
971 }
972
973 #if defined(DUK_USE_DEBUGGER_DUMPHEAP)
974 /* Variant for writing duk_tvals so that any heap allocated values are
975 * written out as tagged heap pointers.
976 */
duk__debug_write_tval_heapptr(duk_hthread * thr,duk_tval * tv)977 DUK_LOCAL void duk__debug_write_tval_heapptr(duk_hthread *thr, duk_tval *tv) {
978 if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
979 duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
980 duk_debug_write_heapptr(thr, h);
981 } else {
982 duk_debug_write_tval(thr, tv);
983 }
984 }
985 #endif /* DUK_USE_DEBUGGER_DUMPHEAP */
986
987 /*
988 * Debug connection message write helpers
989 */
990
991 #if 0 /* unused */
992 DUK_INTERNAL void duk_debug_write_request(duk_hthread *thr, duk_small_uint_t command) {
993 duk_debug_write_byte(thr, DUK_DBG_IB_REQUEST);
994 duk_debug_write_int(thr, command);
995 }
996 #endif
997
duk_debug_write_reply(duk_hthread * thr)998 DUK_INTERNAL void duk_debug_write_reply(duk_hthread *thr) {
999 duk_debug_write_byte(thr, DUK_DBG_IB_REPLY);
1000 }
1001
duk_debug_write_error_eom(duk_hthread * thr,duk_small_uint_t err_code,const char * msg)1002 DUK_INTERNAL void duk_debug_write_error_eom(duk_hthread *thr, duk_small_uint_t err_code, const char *msg) {
1003 /* Allow NULL 'msg' */
1004 duk_debug_write_byte(thr, DUK_DBG_IB_ERROR);
1005 duk_debug_write_int(thr, (duk_int32_t) err_code);
1006 duk_debug_write_cstring(thr, msg);
1007 duk_debug_write_eom(thr);
1008 }
1009
duk_debug_write_notify(duk_hthread * thr,duk_small_uint_t command)1010 DUK_INTERNAL void duk_debug_write_notify(duk_hthread *thr, duk_small_uint_t command) {
1011 duk_debug_write_byte(thr, DUK_DBG_IB_NOTIFY);
1012 duk_debug_write_int(thr, (duk_int32_t) command);
1013 }
1014
duk_debug_write_eom(duk_hthread * thr)1015 DUK_INTERNAL void duk_debug_write_eom(duk_hthread *thr) {
1016 duk_debug_write_byte(thr, DUK_DBG_IB_EOM);
1017
1018 /* As an initial implementation, write flush after every EOM (and the
1019 * version identifier). A better implementation would flush only when
1020 * Duktape is finished processing messages so that a flush only happens
1021 * after all outbound messages are finished on that occasion.
1022 */
1023 duk_debug_write_flush(thr);
1024 }
1025
1026 /*
1027 * Status message and helpers
1028 */
1029
duk_debug_curr_line(duk_hthread * thr)1030 DUK_INTERNAL duk_uint_fast32_t duk_debug_curr_line(duk_hthread *thr) {
1031 duk_activation *act;
1032 duk_uint_fast32_t line;
1033 duk_uint_fast32_t pc;
1034
1035 act = thr->callstack_curr;
1036 if (act == NULL) {
1037 return 0;
1038 }
1039
1040 /* We're conceptually between two opcodes; act->pc indicates the next
1041 * instruction to be executed. This is usually the correct pc/line to
1042 * indicate in Status. (For the 'debugger' statement this now reports
1043 * the pc/line after the debugger statement because the debugger opcode
1044 * has already been executed.)
1045 */
1046
1047 pc = duk_hthread_get_act_curr_pc(thr, act);
1048
1049 /* XXX: this should be optimized to be a raw query and avoid valstack
1050 * operations if possible.
1051 */
1052 duk_push_tval(thr, &act->tv_func);
1053 line = duk_hobject_pc2line_query(thr, -1, pc);
1054 duk_pop(thr);
1055 return line;
1056 }
1057
duk_debug_send_status(duk_hthread * thr)1058 DUK_INTERNAL void duk_debug_send_status(duk_hthread *thr) {
1059 duk_activation *act;
1060
1061 duk_debug_write_notify(thr, DUK_DBG_CMD_STATUS);
1062 duk_debug_write_int(thr, (DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) ? 1 : 0));
1063
1064 act = thr->callstack_curr;
1065 if (act == NULL) {
1066 duk_debug_write_undefined(thr);
1067 duk_debug_write_undefined(thr);
1068 duk_debug_write_int(thr, 0);
1069 duk_debug_write_int(thr, 0);
1070 } else {
1071 duk_push_tval(thr, &act->tv_func);
1072 duk_get_prop_literal(thr, -1, "fileName");
1073 duk__debug_write_hstring_safe_top(thr);
1074 duk_get_prop_literal(thr, -2, "name");
1075 duk__debug_write_hstring_safe_top(thr);
1076 duk_pop_3(thr);
1077 /* Report next pc/line to be executed. */
1078 duk_debug_write_uint(thr, (duk_uint32_t) duk_debug_curr_line(thr));
1079 duk_debug_write_uint(thr, (duk_uint32_t) duk_hthread_get_act_curr_pc(thr, act));
1080 }
1081
1082 duk_debug_write_eom(thr);
1083 }
1084
1085 #if defined(DUK_USE_DEBUGGER_THROW_NOTIFY)
duk_debug_send_throw(duk_hthread * thr,duk_bool_t fatal)1086 DUK_INTERNAL void duk_debug_send_throw(duk_hthread *thr, duk_bool_t fatal) {
1087 /*
1088 * NFY <int: 5> <int: fatal> <str: msg> <str: filename> <int: linenumber> EOM
1089 */
1090
1091 duk_activation *act;
1092 duk_uint32_t pc;
1093
1094 DUK_ASSERT(thr->valstack_top > thr->valstack); /* At least: ... [err] */
1095
1096 duk_debug_write_notify(thr, DUK_DBG_CMD_THROW);
1097 duk_debug_write_int(thr, (duk_int32_t) fatal);
1098
1099 /* Report thrown value to client coerced to string */
1100 duk_dup_top(thr);
1101 duk__debug_write_hstring_safe_top(thr);
1102 duk_pop(thr);
1103
1104 if (duk_is_error(thr, -1)) {
1105 /* Error instance, use augmented error data directly */
1106 duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_FILE_NAME);
1107 duk__debug_write_hstring_safe_top(thr);
1108 duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_LINE_NUMBER);
1109 duk_debug_write_uint(thr, duk_get_uint(thr, -1));
1110 duk_pop_2(thr);
1111 } else {
1112 /* For anything other than an Error instance, we calculate the
1113 * error location directly from the current activation if one
1114 * exists.
1115 */
1116 act = thr->callstack_curr;
1117 if (act != NULL) {
1118 duk_push_tval(thr, &act->tv_func);
1119 duk_get_prop_literal(thr, -1, "fileName");
1120 duk__debug_write_hstring_safe_top(thr);
1121 pc = (duk_uint32_t) duk_hthread_get_act_prev_pc(thr, act);
1122 duk_debug_write_uint(thr, (duk_uint32_t) duk_hobject_pc2line_query(thr, -2, pc));
1123 duk_pop_2(thr);
1124 } else {
1125 /* Can happen if duk_throw() is called on an empty
1126 * callstack.
1127 */
1128 duk_debug_write_cstring(thr, "");
1129 duk_debug_write_uint(thr, 0);
1130 }
1131 }
1132
1133 duk_debug_write_eom(thr);
1134 }
1135 #endif /* DUK_USE_DEBUGGER_THROW_NOTIFY */
1136
1137 /*
1138 * Debug message processing
1139 */
1140
1141 /* Skip dvalue. */
duk__debug_skip_dvalue(duk_hthread * thr)1142 DUK_LOCAL duk_bool_t duk__debug_skip_dvalue(duk_hthread *thr) {
1143 duk_uint8_t x;
1144 duk_uint32_t len;
1145
1146 x = duk_debug_read_byte(thr);
1147
1148 if (x >= 0xc0) {
1149 duk_debug_skip_byte(thr);
1150 return 0;
1151 }
1152 if (x >= 0x80) {
1153 return 0;
1154 }
1155 if (x >= 0x60) {
1156 duk_debug_skip_bytes(thr, (duk_size_t) (x - 0x60));
1157 return 0;
1158 }
1159 switch(x) {
1160 case DUK_DBG_IB_EOM:
1161 return 1; /* Return 1: got EOM */
1162 case DUK_DBG_IB_REQUEST:
1163 case DUK_DBG_IB_REPLY:
1164 case DUK_DBG_IB_ERROR:
1165 case DUK_DBG_IB_NOTIFY:
1166 break;
1167 case DUK_DBG_IB_INT4:
1168 (void) duk__debug_read_uint32_raw(thr);
1169 break;
1170 case DUK_DBG_IB_STR4:
1171 case DUK_DBG_IB_BUF4:
1172 len = duk__debug_read_uint32_raw(thr);
1173 duk_debug_skip_bytes(thr, len);
1174 break;
1175 case DUK_DBG_IB_STR2:
1176 case DUK_DBG_IB_BUF2:
1177 len = duk__debug_read_uint16_raw(thr);
1178 duk_debug_skip_bytes(thr, len);
1179 break;
1180 case DUK_DBG_IB_UNUSED:
1181 case DUK_DBG_IB_UNDEFINED:
1182 case DUK_DBG_IB_NULL:
1183 case DUK_DBG_IB_TRUE:
1184 case DUK_DBG_IB_FALSE:
1185 break;
1186 case DUK_DBG_IB_NUMBER:
1187 duk_debug_skip_bytes(thr, 8);
1188 break;
1189 case DUK_DBG_IB_OBJECT:
1190 duk_debug_skip_byte(thr);
1191 len = duk_debug_read_byte(thr);
1192 duk_debug_skip_bytes(thr, len);
1193 break;
1194 case DUK_DBG_IB_POINTER:
1195 case DUK_DBG_IB_HEAPPTR:
1196 len = duk_debug_read_byte(thr);
1197 duk_debug_skip_bytes(thr, len);
1198 break;
1199 case DUK_DBG_IB_LIGHTFUNC:
1200 duk_debug_skip_bytes(thr, 2);
1201 len = duk_debug_read_byte(thr);
1202 duk_debug_skip_bytes(thr, len);
1203 break;
1204 default:
1205 goto fail;
1206 }
1207
1208 return 0;
1209
1210 fail:
1211 DUK__SET_CONN_BROKEN(thr, 1);
1212 return 1; /* Pretend like we got EOM */
1213 }
1214
1215 /* Skip dvalues to EOM. */
duk__debug_skip_to_eom(duk_hthread * thr)1216 DUK_LOCAL void duk__debug_skip_to_eom(duk_hthread *thr) {
1217 for (;;) {
1218 if (duk__debug_skip_dvalue(thr)) {
1219 break;
1220 }
1221 }
1222 }
1223
1224 /* Read and validate a call stack index. If index is invalid, write out an
1225 * error message and return zero.
1226 */
duk__debug_read_validate_csindex(duk_hthread * thr)1227 DUK_LOCAL duk_int32_t duk__debug_read_validate_csindex(duk_hthread *thr) {
1228 duk_int32_t level;
1229 level = duk_debug_read_int(thr);
1230 if (level >= 0 || -level > (duk_int32_t) thr->callstack_top) {
1231 duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid callstack index");
1232 return 0; /* zero indicates failure */
1233 }
1234 return level;
1235 }
1236
1237 /* Read a call stack index and lookup the corresponding duk_activation.
1238 * If index is invalid, write out an error message and return NULL.
1239 */
duk__debug_read_level_get_activation(duk_hthread * thr)1240 DUK_LOCAL duk_activation *duk__debug_read_level_get_activation(duk_hthread *thr) {
1241 duk_activation *act;
1242 duk_int32_t level;
1243
1244 level = duk_debug_read_int(thr);
1245 act = duk_hthread_get_activation_for_level(thr, level);
1246 if (act == NULL) {
1247 duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid callstack index");
1248 }
1249 return act;
1250 }
1251
1252 /*
1253 * Simple commands
1254 */
1255
duk__debug_handle_basic_info(duk_hthread * thr,duk_heap * heap)1256 DUK_LOCAL void duk__debug_handle_basic_info(duk_hthread *thr, duk_heap *heap) {
1257 DUK_UNREF(heap);
1258 DUK_D(DUK_DPRINT("debug command Version"));
1259
1260 duk_debug_write_reply(thr);
1261 duk_debug_write_int(thr, DUK_VERSION);
1262 duk_debug_write_cstring(thr, DUK_GIT_DESCRIBE);
1263 duk_debug_write_cstring(thr, DUK_USE_TARGET_INFO);
1264 #if defined(DUK_USE_DOUBLE_LE)
1265 duk_debug_write_int(thr, 1);
1266 #elif defined(DUK_USE_DOUBLE_ME)
1267 duk_debug_write_int(thr, 2);
1268 #elif defined(DUK_USE_DOUBLE_BE)
1269 duk_debug_write_int(thr, 3);
1270 #else
1271 duk_debug_write_int(thr, 0);
1272 #endif
1273 duk_debug_write_int(thr, (duk_int_t) sizeof(void *));
1274 duk_debug_write_eom(thr);
1275 }
1276
duk__debug_handle_trigger_status(duk_hthread * thr,duk_heap * heap)1277 DUK_LOCAL void duk__debug_handle_trigger_status(duk_hthread *thr, duk_heap *heap) {
1278 DUK_UNREF(heap);
1279 DUK_D(DUK_DPRINT("debug command TriggerStatus"));
1280
1281 duk_debug_write_reply(thr);
1282 duk_debug_write_eom(thr);
1283 heap->dbg_state_dirty = 1;
1284 }
1285
duk__debug_handle_pause(duk_hthread * thr,duk_heap * heap)1286 DUK_LOCAL void duk__debug_handle_pause(duk_hthread *thr, duk_heap *heap) {
1287 DUK_D(DUK_DPRINT("debug command Pause"));
1288 duk_debug_set_paused(heap);
1289 duk_debug_write_reply(thr);
1290 duk_debug_write_eom(thr);
1291 }
1292
duk__debug_handle_resume(duk_hthread * thr,duk_heap * heap)1293 DUK_LOCAL void duk__debug_handle_resume(duk_hthread *thr, duk_heap *heap) {
1294 duk_small_uint_t pause_flags;
1295
1296 DUK_D(DUK_DPRINT("debug command Resume"));
1297
1298 duk_debug_clear_paused(heap);
1299
1300 pause_flags = 0;
1301 #if 0 /* manual testing */
1302 pause_flags |= DUK_PAUSE_FLAG_ONE_OPCODE;
1303 pause_flags |= DUK_PAUSE_FLAG_CAUGHT_ERROR;
1304 pause_flags |= DUK_PAUSE_FLAG_UNCAUGHT_ERROR;
1305 #endif
1306 #if defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT)
1307 pause_flags |= DUK_PAUSE_FLAG_UNCAUGHT_ERROR;
1308 #endif
1309
1310 duk__debug_set_pause_state(thr, heap, pause_flags);
1311
1312 duk_debug_write_reply(thr);
1313 duk_debug_write_eom(thr);
1314 }
1315
duk__debug_handle_step(duk_hthread * thr,duk_heap * heap,duk_int32_t cmd)1316 DUK_LOCAL void duk__debug_handle_step(duk_hthread *thr, duk_heap *heap, duk_int32_t cmd) {
1317 duk_small_uint_t pause_flags;
1318
1319 DUK_D(DUK_DPRINT("debug command StepInto/StepOver/StepOut: %d", (int) cmd));
1320
1321 if (cmd == DUK_DBG_CMD_STEPINTO) {
1322 pause_flags = DUK_PAUSE_FLAG_LINE_CHANGE |
1323 DUK_PAUSE_FLAG_FUNC_ENTRY |
1324 DUK_PAUSE_FLAG_FUNC_EXIT;
1325 } else if (cmd == DUK_DBG_CMD_STEPOVER) {
1326 pause_flags = DUK_PAUSE_FLAG_LINE_CHANGE |
1327 DUK_PAUSE_FLAG_FUNC_EXIT;
1328 } else {
1329 DUK_ASSERT(cmd == DUK_DBG_CMD_STEPOUT);
1330 pause_flags = DUK_PAUSE_FLAG_FUNC_EXIT;
1331 }
1332 #if defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT)
1333 pause_flags |= DUK_PAUSE_FLAG_UNCAUGHT_ERROR;
1334 #endif
1335
1336 /* If current activation doesn't have line information, line-based
1337 * pause flags are automatically disabled. As a result, e.g.
1338 * StepInto will then pause on (native) function entry or exit.
1339 */
1340 duk_debug_clear_paused(heap);
1341 duk__debug_set_pause_state(thr, heap, pause_flags);
1342
1343 duk_debug_write_reply(thr);
1344 duk_debug_write_eom(thr);
1345 }
1346
duk__debug_handle_list_break(duk_hthread * thr,duk_heap * heap)1347 DUK_LOCAL void duk__debug_handle_list_break(duk_hthread *thr, duk_heap *heap) {
1348 duk_small_int_t i;
1349
1350 DUK_D(DUK_DPRINT("debug command ListBreak"));
1351 duk_debug_write_reply(thr);
1352 for (i = 0; i < (duk_small_int_t) heap->dbg_breakpoint_count; i++) {
1353 duk_debug_write_hstring(thr, heap->dbg_breakpoints[i].filename);
1354 duk_debug_write_uint(thr, (duk_uint32_t) heap->dbg_breakpoints[i].line);
1355 }
1356 duk_debug_write_eom(thr);
1357 }
1358
duk__debug_handle_add_break(duk_hthread * thr,duk_heap * heap)1359 DUK_LOCAL void duk__debug_handle_add_break(duk_hthread *thr, duk_heap *heap) {
1360 duk_hstring *filename;
1361 duk_uint32_t linenumber;
1362 duk_small_int_t idx;
1363
1364 DUK_UNREF(heap);
1365
1366 filename = duk_debug_read_hstring(thr);
1367 linenumber = (duk_uint32_t) duk_debug_read_int(thr);
1368 DUK_D(DUK_DPRINT("debug command AddBreak: %!O:%ld", (duk_hobject *) filename, (long) linenumber));
1369 idx = duk_debug_add_breakpoint(thr, filename, linenumber);
1370 if (idx >= 0) {
1371 duk_debug_write_reply(thr);
1372 duk_debug_write_int(thr, (duk_int32_t) idx);
1373 duk_debug_write_eom(thr);
1374 } else {
1375 duk_debug_write_error_eom(thr, DUK_DBG_ERR_TOOMANY, "no space for breakpoint");
1376 }
1377 }
1378
duk__debug_handle_del_break(duk_hthread * thr,duk_heap * heap)1379 DUK_LOCAL void duk__debug_handle_del_break(duk_hthread *thr, duk_heap *heap) {
1380 duk_small_uint_t idx;
1381
1382 DUK_UNREF(heap);
1383
1384 DUK_D(DUK_DPRINT("debug command DelBreak"));
1385 idx = (duk_small_uint_t) duk_debug_read_int(thr);
1386 if (duk_debug_remove_breakpoint(thr, idx)) {
1387 duk_debug_write_reply(thr);
1388 duk_debug_write_eom(thr);
1389 } else {
1390 duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid breakpoint index");
1391 }
1392 }
1393
duk__debug_handle_get_var(duk_hthread * thr,duk_heap * heap)1394 DUK_LOCAL void duk__debug_handle_get_var(duk_hthread *thr, duk_heap *heap) {
1395 duk_activation *act;
1396 duk_hstring *str;
1397 duk_bool_t rc;
1398
1399 DUK_UNREF(heap);
1400 DUK_D(DUK_DPRINT("debug command GetVar"));
1401
1402 act = duk__debug_read_level_get_activation(thr);
1403 if (act == NULL) {
1404 return;
1405 }
1406 str = duk_debug_read_hstring(thr); /* push to stack */
1407 DUK_ASSERT(str != NULL);
1408
1409 rc = duk_js_getvar_activation(thr, act, str, 0);
1410
1411 duk_debug_write_reply(thr);
1412 if (rc) {
1413 duk_debug_write_int(thr, 1);
1414 DUK_ASSERT(duk_get_tval(thr, -2) != NULL);
1415 duk_debug_write_tval(thr, duk_get_tval(thr, -2));
1416 } else {
1417 duk_debug_write_int(thr, 0);
1418 duk_debug_write_unused(thr);
1419 }
1420 duk_debug_write_eom(thr);
1421 }
1422
duk__debug_handle_put_var(duk_hthread * thr,duk_heap * heap)1423 DUK_LOCAL void duk__debug_handle_put_var(duk_hthread *thr, duk_heap *heap) {
1424 duk_activation *act;
1425 duk_hstring *str;
1426 duk_tval *tv;
1427
1428 DUK_UNREF(heap);
1429 DUK_D(DUK_DPRINT("debug command PutVar"));
1430
1431 act = duk__debug_read_level_get_activation(thr);
1432 if (act == NULL) {
1433 return;
1434 }
1435 str = duk_debug_read_hstring(thr); /* push to stack */
1436 DUK_ASSERT(str != NULL);
1437 tv = duk_debug_read_tval(thr);
1438 if (tv == NULL) {
1439 /* detached */
1440 return;
1441 }
1442
1443 duk_js_putvar_activation(thr, act, str, tv, 0);
1444
1445 /* XXX: Current putvar implementation doesn't have a success flag,
1446 * add one and send to debug client?
1447 */
1448 duk_debug_write_reply(thr);
1449 duk_debug_write_eom(thr);
1450 }
1451
duk__debug_handle_get_call_stack(duk_hthread * thr,duk_heap * heap)1452 DUK_LOCAL void duk__debug_handle_get_call_stack(duk_hthread *thr, duk_heap *heap) {
1453 duk_hthread *curr_thr = thr;
1454 duk_activation *curr_act;
1455 duk_uint_fast32_t pc;
1456 duk_uint_fast32_t line;
1457
1458 DUK_ASSERT(thr != NULL);
1459 DUK_UNREF(heap);
1460
1461 duk_debug_write_reply(thr);
1462 while (curr_thr != NULL) {
1463 for (curr_act = curr_thr->callstack_curr; curr_act != NULL; curr_act = curr_act->parent) {
1464 /* PC/line semantics here are:
1465 * - For callstack top we're conceptually between two
1466 * opcodes and current PC indicates next line to
1467 * execute, so report that (matches Status).
1468 * - For other activations we're conceptually still
1469 * executing the instruction at PC-1, so report that
1470 * (matches error stacktrace behavior).
1471 * - See: https://github.com/svaarala/duktape/issues/281
1472 */
1473
1474 /* XXX: optimize to use direct reads, i.e. avoid
1475 * value stack operations.
1476 */
1477 duk_push_tval(thr, &curr_act->tv_func);
1478 duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_FILE_NAME);
1479 duk__debug_write_hstring_safe_top(thr);
1480 duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_NAME);
1481 duk__debug_write_hstring_safe_top(thr);
1482 pc = duk_hthread_get_act_curr_pc(thr, curr_act);
1483 if (curr_act != curr_thr->callstack_curr && pc > 0) {
1484 pc--;
1485 }
1486 line = duk_hobject_pc2line_query(thr, -3, pc);
1487 duk_debug_write_uint(thr, (duk_uint32_t) line);
1488 duk_debug_write_uint(thr, (duk_uint32_t) pc);
1489 duk_pop_3(thr);
1490 }
1491 curr_thr = curr_thr->resumer;
1492 }
1493 /* SCANBUILD: warning about 'thr' potentially being NULL here,
1494 * warning is incorrect because thr != NULL always here.
1495 */
1496 duk_debug_write_eom(thr);
1497 }
1498
duk__debug_handle_get_locals(duk_hthread * thr,duk_heap * heap)1499 DUK_LOCAL void duk__debug_handle_get_locals(duk_hthread *thr, duk_heap *heap) {
1500 duk_activation *act;
1501 duk_hstring *varname;
1502
1503 DUK_UNREF(heap);
1504
1505 act = duk__debug_read_level_get_activation(thr);
1506 if (act == NULL) {
1507 return;
1508 }
1509
1510 duk_debug_write_reply(thr);
1511
1512 /* XXX: several nice-to-have improvements here:
1513 * - Use direct reads avoiding value stack operations
1514 * - Avoid triggering getters, indicate getter values to debug client
1515 * - If side effects are possible, add error catching
1516 */
1517
1518 if (DUK_TVAL_IS_OBJECT(&act->tv_func)) {
1519 duk_hobject *h_func = DUK_TVAL_GET_OBJECT(&act->tv_func);
1520 duk_hobject *h_varmap;
1521
1522 h_varmap = duk_hobject_get_varmap(thr, h_func);
1523 if (h_varmap != NULL) {
1524 duk_push_hobject(thr, h_varmap);
1525 duk_enum(thr, -1, 0 /*enum_flags*/);
1526 while (duk_next(thr, -1 /*enum_index*/, 0 /*get_value*/)) {
1527 varname = duk_known_hstring(thr, -1);
1528
1529 duk_js_getvar_activation(thr, act, varname, 0 /*throw_flag*/);
1530 /* [ ... func varmap enum key value this ] */
1531 duk_debug_write_hstring(thr, duk_get_hstring(thr, -3));
1532 duk_debug_write_tval(thr, duk_get_tval(thr, -2));
1533 duk_pop_3(thr); /* -> [ ... func varmap enum ] */
1534 }
1535 } else {
1536 DUK_D(DUK_DPRINT("varmap missing in GetLocals, ignore"));
1537 }
1538 } else {
1539 DUK_D(DUK_DPRINT("varmap is not an object in GetLocals, ignore"));
1540 }
1541
1542 duk_debug_write_eom(thr);
1543 }
1544
duk__debug_handle_eval(duk_hthread * thr,duk_heap * heap)1545 DUK_LOCAL void duk__debug_handle_eval(duk_hthread *thr, duk_heap *heap) {
1546 duk_small_uint_t call_flags;
1547 duk_int_t call_ret;
1548 duk_small_int_t eval_err;
1549 duk_bool_t direct_eval;
1550 duk_int32_t level;
1551 duk_idx_t idx_func;
1552
1553 DUK_UNREF(heap);
1554
1555 DUK_D(DUK_DPRINT("debug command Eval"));
1556
1557 /* The eval code is executed within the lexical environment of a specified
1558 * activation. For now, use global object eval() function, with the eval
1559 * considered a 'direct call to eval'.
1560 *
1561 * Callstack index for debug commands only affects scope -- the callstack
1562 * as seen by, e.g. Duktape.act() will be the same regardless.
1563 */
1564
1565 /* nargs == 2 so we can pass a callstack index to eval(). */
1566 idx_func = duk_get_top(thr);
1567 duk_push_c_function(thr, duk_bi_global_object_eval, 2 /*nargs*/);
1568 duk_push_undefined(thr); /* 'this' binding shouldn't matter here */
1569
1570 /* Read callstack index, if non-null. */
1571 if (duk_debug_peek_byte(thr) == DUK_DBG_IB_NULL) {
1572 direct_eval = 0;
1573 level = -1; /* Not needed, but silences warning. */
1574 (void) duk_debug_read_byte(thr);
1575 } else {
1576 direct_eval = 1;
1577 level = duk__debug_read_validate_csindex(thr);
1578 if (level == 0) {
1579 return;
1580 }
1581 }
1582
1583 DUK_ASSERT(!direct_eval ||
1584 (level < 0 && -level <= (duk_int32_t) thr->callstack_top));
1585
1586 (void) duk_debug_read_hstring(thr);
1587 if (direct_eval) {
1588 duk_push_int(thr, level - 1); /* compensate for eval() call */
1589 }
1590
1591 /* [ ... eval "eval" eval_input level? ] */
1592
1593 call_flags = 0;
1594 if (direct_eval) {
1595 duk_activation *act;
1596 duk_hobject *fun;
1597
1598 act = duk_hthread_get_activation_for_level(thr, level);
1599 if (act != NULL) {
1600 fun = DUK_ACT_GET_FUNC(act);
1601 if (fun != NULL && DUK_HOBJECT_IS_COMPFUNC(fun)) {
1602 /* Direct eval requires that there's a current
1603 * activation and it is an ECMAScript function.
1604 * When Eval is executed from e.g. cooperate API
1605 * call we'll need to do an indirect eval instead.
1606 */
1607 call_flags |= DUK_CALL_FLAG_DIRECT_EVAL;
1608 }
1609 }
1610 }
1611
1612 call_ret = duk_pcall_method_flags(thr, duk_get_top(thr) - (idx_func + 2), call_flags);
1613
1614 if (call_ret == DUK_EXEC_SUCCESS) {
1615 eval_err = 0;
1616 /* Use result value as is. */
1617 } else {
1618 /* For errors a string coerced result is most informative
1619 * right now, as the debug client doesn't have the capability
1620 * to traverse the error object.
1621 */
1622 eval_err = 1;
1623 duk_safe_to_string(thr, -1);
1624 }
1625
1626 /* [ ... result ] */
1627
1628 duk_debug_write_reply(thr);
1629 duk_debug_write_int(thr, (duk_int32_t) eval_err);
1630 DUK_ASSERT(duk_get_tval(thr, -1) != NULL);
1631 duk_debug_write_tval(thr, duk_get_tval(thr, -1));
1632 duk_debug_write_eom(thr);
1633 }
1634
duk__debug_handle_detach(duk_hthread * thr,duk_heap * heap)1635 DUK_LOCAL void duk__debug_handle_detach(duk_hthread *thr, duk_heap *heap) {
1636 DUK_UNREF(heap);
1637 DUK_D(DUK_DPRINT("debug command Detach"));
1638
1639 duk_debug_write_reply(thr);
1640 duk_debug_write_eom(thr);
1641
1642 DUK_D(DUK_DPRINT("debug connection detached, mark broken"));
1643 DUK__SET_CONN_BROKEN(thr, 0); /* not an error */
1644 }
1645
duk__debug_handle_apprequest(duk_hthread * thr,duk_heap * heap)1646 DUK_LOCAL void duk__debug_handle_apprequest(duk_hthread *thr, duk_heap *heap) {
1647 duk_idx_t old_top;
1648
1649 DUK_D(DUK_DPRINT("debug command AppRequest"));
1650
1651 old_top = duk_get_top(thr); /* save stack top */
1652
1653 if (heap->dbg_request_cb != NULL) {
1654 duk_idx_t nrets;
1655 duk_idx_t nvalues = 0;
1656 duk_idx_t top, idx;
1657
1658 /* Read tvals from the message and push them onto the valstack,
1659 * then call the request callback to process the request.
1660 */
1661 while (duk_debug_peek_byte(thr) != DUK_DBG_IB_EOM) {
1662 duk_tval *tv;
1663 if (!duk_check_stack(thr, 1)) {
1664 DUK_D(DUK_DPRINT("failed to allocate space for request dvalue(s)"));
1665 goto fail;
1666 }
1667 tv = duk_debug_read_tval(thr); /* push to stack */
1668 if (tv == NULL) {
1669 /* detached */
1670 return;
1671 }
1672 nvalues++;
1673 }
1674 DUK_ASSERT(duk_get_top(thr) == old_top + nvalues);
1675
1676 /* Request callback should push values for reply to client onto valstack */
1677 DUK_D(DUK_DPRINT("calling into AppRequest request_cb with nvalues=%ld, old_top=%ld, top=%ld",
1678 (long) nvalues, (long) old_top, (long) duk_get_top(thr)));
1679 nrets = heap->dbg_request_cb(thr, heap->dbg_udata, nvalues);
1680 DUK_D(DUK_DPRINT("returned from AppRequest request_cb; nvalues=%ld -> nrets=%ld, old_top=%ld, top=%ld",
1681 (long) nvalues, (long) nrets, (long) old_top, (long) duk_get_top(thr)));
1682 if (nrets >= 0) {
1683 DUK_ASSERT(duk_get_top(thr) >= old_top + nrets);
1684 if (duk_get_top(thr) < old_top + nrets) {
1685 DUK_D(DUK_DPRINT("AppRequest callback doesn't match value stack configuration, "
1686 "top=%ld < old_top=%ld + nrets=%ld; "
1687 "this might mean it's unsafe to continue!",
1688 (long) duk_get_top(thr), (long) old_top, (long) nrets));
1689 goto fail;
1690 }
1691
1692 /* Reply with tvals pushed by request callback */
1693 duk_debug_write_byte(thr, DUK_DBG_IB_REPLY);
1694 top = duk_get_top(thr);
1695 for (idx = top - nrets; idx < top; idx++) {
1696 duk_debug_write_tval(thr, DUK_GET_TVAL_POSIDX(thr, idx));
1697 }
1698 duk_debug_write_eom(thr);
1699 } else {
1700 DUK_ASSERT(duk_get_top(thr) >= old_top + 1);
1701 if (duk_get_top(thr) < old_top + 1) {
1702 DUK_D(DUK_DPRINT("request callback return value doesn't match value stack configuration"));
1703 goto fail;
1704 }
1705 duk_debug_write_error_eom(thr, DUK_DBG_ERR_APPLICATION, duk_get_string(thr, -1));
1706 }
1707
1708 duk_set_top(thr, old_top); /* restore stack top */
1709 } else {
1710 DUK_D(DUK_DPRINT("no request callback, treat AppRequest as unsupported"));
1711 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "AppRequest unsupported by target");
1712 }
1713
1714 return;
1715
1716 fail:
1717 duk_set_top(thr, old_top); /* restore stack top */
1718 DUK__SET_CONN_BROKEN(thr, 1);
1719 }
1720
1721 /*
1722 * DumpHeap command
1723 */
1724
1725 #if defined(DUK_USE_DEBUGGER_DUMPHEAP)
1726 /* XXX: this has some overlap with object inspection; remove this and make
1727 * DumpHeap return lists of heapptrs instead?
1728 */
duk__debug_dump_heaphdr(duk_hthread * thr,duk_heap * heap,duk_heaphdr * hdr)1729 DUK_LOCAL void duk__debug_dump_heaphdr(duk_hthread *thr, duk_heap *heap, duk_heaphdr *hdr) {
1730 DUK_UNREF(heap);
1731
1732 duk_debug_write_heapptr(thr, hdr);
1733 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HEAPHDR_GET_TYPE(hdr));
1734 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HEAPHDR_GET_FLAGS_RAW(hdr));
1735 #if defined(DUK_USE_REFERENCE_COUNTING)
1736 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HEAPHDR_GET_REFCOUNT(hdr));
1737 #else
1738 duk_debug_write_int(thr, (duk_int32_t) -1);
1739 #endif
1740
1741 switch (DUK_HEAPHDR_GET_TYPE(hdr)) {
1742 case DUK_HTYPE_STRING: {
1743 duk_hstring *h = (duk_hstring *) hdr;
1744
1745 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HSTRING_GET_BYTELEN(h));
1746 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HSTRING_GET_CHARLEN(h));
1747 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HSTRING_GET_HASH(h));
1748 duk_debug_write_hstring(thr, h);
1749 break;
1750 }
1751 case DUK_HTYPE_OBJECT: {
1752 duk_hobject *h = (duk_hobject *) hdr;
1753 duk_hstring *k;
1754 duk_uint_fast32_t i;
1755
1756 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_CLASS_NUMBER(h));
1757 duk_debug_write_heapptr(thr, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(heap, h));
1758 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_ESIZE(h));
1759 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_ENEXT(h));
1760 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_ASIZE(h));
1761 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_HSIZE(h));
1762
1763 for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) {
1764 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_E_GET_FLAGS(heap, h, i));
1765 k = DUK_HOBJECT_E_GET_KEY(heap, h, i);
1766 duk_debug_write_heapptr(thr, (duk_heaphdr *) k);
1767 if (k == NULL) {
1768 duk_debug_write_int(thr, 0); /* isAccessor */
1769 duk_debug_write_unused(thr);
1770 continue;
1771 }
1772 if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, h, i)) {
1773 duk_debug_write_int(thr, 1); /* isAccessor */
1774 duk_debug_write_heapptr(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.get);
1775 duk_debug_write_heapptr(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.set);
1776 } else {
1777 duk_debug_write_int(thr, 0); /* isAccessor */
1778
1779 duk__debug_write_tval_heapptr(thr, &DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->v);
1780 }
1781 }
1782
1783 for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) {
1784 /* Note: array dump will include elements beyond
1785 * 'length'.
1786 */
1787 duk__debug_write_tval_heapptr(thr, DUK_HOBJECT_A_GET_VALUE_PTR(heap, h, i));
1788 }
1789 break;
1790 }
1791 case DUK_HTYPE_BUFFER: {
1792 duk_hbuffer *h = (duk_hbuffer *) hdr;
1793
1794 duk_debug_write_uint(thr, (duk_uint32_t) DUK_HBUFFER_GET_SIZE(h));
1795 duk_debug_write_buffer(thr, (const char *) DUK_HBUFFER_GET_DATA_PTR(heap, h), (duk_size_t) DUK_HBUFFER_GET_SIZE(h));
1796 break;
1797 }
1798 default: {
1799 DUK_D(DUK_DPRINT("invalid htype: %d", (int) DUK_HEAPHDR_GET_TYPE(hdr)));
1800 }
1801 }
1802 }
1803
duk__debug_dump_heap_allocated(duk_hthread * thr,duk_heap * heap)1804 DUK_LOCAL void duk__debug_dump_heap_allocated(duk_hthread *thr, duk_heap *heap) {
1805 duk_heaphdr *hdr;
1806
1807 hdr = heap->heap_allocated;
1808 while (hdr != NULL) {
1809 duk__debug_dump_heaphdr(thr, heap, hdr);
1810 hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
1811 }
1812 }
1813
duk__debug_dump_strtab(duk_hthread * thr,duk_heap * heap)1814 DUK_LOCAL void duk__debug_dump_strtab(duk_hthread *thr, duk_heap *heap) {
1815 duk_uint32_t i;
1816 duk_hstring *h;
1817
1818 for (i = 0; i < heap->st_size; i++) {
1819 #if defined(DUK_USE_STRTAB_PTRCOMP)
1820 h = DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, heap->strtable16[i]);
1821 #else
1822 h = heap->strtable[i];
1823 #endif
1824 while (h != NULL) {
1825 duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h);
1826 h = h->hdr.h_next;
1827 }
1828 }
1829 }
1830
duk__debug_handle_dump_heap(duk_hthread * thr,duk_heap * heap)1831 DUK_LOCAL void duk__debug_handle_dump_heap(duk_hthread *thr, duk_heap *heap) {
1832 DUK_D(DUK_DPRINT("debug command DumpHeap"));
1833
1834 duk_debug_write_reply(thr);
1835 duk__debug_dump_heap_allocated(thr, heap);
1836 duk__debug_dump_strtab(thr, heap);
1837 duk_debug_write_eom(thr);
1838 }
1839 #endif /* DUK_USE_DEBUGGER_DUMPHEAP */
1840
duk__debug_handle_get_bytecode(duk_hthread * thr,duk_heap * heap)1841 DUK_LOCAL void duk__debug_handle_get_bytecode(duk_hthread *thr, duk_heap *heap) {
1842 duk_activation *act;
1843 duk_hcompfunc *fun = NULL;
1844 duk_size_t i, n;
1845 duk_tval *tv;
1846 duk_hobject **fn;
1847 duk_int32_t level = -1;
1848 duk_uint8_t ibyte;
1849
1850 DUK_UNREF(heap);
1851
1852 DUK_D(DUK_DPRINT("debug command GetBytecode"));
1853
1854 ibyte = duk_debug_peek_byte(thr);
1855 if (ibyte != DUK_DBG_IB_EOM) {
1856 tv = duk_debug_read_tval(thr);
1857 if (tv == NULL) {
1858 /* detached */
1859 return;
1860 }
1861 if (DUK_TVAL_IS_OBJECT(tv)) {
1862 /* tentative, checked later */
1863 fun = (duk_hcompfunc *) DUK_TVAL_GET_OBJECT(tv);
1864 DUK_ASSERT(fun != NULL);
1865 } else if (DUK_TVAL_IS_NUMBER(tv)) {
1866 level = (duk_int32_t) DUK_TVAL_GET_NUMBER(tv);
1867 } else {
1868 DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!T", tv));
1869 goto fail_args;
1870 }
1871 }
1872
1873 if (fun == NULL) {
1874 act = duk_hthread_get_activation_for_level(thr, level);
1875 if (act == NULL) {
1876 goto fail_index;
1877 }
1878 fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);
1879 }
1880
1881 if (fun == NULL || !DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun)) {
1882 DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!O", fun));
1883 goto fail_args;
1884 }
1885 DUK_ASSERT(fun != NULL && DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun));
1886
1887 duk_debug_write_reply(thr);
1888 n = DUK_HCOMPFUNC_GET_CONSTS_COUNT(heap, fun);
1889 duk_debug_write_int(thr, (duk_int32_t) n);
1890 tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(heap, fun);
1891 for (i = 0; i < n; i++) {
1892 duk_debug_write_tval(thr, tv);
1893 tv++;
1894 }
1895 n = DUK_HCOMPFUNC_GET_FUNCS_COUNT(heap, fun);
1896 duk_debug_write_int(thr, (duk_int32_t) n);
1897 fn = DUK_HCOMPFUNC_GET_FUNCS_BASE(heap, fun);
1898 for (i = 0; i < n; i++) {
1899 duk_debug_write_hobject(thr, *fn);
1900 fn++;
1901 }
1902 duk_debug_write_string(thr,
1903 (const char *) DUK_HCOMPFUNC_GET_CODE_BASE(heap, fun),
1904 (duk_size_t) DUK_HCOMPFUNC_GET_CODE_SIZE(heap, fun));
1905 duk_debug_write_eom(thr);
1906 return;
1907
1908 fail_args:
1909 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid argument");
1910 return;
1911
1912 fail_index:
1913 duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid callstack index");
1914 return;
1915 }
1916
1917 /*
1918 * Object inspection commands: GetHeapObjInfo, GetObjPropDesc,
1919 * GetObjPropDescRange
1920 */
1921
1922 #if defined(DUK_USE_DEBUGGER_INSPECT)
1923
1924 #if 0 /* pruned */
1925 DUK_LOCAL const char * const duk__debug_getinfo_heaphdr_keys[] = {
1926 "reachable",
1927 "temproot",
1928 "finalizable",
1929 "finalized",
1930 "readonly"
1931 /* NULL not needed here */
1932 };
1933 DUK_LOCAL duk_uint_t duk__debug_getinfo_heaphdr_masks[] = {
1934 DUK_HEAPHDR_FLAG_REACHABLE,
1935 DUK_HEAPHDR_FLAG_TEMPROOT,
1936 DUK_HEAPHDR_FLAG_FINALIZABLE,
1937 DUK_HEAPHDR_FLAG_FINALIZED,
1938 DUK_HEAPHDR_FLAG_READONLY,
1939 0 /* terminator */
1940 };
1941 #endif
1942 DUK_LOCAL const char * const duk__debug_getinfo_hstring_keys[] = {
1943 #if 0
1944 "arridx",
1945 "symbol",
1946 "hidden",
1947 "reserved_word",
1948 "strict_reserved_word",
1949 "eval_or_arguments",
1950 #endif
1951 "extdata"
1952 /* NULL not needed here */
1953 };
1954 DUK_LOCAL duk_uint_t duk__debug_getinfo_hstring_masks[] = {
1955 #if 0
1956 DUK_HSTRING_FLAG_ARRIDX,
1957 DUK_HSTRING_FLAG_SYMBOL,
1958 DUK_HSTRING_FLAG_HIDDEN,
1959 DUK_HSTRING_FLAG_RESERVED_WORD,
1960 DUK_HSTRING_FLAG_STRICT_RESERVED_WORD,
1961 DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS,
1962 #endif
1963 DUK_HSTRING_FLAG_EXTDATA,
1964 0 /* terminator */
1965 };
1966 DUK_LOCAL const char * const duk__debug_getinfo_hobject_keys[] = {
1967 "extensible",
1968 "constructable",
1969 "callable",
1970 "boundfunc",
1971 "compfunc",
1972 "natfunc",
1973 "bufobj",
1974 "fastrefs",
1975 "array_part",
1976 "strict",
1977 "notail",
1978 "newenv",
1979 "namebinding",
1980 "createargs",
1981 "have_finalizer",
1982 "exotic_array",
1983 "exotic_stringobj",
1984 "exotic_arguments",
1985 "exotic_proxyobj",
1986 "special_call"
1987 /* NULL not needed here */
1988 };
1989 DUK_LOCAL duk_uint_t duk__debug_getinfo_hobject_masks[] = {
1990 DUK_HOBJECT_FLAG_EXTENSIBLE,
1991 DUK_HOBJECT_FLAG_CONSTRUCTABLE,
1992 DUK_HOBJECT_FLAG_CALLABLE,
1993 DUK_HOBJECT_FLAG_BOUNDFUNC,
1994 DUK_HOBJECT_FLAG_COMPFUNC,
1995 DUK_HOBJECT_FLAG_NATFUNC,
1996 DUK_HOBJECT_FLAG_BUFOBJ,
1997 DUK_HOBJECT_FLAG_FASTREFS,
1998 DUK_HOBJECT_FLAG_ARRAY_PART,
1999 DUK_HOBJECT_FLAG_STRICT,
2000 DUK_HOBJECT_FLAG_NOTAIL,
2001 DUK_HOBJECT_FLAG_NEWENV,
2002 DUK_HOBJECT_FLAG_NAMEBINDING,
2003 DUK_HOBJECT_FLAG_CREATEARGS,
2004 DUK_HOBJECT_FLAG_HAVE_FINALIZER,
2005 DUK_HOBJECT_FLAG_EXOTIC_ARRAY,
2006 DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ,
2007 DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS,
2008 DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ,
2009 DUK_HOBJECT_FLAG_SPECIAL_CALL,
2010 0 /* terminator */
2011 };
2012 DUK_LOCAL const char * const duk__debug_getinfo_hbuffer_keys[] = {
2013 "dynamic",
2014 "external"
2015 /* NULL not needed here */
2016 };
2017 DUK_LOCAL duk_uint_t duk__debug_getinfo_hbuffer_masks[] = {
2018 DUK_HBUFFER_FLAG_DYNAMIC,
2019 DUK_HBUFFER_FLAG_EXTERNAL,
2020 0 /* terminator */
2021 };
2022
duk__debug_getinfo_flags_key(duk_hthread * thr,const char * key)2023 DUK_LOCAL void duk__debug_getinfo_flags_key(duk_hthread *thr, const char *key) {
2024 duk_debug_write_uint(thr, 0);
2025 duk_debug_write_cstring(thr, key);
2026 }
2027
duk__debug_getinfo_prop_uint(duk_hthread * thr,const char * key,duk_uint_t val)2028 DUK_LOCAL void duk__debug_getinfo_prop_uint(duk_hthread *thr, const char *key, duk_uint_t val) {
2029 duk_debug_write_uint(thr, 0);
2030 duk_debug_write_cstring(thr, key);
2031 duk_debug_write_uint(thr, val);
2032 }
2033
duk__debug_getinfo_prop_int(duk_hthread * thr,const char * key,duk_int_t val)2034 DUK_LOCAL void duk__debug_getinfo_prop_int(duk_hthread *thr, const char *key, duk_int_t val) {
2035 duk_debug_write_uint(thr, 0);
2036 duk_debug_write_cstring(thr, key);
2037 duk_debug_write_int(thr, val);
2038 }
2039
duk__debug_getinfo_prop_bool(duk_hthread * thr,const char * key,duk_bool_t val)2040 DUK_LOCAL void duk__debug_getinfo_prop_bool(duk_hthread *thr, const char *key, duk_bool_t val) {
2041 duk_debug_write_uint(thr, 0);
2042 duk_debug_write_cstring(thr, key);
2043 duk_debug_write_boolean(thr, val);
2044 }
2045
duk__debug_getinfo_bitmask(duk_hthread * thr,const char * const * keys,duk_uint_t * masks,duk_uint_t flags)2046 DUK_LOCAL void duk__debug_getinfo_bitmask(duk_hthread *thr, const char * const * keys, duk_uint_t *masks, duk_uint_t flags) {
2047 const char *key;
2048 duk_uint_t mask;
2049
2050 for (;;) {
2051 mask = *masks++;
2052 if (mask == 0) {
2053 break;
2054 }
2055 key = *keys++;
2056 DUK_ASSERT(key != NULL);
2057
2058 DUK_DD(DUK_DDPRINT("inspect bitmask: key=%s, mask=0x%08lx, flags=0x%08lx", key, (unsigned long) mask, (unsigned long) flags));
2059 duk__debug_getinfo_prop_bool(thr, key, flags & mask);
2060 }
2061 }
2062
2063 /* Inspect a property using a virtual index into a conceptual property list
2064 * consisting of (1) all array part items from [0,a_size[ (even when above
2065 * .length) and (2) all entry part items from [0,e_next[. Unused slots are
2066 * indicated using dvalue 'unused'.
2067 */
duk__debug_getprop_index(duk_hthread * thr,duk_heap * heap,duk_hobject * h_obj,duk_uint_t idx)2068 DUK_LOCAL duk_bool_t duk__debug_getprop_index(duk_hthread *thr, duk_heap *heap, duk_hobject *h_obj, duk_uint_t idx) {
2069 duk_uint_t a_size;
2070 duk_tval *tv;
2071 duk_hstring *h_key;
2072 duk_hobject *h_getset;
2073 duk_uint_t flags;
2074
2075 DUK_UNREF(heap);
2076
2077 a_size = DUK_HOBJECT_GET_ASIZE(h_obj);
2078 if (idx < a_size) {
2079 duk_debug_write_uint(thr, DUK_PROPDESC_FLAGS_WEC);
2080 duk_debug_write_uint(thr, idx);
2081 tv = DUK_HOBJECT_A_GET_VALUE_PTR(heap, h_obj, idx);
2082 duk_debug_write_tval(thr, tv);
2083 return 1;
2084 }
2085
2086 idx -= a_size;
2087 if (idx >= DUK_HOBJECT_GET_ENEXT(h_obj)) {
2088 return 0;
2089 }
2090
2091 h_key = DUK_HOBJECT_E_GET_KEY(heap, h_obj, idx);
2092 if (h_key == NULL) {
2093 duk_debug_write_uint(thr, 0);
2094 duk_debug_write_null(thr);
2095 duk_debug_write_unused(thr);
2096 return 1;
2097 }
2098
2099 flags = DUK_HOBJECT_E_GET_FLAGS(heap, h_obj, idx);
2100 if (DUK_HSTRING_HAS_SYMBOL(h_key)) {
2101 flags |= DUK_DBG_PROPFLAG_SYMBOL;
2102 }
2103 if (DUK_HSTRING_HAS_HIDDEN(h_key)) {
2104 flags |= DUK_DBG_PROPFLAG_HIDDEN;
2105 }
2106 duk_debug_write_uint(thr, flags);
2107 duk_debug_write_hstring(thr, h_key);
2108 if (flags & DUK_PROPDESC_FLAG_ACCESSOR) {
2109 h_getset = DUK_HOBJECT_E_GET_VALUE_GETTER(heap, h_obj, idx);
2110 if (h_getset) {
2111 duk_debug_write_hobject(thr, h_getset);
2112 } else {
2113 duk_debug_write_null(thr);
2114 }
2115 h_getset = DUK_HOBJECT_E_GET_VALUE_SETTER(heap, h_obj, idx);
2116 if (h_getset) {
2117 duk_debug_write_hobject(thr, h_getset);
2118 } else {
2119 duk_debug_write_null(thr);
2120 }
2121 } else {
2122 tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, h_obj, idx);
2123 duk_debug_write_tval(thr, tv);
2124 }
2125 return 1;
2126 }
2127
duk__debug_handle_get_heap_obj_info(duk_hthread * thr,duk_heap * heap)2128 DUK_LOCAL void duk__debug_handle_get_heap_obj_info(duk_hthread *thr, duk_heap *heap) {
2129 duk_heaphdr *h;
2130
2131 DUK_D(DUK_DPRINT("debug command GetHeapObjInfo"));
2132 DUK_UNREF(heap);
2133
2134 DUK_ASSERT(sizeof(duk__debug_getinfo_hstring_keys) / sizeof(const char *) == sizeof(duk__debug_getinfo_hstring_masks) / sizeof(duk_uint_t) - 1);
2135 DUK_ASSERT(sizeof(duk__debug_getinfo_hobject_keys) / sizeof(const char *) == sizeof(duk__debug_getinfo_hobject_masks) / sizeof(duk_uint_t) - 1);
2136 DUK_ASSERT(sizeof(duk__debug_getinfo_hbuffer_keys) / sizeof(const char *) == sizeof(duk__debug_getinfo_hbuffer_masks) / sizeof(duk_uint_t) - 1);
2137
2138 h = duk_debug_read_any_ptr(thr);
2139 if (!h) {
2140 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid target");
2141 return;
2142 }
2143
2144 duk_debug_write_reply(thr);
2145
2146 /* As with all inspection code, we rely on the debug client providing
2147 * a valid, non-stale pointer: there's no portable way to safely
2148 * validate the pointer here.
2149 */
2150
2151 duk__debug_getinfo_flags_key(thr, "heapptr");
2152 duk_debug_write_heapptr(thr, h);
2153
2154 /* XXX: comes out as signed now */
2155 duk__debug_getinfo_prop_uint(thr, "heaphdr_flags", (duk_uint_t) DUK_HEAPHDR_GET_FLAGS(h));
2156 duk__debug_getinfo_prop_uint(thr, "heaphdr_type", (duk_uint_t) DUK_HEAPHDR_GET_TYPE(h));
2157 #if defined(DUK_USE_REFERENCE_COUNTING)
2158 duk__debug_getinfo_prop_uint(thr, "refcount", (duk_uint_t) DUK_HEAPHDR_GET_REFCOUNT(h));
2159 #endif
2160 #if 0 /* pruned */
2161 duk__debug_getinfo_bitmask(thr,
2162 duk__debug_getinfo_heaphdr_keys,
2163 duk__debug_getinfo_heaphdr_masks,
2164 DUK_HEAPHDR_GET_FLAGS_RAW(h));
2165 #endif
2166
2167 switch (DUK_HEAPHDR_GET_TYPE(h)) {
2168 case DUK_HTYPE_STRING: {
2169 duk_hstring *h_str;
2170
2171 h_str = (duk_hstring *) h;
2172 duk__debug_getinfo_bitmask(thr,
2173 duk__debug_getinfo_hstring_keys,
2174 duk__debug_getinfo_hstring_masks,
2175 DUK_HEAPHDR_GET_FLAGS_RAW(h));
2176 duk__debug_getinfo_prop_uint(thr, "bytelen", (duk_uint_t) DUK_HSTRING_GET_BYTELEN(h_str));
2177 duk__debug_getinfo_prop_uint(thr, "charlen", (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h_str));
2178 duk__debug_getinfo_prop_uint(thr, "hash", (duk_uint_t) DUK_HSTRING_GET_HASH(h_str));
2179 duk__debug_getinfo_flags_key(thr, "data");
2180 duk_debug_write_hstring(thr, h_str);
2181 break;
2182 }
2183 case DUK_HTYPE_OBJECT: {
2184 duk_hobject *h_obj;
2185 duk_hobject *h_proto;
2186
2187 h_obj = (duk_hobject *) h;
2188 h_proto = DUK_HOBJECT_GET_PROTOTYPE(heap, h_obj);
2189
2190 /* duk_hobject specific fields. */
2191 duk__debug_getinfo_bitmask(thr,
2192 duk__debug_getinfo_hobject_keys,
2193 duk__debug_getinfo_hobject_masks,
2194 DUK_HEAPHDR_GET_FLAGS_RAW(h));
2195 duk__debug_getinfo_prop_uint(thr, "class_number", DUK_HOBJECT_GET_CLASS_NUMBER(h_obj));
2196 duk__debug_getinfo_flags_key(thr, "class_name");
2197 duk_debug_write_hstring(thr, DUK_HOBJECT_GET_CLASS_STRING(heap, h_obj));
2198 duk__debug_getinfo_flags_key(thr, "prototype");
2199 if (h_proto != NULL) {
2200 duk_debug_write_hobject(thr, h_proto);
2201 } else {
2202 duk_debug_write_null(thr);
2203 }
2204 duk__debug_getinfo_flags_key(thr, "props");
2205 duk_debug_write_pointer(thr, (void *) DUK_HOBJECT_GET_PROPS(heap, h_obj));
2206 duk__debug_getinfo_prop_uint(thr, "e_size", (duk_uint_t) DUK_HOBJECT_GET_ESIZE(h_obj));
2207 duk__debug_getinfo_prop_uint(thr, "e_next", (duk_uint_t) DUK_HOBJECT_GET_ENEXT(h_obj));
2208 duk__debug_getinfo_prop_uint(thr, "a_size", (duk_uint_t) DUK_HOBJECT_GET_ASIZE(h_obj));
2209 duk__debug_getinfo_prop_uint(thr, "h_size", (duk_uint_t) DUK_HOBJECT_GET_HSIZE(h_obj));
2210
2211 if (DUK_HOBJECT_IS_ARRAY(h_obj)) {
2212 duk_harray *h_arr;
2213 h_arr = (duk_harray *) h_obj;
2214
2215 duk__debug_getinfo_prop_uint(thr, "length", (duk_uint_t) h_arr->length);
2216 duk__debug_getinfo_prop_bool(thr, "length_nonwritable", h_arr->length_nonwritable);
2217 }
2218
2219 if (DUK_HOBJECT_IS_NATFUNC(h_obj)) {
2220 duk_hnatfunc *h_fun;
2221 h_fun = (duk_hnatfunc *) h_obj;
2222
2223 duk__debug_getinfo_prop_int(thr, "nargs", h_fun->nargs);
2224 duk__debug_getinfo_prop_int(thr, "magic", h_fun->magic);
2225 duk__debug_getinfo_prop_bool(thr, "varargs", h_fun->magic == DUK_HNATFUNC_NARGS_VARARGS);
2226 /* Native function pointer may be different from a void pointer,
2227 * and we serialize it from memory directly now (no byte swapping etc).
2228 */
2229 duk__debug_getinfo_flags_key(thr, "funcptr");
2230 duk_debug_write_buffer(thr, (const char *) &h_fun->func, sizeof(h_fun->func));
2231 }
2232
2233 if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) {
2234 duk_hcompfunc *h_fun;
2235 duk_hbuffer *h_buf;
2236 duk_hobject *h_lexenv;
2237 duk_hobject *h_varenv;
2238 h_fun = (duk_hcompfunc *) h_obj;
2239
2240 duk__debug_getinfo_prop_int(thr, "nregs", h_fun->nregs);
2241 duk__debug_getinfo_prop_int(thr, "nargs", h_fun->nargs);
2242
2243 duk__debug_getinfo_flags_key(thr, "lex_env");
2244 h_lexenv = DUK_HCOMPFUNC_GET_LEXENV(thr->heap, h_fun);
2245 if (h_lexenv != NULL) {
2246 duk_debug_write_hobject(thr, h_lexenv);
2247 } else {
2248 duk_debug_write_null(thr);
2249 }
2250 duk__debug_getinfo_flags_key(thr, "var_env");
2251 h_varenv = DUK_HCOMPFUNC_GET_VARENV(thr->heap, h_fun);
2252 if (h_varenv != NULL) {
2253 duk_debug_write_hobject(thr, h_varenv);
2254 } else {
2255 duk_debug_write_null(thr);
2256 }
2257
2258 duk__debug_getinfo_prop_uint(thr, "start_line", h_fun->start_line);
2259 duk__debug_getinfo_prop_uint(thr, "end_line", h_fun->end_line);
2260 h_buf = (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(thr->heap, h_fun);
2261 if (h_buf != NULL) {
2262 duk__debug_getinfo_flags_key(thr, "data");
2263 duk_debug_write_heapptr(thr, (duk_heaphdr *) h_buf);
2264 }
2265 }
2266
2267 if (DUK_HOBJECT_IS_BOUNDFUNC(h_obj)) {
2268 duk_hboundfunc *h_bfun;
2269 h_bfun = (duk_hboundfunc *) (void *) h_obj;
2270
2271 duk__debug_getinfo_flags_key(thr, "target");
2272 duk_debug_write_tval(thr, &h_bfun->target);
2273 duk__debug_getinfo_flags_key(thr, "this_binding");
2274 duk_debug_write_tval(thr, &h_bfun->this_binding);
2275 duk__debug_getinfo_flags_key(thr, "nargs");
2276 duk_debug_write_int(thr, h_bfun->nargs);
2277 /* h_bfun->args not exposed now */
2278 }
2279
2280 if (DUK_HOBJECT_IS_THREAD(h_obj)) {
2281 /* XXX: Currently no inspection of threads, e.g. value stack, call
2282 * stack, catch stack, etc.
2283 */
2284 duk_hthread *h_thr;
2285 h_thr = (duk_hthread *) h_obj;
2286 DUK_UNREF(h_thr);
2287 }
2288
2289 if (DUK_HOBJECT_IS_DECENV(h_obj)) {
2290 duk_hdecenv *h_env;
2291 h_env = (duk_hdecenv *) h_obj;
2292
2293 duk__debug_getinfo_flags_key(thr, "thread");
2294 duk_debug_write_heapptr(thr, (duk_heaphdr *) (h_env->thread));
2295 duk__debug_getinfo_flags_key(thr, "varmap");
2296 duk_debug_write_heapptr(thr, (duk_heaphdr *) (h_env->varmap));
2297 duk__debug_getinfo_prop_uint(thr, "regbase", (duk_uint_t) h_env->regbase_byteoff);
2298 }
2299
2300 if (DUK_HOBJECT_IS_OBJENV(h_obj)) {
2301 duk_hobjenv *h_env;
2302 h_env = (duk_hobjenv *) h_obj;
2303
2304 duk__debug_getinfo_flags_key(thr, "target");
2305 duk_debug_write_heapptr(thr, (duk_heaphdr *) (h_env->target));
2306 duk__debug_getinfo_prop_bool(thr, "has_this", h_env->has_this);
2307 }
2308
2309 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2310 if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) {
2311 duk_hbufobj *h_bufobj;
2312 h_bufobj = (duk_hbufobj *) h_obj;
2313
2314 duk__debug_getinfo_prop_uint(thr, "slice_offset", h_bufobj->offset);
2315 duk__debug_getinfo_prop_uint(thr, "slice_length", h_bufobj->length);
2316 duk__debug_getinfo_prop_uint(thr, "elem_shift", (duk_uint_t) h_bufobj->shift);
2317 duk__debug_getinfo_prop_uint(thr, "elem_type", (duk_uint_t) h_bufobj->elem_type);
2318 duk__debug_getinfo_prop_bool(thr, "is_typedarray", (duk_uint_t) h_bufobj->is_typedarray);
2319 if (h_bufobj->buf != NULL) {
2320 duk__debug_getinfo_flags_key(thr, "buffer");
2321 duk_debug_write_heapptr(thr, (duk_heaphdr *) h_bufobj->buf);
2322 }
2323 }
2324 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2325 break;
2326 }
2327 case DUK_HTYPE_BUFFER: {
2328 duk_hbuffer *h_buf;
2329
2330 h_buf = (duk_hbuffer *) h;
2331 duk__debug_getinfo_bitmask(thr,
2332 duk__debug_getinfo_hbuffer_keys,
2333 duk__debug_getinfo_hbuffer_masks,
2334 DUK_HEAPHDR_GET_FLAGS_RAW(h));
2335 duk__debug_getinfo_prop_uint(thr, "size", (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_buf));
2336 duk__debug_getinfo_flags_key(thr, "dataptr");
2337 duk_debug_write_pointer(thr, (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_buf));
2338 duk__debug_getinfo_flags_key(thr, "data");
2339 duk_debug_write_hbuffer(thr, h_buf); /* tolerates NULL h_buf */
2340 break;
2341 }
2342 default: {
2343 /* Since we already started writing the reply, just emit nothing. */
2344 DUK_D(DUK_DPRINT("inspect target pointer has invalid heaphdr type"));
2345 }
2346 }
2347
2348 duk_debug_write_eom(thr);
2349 }
2350
duk__debug_handle_get_obj_prop_desc(duk_hthread * thr,duk_heap * heap)2351 DUK_LOCAL void duk__debug_handle_get_obj_prop_desc(duk_hthread *thr, duk_heap *heap) {
2352 duk_heaphdr *h;
2353 duk_hobject *h_obj;
2354 duk_hstring *h_key;
2355 duk_propdesc desc;
2356
2357 DUK_D(DUK_DPRINT("debug command GetObjPropDesc"));
2358 DUK_UNREF(heap);
2359
2360 h = duk_debug_read_any_ptr(thr);
2361 if (!h) {
2362 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid target");
2363 return;
2364 }
2365 h_key = duk_debug_read_hstring(thr);
2366 if (h == NULL || DUK_HEAPHDR_GET_TYPE(h) != DUK_HTYPE_OBJECT || h_key == NULL) {
2367 goto fail_args;
2368 }
2369 h_obj = (duk_hobject *) h;
2370
2371 if (duk_hobject_get_own_propdesc(thr, h_obj, h_key, &desc, 0 /*flags*/)) {
2372 duk_int_t virtual_idx;
2373 duk_bool_t rc;
2374
2375 /* To use the shared helper need the virtual index. */
2376 DUK_ASSERT(desc.e_idx >= 0 || desc.a_idx >= 0);
2377 virtual_idx = (desc.a_idx >= 0 ? desc.a_idx :
2378 (duk_int_t) DUK_HOBJECT_GET_ASIZE(h_obj) + desc.e_idx);
2379
2380 duk_debug_write_reply(thr);
2381 rc = duk__debug_getprop_index(thr, heap, h_obj, (duk_uint_t) virtual_idx);
2382 DUK_ASSERT(rc == 1);
2383 DUK_UNREF(rc);
2384 duk_debug_write_eom(thr);
2385 } else {
2386 duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "not found");
2387 }
2388 return;
2389
2390 fail_args:
2391 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid args");
2392 }
2393
duk__debug_handle_get_obj_prop_desc_range(duk_hthread * thr,duk_heap * heap)2394 DUK_LOCAL void duk__debug_handle_get_obj_prop_desc_range(duk_hthread *thr, duk_heap *heap) {
2395 duk_heaphdr *h;
2396 duk_hobject *h_obj;
2397 duk_uint_t idx, idx_start, idx_end;
2398
2399 DUK_D(DUK_DPRINT("debug command GetObjPropDescRange"));
2400 DUK_UNREF(heap);
2401
2402 h = duk_debug_read_any_ptr(thr);
2403 idx_start = (duk_uint_t) duk_debug_read_int(thr);
2404 idx_end = (duk_uint_t) duk_debug_read_int(thr);
2405 if (h == NULL || DUK_HEAPHDR_GET_TYPE(h) != DUK_HTYPE_OBJECT) {
2406 goto fail_args;
2407 }
2408 h_obj = (duk_hobject *) h;
2409
2410 /* The index range space is conceptually the array part followed by the
2411 * entry part. Unlike normal enumeration all slots are exposed here as
2412 * is and return 'unused' if the slots are not in active use. In particular
2413 * the array part is included for the full a_size regardless of what the
2414 * array .length is.
2415 */
2416
2417 duk_debug_write_reply(thr);
2418 for (idx = idx_start; idx < idx_end; idx++) {
2419 if (!duk__debug_getprop_index(thr, heap, h_obj, idx)) {
2420 break;
2421 }
2422 }
2423 duk_debug_write_eom(thr);
2424 return;
2425
2426 fail_args:
2427 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid args");
2428 }
2429
2430 #endif /* DUK_USE_DEBUGGER_INSPECT */
2431
2432 /*
2433 * Process incoming debug requests
2434 *
2435 * Individual request handlers can push temporaries on the value stack and
2436 * rely on duk__debug_process_message() to restore the value stack top
2437 * automatically.
2438 */
2439
2440 /* Process one debug message. Automatically restore value stack top to its
2441 * entry value, so that individual message handlers don't need exact value
2442 * stack handling which is convenient.
2443 */
duk__debug_process_message(duk_hthread * thr)2444 DUK_LOCAL void duk__debug_process_message(duk_hthread *thr) {
2445 duk_heap *heap;
2446 duk_uint8_t x;
2447 duk_int32_t cmd;
2448 duk_idx_t entry_top;
2449
2450 DUK_ASSERT(thr != NULL);
2451 heap = thr->heap;
2452 DUK_ASSERT(heap != NULL);
2453
2454 entry_top = duk_get_top(thr);
2455
2456 x = duk_debug_read_byte(thr);
2457 switch (x) {
2458 case DUK_DBG_IB_REQUEST: {
2459 cmd = duk_debug_read_int(thr);
2460 switch (cmd) {
2461 case DUK_DBG_CMD_BASICINFO: {
2462 duk__debug_handle_basic_info(thr, heap);
2463 break;
2464 }
2465 case DUK_DBG_CMD_TRIGGERSTATUS: {
2466 duk__debug_handle_trigger_status(thr, heap);
2467 break;
2468 }
2469 case DUK_DBG_CMD_PAUSE: {
2470 duk__debug_handle_pause(thr, heap);
2471 break;
2472 }
2473 case DUK_DBG_CMD_RESUME: {
2474 duk__debug_handle_resume(thr, heap);
2475 break;
2476 }
2477 case DUK_DBG_CMD_STEPINTO:
2478 case DUK_DBG_CMD_STEPOVER:
2479 case DUK_DBG_CMD_STEPOUT: {
2480 duk__debug_handle_step(thr, heap, cmd);
2481 break;
2482 }
2483 case DUK_DBG_CMD_LISTBREAK: {
2484 duk__debug_handle_list_break(thr, heap);
2485 break;
2486 }
2487 case DUK_DBG_CMD_ADDBREAK: {
2488 duk__debug_handle_add_break(thr, heap);
2489 break;
2490 }
2491 case DUK_DBG_CMD_DELBREAK: {
2492 duk__debug_handle_del_break(thr, heap);
2493 break;
2494 }
2495 case DUK_DBG_CMD_GETVAR: {
2496 duk__debug_handle_get_var(thr, heap);
2497 break;
2498 }
2499 case DUK_DBG_CMD_PUTVAR: {
2500 duk__debug_handle_put_var(thr, heap);
2501 break;
2502 }
2503 case DUK_DBG_CMD_GETCALLSTACK: {
2504 duk__debug_handle_get_call_stack(thr, heap);
2505 break;
2506 }
2507 case DUK_DBG_CMD_GETLOCALS: {
2508 duk__debug_handle_get_locals(thr, heap);
2509 break;
2510 }
2511 case DUK_DBG_CMD_EVAL: {
2512 duk__debug_handle_eval(thr, heap);
2513 break;
2514 }
2515 case DUK_DBG_CMD_DETACH: {
2516 /* The actual detached_cb call is postponed to message loop so
2517 * we don't need any special precautions here (just skip to EOM
2518 * on the already closed connection).
2519 */
2520 duk__debug_handle_detach(thr, heap);
2521 break;
2522 }
2523 #if defined(DUK_USE_DEBUGGER_DUMPHEAP)
2524 case DUK_DBG_CMD_DUMPHEAP: {
2525 duk__debug_handle_dump_heap(thr, heap);
2526 break;
2527 }
2528 #endif /* DUK_USE_DEBUGGER_DUMPHEAP */
2529 case DUK_DBG_CMD_GETBYTECODE: {
2530 duk__debug_handle_get_bytecode(thr, heap);
2531 break;
2532 }
2533 case DUK_DBG_CMD_APPREQUEST: {
2534 duk__debug_handle_apprequest(thr, heap);
2535 break;
2536 }
2537 #if defined(DUK_USE_DEBUGGER_INSPECT)
2538 case DUK_DBG_CMD_GETHEAPOBJINFO: {
2539 duk__debug_handle_get_heap_obj_info(thr, heap);
2540 break;
2541 }
2542 case DUK_DBG_CMD_GETOBJPROPDESC: {
2543 duk__debug_handle_get_obj_prop_desc(thr, heap);
2544 break;
2545 }
2546 case DUK_DBG_CMD_GETOBJPROPDESCRANGE: {
2547 duk__debug_handle_get_obj_prop_desc_range(thr, heap);
2548 break;
2549 }
2550 #endif /* DUK_USE_DEBUGGER_INSPECT */
2551 default: {
2552 DUK_D(DUK_DPRINT("debug command unsupported: %d", (int) cmd));
2553 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "unsupported command");
2554 }
2555 } /* switch cmd */
2556 break;
2557 }
2558 case DUK_DBG_IB_REPLY: {
2559 DUK_D(DUK_DPRINT("debug reply, skipping"));
2560 break;
2561 }
2562 case DUK_DBG_IB_ERROR: {
2563 DUK_D(DUK_DPRINT("debug error, skipping"));
2564 break;
2565 }
2566 case DUK_DBG_IB_NOTIFY: {
2567 DUK_D(DUK_DPRINT("debug notify, skipping"));
2568 break;
2569 }
2570 default: {
2571 DUK_D(DUK_DPRINT("invalid initial byte, drop connection: %d", (int) x));
2572 goto fail;
2573 }
2574 } /* switch initial byte */
2575
2576 DUK_ASSERT(duk_get_top(thr) >= entry_top);
2577 duk_set_top(thr, entry_top);
2578 duk__debug_skip_to_eom(thr);
2579 return;
2580
2581 fail:
2582 DUK_ASSERT(duk_get_top(thr) >= entry_top);
2583 duk_set_top(thr, entry_top);
2584 DUK__SET_CONN_BROKEN(thr, 1);
2585 return;
2586 }
2587
duk__check_resend_status(duk_hthread * thr)2588 DUK_LOCAL void duk__check_resend_status(duk_hthread *thr) {
2589 if (thr->heap->dbg_read_cb != NULL && thr->heap->dbg_state_dirty) {
2590 duk_debug_send_status(thr);
2591 thr->heap->dbg_state_dirty = 0;
2592 }
2593 }
2594
duk_debug_process_messages(duk_hthread * thr,duk_bool_t no_block)2595 DUK_INTERNAL duk_bool_t duk_debug_process_messages(duk_hthread *thr, duk_bool_t no_block) {
2596 #if defined(DUK_USE_ASSERTIONS)
2597 duk_idx_t entry_top;
2598 #endif
2599 duk_bool_t retval = 0;
2600
2601 DUK_ASSERT(thr != NULL);
2602 DUK_ASSERT(thr->heap != NULL);
2603 #if defined(DUK_USE_ASSERTIONS)
2604 entry_top = duk_get_top(thr);
2605 #endif
2606
2607 DUK_D(DUK_DPRINT("process debug messages: read_cb=%s, no_block=%ld, detaching=%ld, processing=%ld",
2608 thr->heap->dbg_read_cb ? "not NULL" : "NULL", (long) no_block,
2609 (long) thr->heap->dbg_detaching, (long) thr->heap->dbg_processing));
2610 DUK_DD(DUK_DDPRINT("top at entry: %ld", (long) duk_get_top(thr)));
2611
2612 /* thr->heap->dbg_detaching may be != 0 if a debugger write outside
2613 * the message loop caused a transport error and detach1() to run.
2614 */
2615 DUK_ASSERT(thr->heap->dbg_detaching == 0 || thr->heap->dbg_detaching == 1);
2616 DUK_ASSERT(thr->heap->dbg_processing == 0);
2617 thr->heap->dbg_processing = 1;
2618
2619 /* Ensure dirty state causes a Status even if never process any
2620 * messages. This is expected by the bytecode executor when in
2621 * the running state.
2622 */
2623 duk__check_resend_status(thr);
2624
2625 for (;;) {
2626 /* Process messages until we're no longer paused or we peek
2627 * and see there's nothing to read right now.
2628 */
2629 DUK_DD(DUK_DDPRINT("top at loop top: %ld", (long) duk_get_top(thr)));
2630 DUK_ASSERT(thr->heap->dbg_processing == 1);
2631
2632 while (thr->heap->dbg_read_cb == NULL && thr->heap->dbg_detaching) {
2633 /* Detach is pending; can be triggered from outside the
2634 * debugger loop (e.g. Status notify write error) or by
2635 * previous message handling. Call detached callback
2636 * here, in a controlled state, to ensure a possible
2637 * reattach inside the detached_cb is handled correctly.
2638 *
2639 * Recheck for detach in a while loop: an immediate
2640 * reattach involves a call to duk_debugger_attach()
2641 * which writes a debugger handshake line immediately
2642 * inside the API call. If the transport write fails
2643 * for that handshake, we can immediately end up in a
2644 * "transport broken, detaching" case several times here.
2645 * Loop back until we're either cleanly attached or
2646 * fully detached.
2647 *
2648 * NOTE: Reset dbg_processing = 1 forcibly, in case we
2649 * re-attached; duk_debugger_attach() sets dbg_processing
2650 * to 0 at the moment.
2651 */
2652
2653 DUK_D(DUK_DPRINT("detach pending (dbg_read_cb == NULL, dbg_detaching != 0), call detach2"));
2654
2655 duk__debug_do_detach2(thr->heap);
2656 thr->heap->dbg_processing = 1; /* may be set to 0 by duk_debugger_attach() inside callback */
2657
2658 DUK_D(DUK_DPRINT("after detach2 (and possible reattach): dbg_read_cb=%s, dbg_detaching=%ld",
2659 thr->heap->dbg_read_cb ? "not NULL" : "NULL", (long) thr->heap->dbg_detaching));
2660 }
2661 DUK_ASSERT(thr->heap->dbg_detaching == 0); /* true even with reattach */
2662 DUK_ASSERT(thr->heap->dbg_processing == 1); /* even after a detach and possible reattach */
2663
2664 if (thr->heap->dbg_read_cb == NULL) {
2665 DUK_D(DUK_DPRINT("debug connection broken (and not detaching), stop processing messages"));
2666 break;
2667 }
2668
2669 if (!DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) || no_block) {
2670 if (!duk_debug_read_peek(thr)) {
2671 /* Note: peek cannot currently trigger a detach
2672 * so the dbg_detaching == 0 assert outside the
2673 * loop is correct.
2674 */
2675 DUK_D(DUK_DPRINT("processing debug message, peek indicated no data, stop processing messages"));
2676 break;
2677 }
2678 DUK_D(DUK_DPRINT("processing debug message, peek indicated there is data, handle it"));
2679 } else {
2680 DUK_D(DUK_DPRINT("paused, process debug message, blocking if necessary"));
2681 }
2682
2683 duk__check_resend_status(thr);
2684 duk__debug_process_message(thr);
2685 duk__check_resend_status(thr);
2686
2687 retval = 1; /* processed one or more messages */
2688 }
2689
2690 DUK_ASSERT(thr->heap->dbg_detaching == 0);
2691 DUK_ASSERT(thr->heap->dbg_processing == 1);
2692 thr->heap->dbg_processing = 0;
2693
2694 /* As an initial implementation, read flush after exiting the message
2695 * loop. If transport is broken, this is a no-op (with debug logs).
2696 */
2697 duk_debug_read_flush(thr); /* this cannot initiate a detach */
2698 DUK_ASSERT(thr->heap->dbg_detaching == 0);
2699
2700 DUK_DD(DUK_DDPRINT("top at exit: %ld", (long) duk_get_top(thr)));
2701
2702 #if defined(DUK_USE_ASSERTIONS)
2703 /* Easy to get wrong, so assert for it. */
2704 DUK_ASSERT(entry_top == duk_get_top(thr));
2705 #endif
2706
2707 return retval;
2708 }
2709
2710 /*
2711 * Halt execution helper
2712 */
2713
2714 /* Halt execution and enter a debugger message loop until execution is resumed
2715 * by the client. PC for the current activation may be temporarily decremented
2716 * so that the "current" instruction will be shown by the client. This helper
2717 * is callable from anywhere, also outside bytecode executor.
2718 */
2719
duk_debug_halt_execution(duk_hthread * thr,duk_bool_t use_prev_pc)2720 DUK_INTERNAL void duk_debug_halt_execution(duk_hthread *thr, duk_bool_t use_prev_pc) {
2721 duk_activation *act;
2722 duk_hcompfunc *fun;
2723 duk_instr_t *old_pc = NULL;
2724
2725 DUK_ASSERT(thr != NULL);
2726 DUK_ASSERT(thr->heap != NULL);
2727 DUK_ASSERT(duk_debug_is_attached(thr->heap));
2728 DUK_ASSERT(thr->heap->dbg_processing == 0);
2729 DUK_ASSERT(!duk_debug_is_paused(thr->heap));
2730
2731 duk_debug_set_paused(thr->heap);
2732
2733 act = thr->callstack_curr;
2734
2735 /* NOTE: act may be NULL if an error is thrown outside of any activation,
2736 * which may happen in the case of, e.g. syntax errors.
2737 */
2738
2739 /* Decrement PC if that was requested, this requires a PC sync. */
2740 if (act != NULL) {
2741 duk_hthread_sync_currpc(thr);
2742 old_pc = act->curr_pc;
2743 fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);
2744
2745 /* Short circuit if is safe: if act->curr_pc != NULL, 'fun' is
2746 * guaranteed to be a non-NULL ECMAScript function.
2747 */
2748 DUK_ASSERT(act->curr_pc == NULL ||
2749 (fun != NULL && DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun)));
2750 if (use_prev_pc &&
2751 act->curr_pc != NULL &&
2752 act->curr_pc > DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, fun)) {
2753 act->curr_pc--;
2754 }
2755 }
2756
2757 /* Process debug messages until we are no longer paused. */
2758
2759 /* NOTE: This is a bit fragile. It's important to ensure that
2760 * duk_debug_process_messages() never throws an error or
2761 * act->curr_pc will never be reset.
2762 */
2763
2764 thr->heap->dbg_state_dirty = 1;
2765 while (DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap)) {
2766 DUK_ASSERT(duk_debug_is_attached(thr->heap));
2767 DUK_ASSERT(thr->heap->dbg_processing == 0);
2768 duk_debug_process_messages(thr, 0 /*no_block*/);
2769 }
2770
2771 /* XXX: Decrementing and restoring act->curr_pc works now, but if the
2772 * debugger message loop gains the ability to adjust the current PC
2773 * (e.g. a forced jump) restoring the PC here will break. Another
2774 * approach would be to use a state flag for the "decrement 1 from
2775 * topmost activation's PC" and take it into account whenever dealing
2776 * with PC values.
2777 */
2778 if (act != NULL) {
2779 act->curr_pc = old_pc; /* restore PC */
2780 }
2781 }
2782
2783 /*
2784 * Breakpoint management
2785 */
2786
duk_debug_add_breakpoint(duk_hthread * thr,duk_hstring * filename,duk_uint32_t line)2787 DUK_INTERNAL duk_small_int_t duk_debug_add_breakpoint(duk_hthread *thr, duk_hstring *filename, duk_uint32_t line) {
2788 duk_heap *heap;
2789 duk_breakpoint *b;
2790
2791 /* Caller must trigger recomputation of active breakpoint list. To
2792 * ensure stale values are not used if that doesn't happen, clear the
2793 * active breakpoint list here.
2794 */
2795
2796 DUK_ASSERT(thr != NULL);
2797 DUK_ASSERT(filename != NULL);
2798 heap = thr->heap;
2799 DUK_ASSERT(heap != NULL);
2800
2801 if (heap->dbg_breakpoint_count >= DUK_HEAP_MAX_BREAKPOINTS) {
2802 DUK_D(DUK_DPRINT("failed to add breakpoint for %O:%ld, all breakpoint slots used",
2803 (duk_heaphdr *) filename, (long) line));
2804 return -1;
2805 }
2806 heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL;
2807 b = heap->dbg_breakpoints + (heap->dbg_breakpoint_count++);
2808 b->filename = filename;
2809 b->line = line;
2810 DUK_HSTRING_INCREF(thr, filename);
2811
2812 return (duk_small_int_t) (heap->dbg_breakpoint_count - 1); /* index */
2813 }
2814
duk_debug_remove_breakpoint(duk_hthread * thr,duk_small_uint_t breakpoint_index)2815 DUK_INTERNAL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_uint_t breakpoint_index) {
2816 duk_heap *heap;
2817 duk_hstring *h;
2818 duk_breakpoint *b;
2819 duk_size_t move_size;
2820
2821 /* Caller must trigger recomputation of active breakpoint list. To
2822 * ensure stale values are not used if that doesn't happen, clear the
2823 * active breakpoint list here.
2824 */
2825
2826 DUK_ASSERT(thr != NULL);
2827 heap = thr->heap;
2828 DUK_ASSERT(heap != NULL);
2829 DUK_ASSERT(duk_debug_is_attached(thr->heap));
2830 DUK_ASSERT_DISABLE(breakpoint_index >= 0); /* unsigned */
2831
2832 if (breakpoint_index >= heap->dbg_breakpoint_count) {
2833 DUK_D(DUK_DPRINT("invalid breakpoint index: %ld", (long) breakpoint_index));
2834 return 0;
2835 }
2836 b = heap->dbg_breakpoints + breakpoint_index;
2837
2838 h = b->filename;
2839 DUK_ASSERT(h != NULL);
2840
2841 move_size = sizeof(duk_breakpoint) * (heap->dbg_breakpoint_count - breakpoint_index - 1);
2842 duk_memmove((void *) b,
2843 (const void *) (b + 1),
2844 (size_t) move_size);
2845
2846 heap->dbg_breakpoint_count--;
2847 heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL;
2848
2849 DUK_HSTRING_DECREF(thr, h); /* side effects */
2850 DUK_UNREF(h); /* w/o refcounting */
2851
2852 /* Breakpoint entries above the used area are left as garbage. */
2853
2854 return 1;
2855 }
2856
2857 /*
2858 * Misc state management
2859 */
2860
duk_debug_is_attached(duk_heap * heap)2861 DUK_INTERNAL duk_bool_t duk_debug_is_attached(duk_heap *heap) {
2862 return (heap->dbg_read_cb != NULL);
2863 }
2864
duk_debug_is_paused(duk_heap * heap)2865 DUK_INTERNAL duk_bool_t duk_debug_is_paused(duk_heap *heap) {
2866 return (DUK_HEAP_HAS_DEBUGGER_PAUSED(heap) != 0);
2867 }
2868
duk_debug_set_paused(duk_heap * heap)2869 DUK_INTERNAL void duk_debug_set_paused(duk_heap *heap) {
2870 if (duk_debug_is_paused(heap)) {
2871 DUK_D(DUK_DPRINT("trying to set paused state when already paused, ignoring"));
2872 } else {
2873 DUK_HEAP_SET_DEBUGGER_PAUSED(heap);
2874 heap->dbg_state_dirty = 1;
2875 duk_debug_clear_pause_state(heap);
2876 DUK_ASSERT(heap->ms_running == 0); /* debugger can't be triggered within mark-and-sweep */
2877 heap->ms_running = 2; /* prevent mark-and-sweep, prevent refzero queueing */
2878 heap->ms_prevent_count++;
2879 DUK_ASSERT(heap->ms_prevent_count != 0); /* Wrap. */
2880 DUK_ASSERT(heap->heap_thread != NULL);
2881 }
2882 }
2883
duk_debug_clear_paused(duk_heap * heap)2884 DUK_INTERNAL void duk_debug_clear_paused(duk_heap *heap) {
2885 if (duk_debug_is_paused(heap)) {
2886 DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap);
2887 heap->dbg_state_dirty = 1;
2888 duk_debug_clear_pause_state(heap);
2889 DUK_ASSERT(heap->ms_running == 2);
2890 DUK_ASSERT(heap->ms_prevent_count > 0);
2891 heap->ms_prevent_count--;
2892 heap->ms_running = 0;
2893 DUK_ASSERT(heap->heap_thread != NULL);
2894 } else {
2895 DUK_D(DUK_DPRINT("trying to clear paused state when not paused, ignoring"));
2896 }
2897 }
2898
duk_debug_clear_pause_state(duk_heap * heap)2899 DUK_INTERNAL void duk_debug_clear_pause_state(duk_heap *heap) {
2900 heap->dbg_pause_flags = 0;
2901 heap->dbg_pause_act = NULL;
2902 heap->dbg_pause_startline = 0;
2903 }
2904
2905 #else /* DUK_USE_DEBUGGER_SUPPORT */
2906
2907 /* No debugger support. */
2908
2909 #endif /* DUK_USE_DEBUGGER_SUPPORT */
2910