5 #include "duk_internal.h"
7 #if defined(DUK_USE_DEBUGGER_SUPPORT)
16 /* Use b[] to access the size of the union, which is strictly not
17 * correct. Can't use fixed size unless there's feature detection
18 * for pointer byte size.
26 #define DUK__SET_CONN_BROKEN(thr,reason) do { \
27 /* For now shared handler is fine. */ \
28 duk__debug_do_detach1((thr)->heap, (reason)); \
31 DUK_LOCAL
void duk__debug_do_detach1(duk_heap
*heap
, duk_int_t reason
) {
32 /* Can be called multiple times with no harm. Mark the transport
33 * bad (dbg_read_cb == NULL) and clear state except for the detached
34 * callback and the udata field. The detached callback is delayed
35 * to the message loop so that it can be called between messages;
36 * this avoids corner cases related to immediate debugger reattach
37 * inside the detached callback.
40 if (heap
->dbg_detaching
) {
41 DUK_D(DUK_DPRINT("debugger already detaching, ignore detach1"));
45 DUK_D(DUK_DPRINT("debugger transport detaching, marking transport broken"));
47 heap
->dbg_detaching
= 1; /* prevent multiple in-progress detaches */
49 if (heap
->dbg_write_cb
!= NULL
) {
52 thr
= heap
->heap_thread
;
53 DUK_ASSERT(thr
!= NULL
);
55 duk_debug_write_notify(thr
, DUK_DBG_CMD_DETACHING
);
56 duk_debug_write_int(thr
, reason
);
57 duk_debug_write_eom(thr
);
60 heap
->dbg_read_cb
= NULL
;
61 heap
->dbg_write_cb
= NULL
;
62 heap
->dbg_peek_cb
= NULL
;
63 heap
->dbg_read_flush_cb
= NULL
;
64 heap
->dbg_write_flush_cb
= NULL
;
65 heap
->dbg_request_cb
= NULL
;
66 /* heap->dbg_detached_cb: keep */
67 /* heap->dbg_udata: keep */
68 /* heap->dbg_processing: keep on purpose to avoid debugger re-entry in detaching state */
70 heap
->dbg_state_dirty
= 0;
71 heap
->dbg_force_restart
= 0;
72 heap
->dbg_step_type
= 0;
73 heap
->dbg_step_thread
= NULL
;
74 heap
->dbg_step_csindex
= 0;
75 heap
->dbg_step_startline
= 0;
76 heap
->dbg_have_next_byte
= 0;
78 /* Ensure there are no stale active breakpoint pointers.
79 * Breakpoint list is currently kept - we could empty it
80 * here but we'd need to handle refcounts correctly, and
81 * we'd need a 'thr' reference for that.
83 * XXX: clear breakpoint on either attach or detach?
85 heap
->dbg_breakpoints_active
[0] = (duk_breakpoint
*) NULL
;
88 DUK_LOCAL
void duk__debug_do_detach2(duk_heap
*heap
) {
89 duk_debug_detached_function detached_cb
;
92 /* Safe to call multiple times. */
94 detached_cb
= heap
->dbg_detached_cb
;
95 detached_udata
= heap
->dbg_udata
;
96 heap
->dbg_detached_cb
= NULL
;
97 heap
->dbg_udata
= NULL
;
100 /* Careful here: state must be wiped before the call
101 * so that we can cleanly handle a re-attach from
102 * inside the callback.
104 DUK_D(DUK_DPRINT("detached during message loop, delayed call to detached_cb"));
105 detached_cb(detached_udata
);
108 heap
->dbg_detaching
= 0;
111 DUK_INTERNAL
void duk_debug_do_detach(duk_heap
*heap
) {
112 duk__debug_do_detach1(heap
, 0);
113 duk__debug_do_detach2(heap
);
116 /* Called on a read/write error: NULL all callbacks except the detached
117 * callback so that we never accidentally call them after a read/write
118 * error has been indicated. This is especially important for the transport
119 * I/O callbacks to fulfill guaranteed callback semantics.
121 DUK_LOCAL
void duk__debug_null_most_callbacks(duk_hthread
*thr
) {
124 DUK_D(DUK_DPRINT("transport read/write error, NULL all callbacks expected detached"));
125 heap
->dbg_read_cb
= NULL
;
126 heap
->dbg_write_cb
= NULL
; /* this is especially critical to avoid another write call in detach1() */
127 heap
->dbg_peek_cb
= NULL
;
128 heap
->dbg_read_flush_cb
= NULL
;
129 heap
->dbg_write_flush_cb
= NULL
;
130 heap
->dbg_request_cb
= NULL
;
131 /* keep heap->dbg_detached_cb */
135 * Debug connection peek and flush primitives
138 DUK_INTERNAL duk_bool_t
duk_debug_read_peek(duk_hthread
*thr
) {
141 DUK_ASSERT(thr
!= NULL
);
143 DUK_ASSERT(heap
!= NULL
);
145 if (heap
->dbg_read_cb
== NULL
) {
146 DUK_D(DUK_DPRINT("attempt to peek in detached state, return zero (= no data)"));
149 if (heap
->dbg_peek_cb
== NULL
) {
150 DUK_DD(DUK_DDPRINT("no peek callback, return zero (= no data)"));
154 return (duk_bool_t
) (heap
->dbg_peek_cb(heap
->dbg_udata
) > 0);
157 DUK_INTERNAL
void duk_debug_read_flush(duk_hthread
*thr
) {
160 DUK_ASSERT(thr
!= NULL
);
162 DUK_ASSERT(heap
!= NULL
);
164 if (heap
->dbg_read_cb
== NULL
) {
165 DUK_D(DUK_DPRINT("attempt to read flush in detached state, ignore"));
168 if (heap
->dbg_read_flush_cb
== NULL
) {
169 DUK_DD(DUK_DDPRINT("no read flush callback, ignore"));
173 heap
->dbg_read_flush_cb(heap
->dbg_udata
);
176 DUK_INTERNAL
void duk_debug_write_flush(duk_hthread
*thr
) {
179 DUK_ASSERT(thr
!= NULL
);
181 DUK_ASSERT(heap
!= NULL
);
183 if (heap
->dbg_read_cb
== NULL
) {
184 DUK_D(DUK_DPRINT("attempt to write flush in detached state, ignore"));
187 if (heap
->dbg_write_flush_cb
== NULL
) {
188 DUK_DD(DUK_DDPRINT("no write flush callback, ignore"));
192 heap
->dbg_write_flush_cb(heap
->dbg_udata
);
196 * Debug connection skip primitives
200 DUK_INTERNAL
void duk_debug_skip_bytes(duk_hthread
*thr
, duk_size_t length
) {
201 duk_uint8_t dummy
[64];
204 DUK_ASSERT(thr
!= NULL
);
207 now
= (length
> sizeof(dummy
) ? sizeof(dummy
) : length
);
208 duk_debug_read_bytes(thr
, dummy
, now
);
213 DUK_INTERNAL
void duk_debug_skip_byte(duk_hthread
*thr
) {
214 DUK_ASSERT(thr
!= NULL
);
216 (void) duk_debug_read_byte(thr
);
220 * Debug connection read primitives
223 /* Peek ahead in the stream one byte. */
224 DUK_INTERNAL
uint8_t duk_debug_peek_byte(duk_hthread
*thr
) {
225 /* It is important not to call this if the last byte read was an EOM.
226 * Reading ahead in this scenario would cause unnecessary blocking if
227 * another message is not available.
232 x
= duk_debug_read_byte(thr
);
233 thr
->heap
->dbg_have_next_byte
= 1;
234 thr
->heap
->dbg_next_byte
= x
;
239 DUK_INTERNAL
void duk_debug_read_bytes(duk_hthread
*thr
, duk_uint8_t
*data
, duk_size_t length
) {
245 DUK_ASSERT(thr
!= NULL
);
247 DUK_ASSERT(heap
!= NULL
);
249 if (heap
->dbg_read_cb
== NULL
) {
250 DUK_D(DUK_DPRINT("attempt to read %ld bytes in detached state, return zero data", (long) length
));
254 /* NOTE: length may be zero */
256 if (length
>= 1 && heap
->dbg_have_next_byte
) {
257 heap
->dbg_have_next_byte
= 0;
258 *p
++ = heap
->dbg_next_byte
;
261 left
= (duk_size_t
) ((data
+ length
) - p
);
265 DUK_ASSERT(heap
->dbg_read_cb
!= NULL
);
266 DUK_ASSERT(left
>= 1);
267 #if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE)
270 got
= heap
->dbg_read_cb(heap
->dbg_udata
, (char *) p
, left
);
271 if (got
== 0 || got
> left
) {
272 DUK_D(DUK_DPRINT("connection error during read, return zero data"));
273 duk__debug_null_most_callbacks(thr
); /* avoid calling write callback in detach1() */
274 DUK__SET_CONN_BROKEN(thr
, 1);
282 DUK_MEMZERO((void *) data
, (size_t) length
);
285 DUK_INTERNAL duk_uint8_t
duk_debug_read_byte(duk_hthread
*thr
) {
288 x
= 0; /* just in case callback is broken and won't write 'x' */
289 duk_debug_read_bytes(thr
, &x
, 1);
293 DUK_LOCAL duk_uint32_t
duk__debug_read_uint32_raw(duk_hthread
*thr
) {
296 DUK_ASSERT(thr
!= NULL
);
298 duk_debug_read_bytes(thr
, buf
, 4);
299 return ((duk_uint32_t
) buf
[0] << 24) |
300 ((duk_uint32_t
) buf
[1] << 16) |
301 ((duk_uint32_t
) buf
[2] << 8) |
302 (duk_uint32_t
) buf
[3];
305 DUK_LOCAL duk_uint32_t
duk__debug_read_int32_raw(duk_hthread
*thr
) {
306 return (duk_int32_t
) duk__debug_read_uint32_raw(thr
);
309 DUK_LOCAL duk_uint16_t
duk__debug_read_uint16_raw(duk_hthread
*thr
) {
312 DUK_ASSERT(thr
!= NULL
);
314 duk_debug_read_bytes(thr
, buf
, 2);
315 return ((duk_uint16_t
) buf
[0] << 8) |
316 (duk_uint16_t
) buf
[1];
319 DUK_INTERNAL duk_int32_t
duk_debug_read_int(duk_hthread
*thr
) {
323 DUK_ASSERT(thr
!= NULL
);
325 x
= duk_debug_read_byte(thr
);
327 t
= duk_debug_read_byte(thr
);
328 return (duk_int32_t
) (((x
- 0xc0) << 8) + t
);
329 } else if (x
>= 0x80) {
330 return (duk_int32_t
) (x
- 0x80);
331 } else if (x
== DUK_DBG_IB_INT4
) {
332 return (duk_int32_t
) duk__debug_read_uint32_raw(thr
);
335 DUK_D(DUK_DPRINT("debug connection error: failed to decode int"));
336 DUK__SET_CONN_BROKEN(thr
, 1);
340 DUK_LOCAL duk_hstring
*duk__debug_read_hstring_raw(duk_hthread
*thr
, duk_uint32_t len
) {
341 duk_context
*ctx
= (duk_context
*) thr
;
345 if (len
<= sizeof(buf
)) {
346 duk_debug_read_bytes(thr
, buf
, (duk_size_t
) len
);
347 duk_push_lstring(ctx
, (const char *) buf
, (duk_size_t
) len
);
349 p
= (duk_uint8_t
*) duk_push_fixed_buffer(ctx
, (duk_size_t
) len
);
350 DUK_ASSERT(p
!= NULL
);
351 duk_debug_read_bytes(thr
, p
, (duk_size_t
) len
);
352 duk_to_string(ctx
, -1);
355 return duk_require_hstring(ctx
, -1);
358 DUK_INTERNAL duk_hstring
*duk_debug_read_hstring(duk_hthread
*thr
) {
359 duk_context
*ctx
= (duk_context
*) thr
;
363 DUK_ASSERT(thr
!= NULL
);
365 x
= duk_debug_read_byte(thr
);
366 if (x
>= 0x60 && x
<= 0x7f) {
367 /* For short strings, use a fixed temp buffer. */
368 len
= (duk_uint32_t
) (x
- 0x60);
369 } else if (x
== DUK_DBG_IB_STR2
) {
370 len
= (duk_uint32_t
) duk__debug_read_uint16_raw(thr
);
371 } else if (x
== DUK_DBG_IB_STR4
) {
372 len
= (duk_uint32_t
) duk__debug_read_uint32_raw(thr
);
377 return duk__debug_read_hstring_raw(thr
, len
);
380 DUK_D(DUK_DPRINT("debug connection error: failed to decode int"));
381 DUK__SET_CONN_BROKEN(thr
, 1);
382 duk_push_hstring_stridx(thr
, DUK_STRIDX_EMPTY_STRING
); /* always push some string */
383 return duk_require_hstring(ctx
, -1);
386 DUK_LOCAL duk_hbuffer
*duk__debug_read_hbuffer_raw(duk_hthread
*thr
, duk_uint32_t len
) {
387 duk_context
*ctx
= (duk_context
*) thr
;
390 p
= (duk_uint8_t
*) duk_push_fixed_buffer(ctx
, (duk_size_t
) len
);
391 DUK_ASSERT(p
!= NULL
);
392 duk_debug_read_bytes(thr
, p
, (duk_size_t
) len
);
394 return duk_require_hbuffer(ctx
, -1);
397 DUK_LOCAL
void *duk__debug_read_pointer_raw(duk_hthread
*thr
) {
401 DUK_ASSERT(thr
!= NULL
);
403 x
= duk_debug_read_byte(thr
);
404 if (x
!= sizeof(pu
)) {
407 duk_debug_read_bytes(thr
, (duk_uint8_t
*) &pu
.p
, sizeof(pu
));
408 #if defined(DUK_USE_INTEGER_LE)
409 duk_byteswap_bytes((duk_uint8_t
*) pu
.b
, sizeof(pu
));
411 return (void *) pu
.p
;
414 DUK_D(DUK_DPRINT("debug connection error: failed to decode pointer"));
415 DUK__SET_CONN_BROKEN(thr
, 1);
416 return (void *) NULL
;
419 DUK_LOCAL duk_double_t
duk__debug_read_double_raw(duk_hthread
*thr
) {
422 DUK_ASSERT(sizeof(du
.uc
) == 8);
423 duk_debug_read_bytes(thr
, (duk_uint8_t
*) du
.uc
, sizeof(du
.uc
));
424 DUK_DBLUNION_DOUBLE_NTOH(&du
);
429 DUK_INTERNAL duk_heaphdr
*duk_debug_read_heapptr(duk_hthread
*thr
) {
432 DUK_ASSERT(thr
!= NULL
);
434 x
= duk_debug_read_byte(thr
);
435 if (x
!= DUK_DBG_IB_HEAPPTR
) {
439 return (duk_heaphdr
*) duk__debug_read_pointer_raw(thr
);
442 DUK_D(DUK_DPRINT("debug connection error: failed to decode heapptr"));
443 DUK__SET_CONN_BROKEN(thr
, 1);
448 DUK_INTERNAL duk_heaphdr
*duk_debug_read_any_ptr(duk_hthread
*thr
) {
451 DUK_ASSERT(thr
!= NULL
);
453 x
= duk_debug_read_byte(thr
);
455 case DUK_DBG_IB_OBJECT
:
456 case DUK_DBG_IB_POINTER
:
457 case DUK_DBG_IB_HEAPPTR
:
458 /* Accept any pointer-like value; for 'object' dvalue, read
459 * and ignore the class number.
461 if (x
== DUK_DBG_IB_OBJECT
) {
462 duk_debug_skip_byte(thr
);
469 return (duk_heaphdr
*) duk__debug_read_pointer_raw(thr
);
472 DUK_D(DUK_DPRINT("debug connection error: failed to decode any pointer (object, pointer, heapptr)"));
473 DUK__SET_CONN_BROKEN(thr
, 1);
477 DUK_INTERNAL duk_tval
*duk_debug_read_tval(duk_hthread
*thr
) {
478 duk_context
*ctx
= (duk_context
*) thr
;
483 DUK_ASSERT(thr
!= NULL
);
485 x
= duk_debug_read_byte(thr
);
488 t
= (duk_uint_t
) (x
- 0xc0);
489 t
= (t
<< 8) + duk_debug_read_byte(thr
);
490 duk_push_uint(ctx
, (duk_uint_t
) t
);
494 duk_push_uint(ctx
, (duk_uint_t
) (x
- 0x80));
498 len
= (duk_uint32_t
) (x
- 0x60);
499 duk__debug_read_hstring_raw(thr
, len
);
504 case DUK_DBG_IB_INT4
: {
505 duk_int32_t i
= duk__debug_read_int32_raw(thr
);
506 duk_push_i32(ctx
, i
);
509 case DUK_DBG_IB_STR4
: {
510 len
= duk__debug_read_uint32_raw(thr
);
511 duk__debug_read_hstring_raw(thr
, len
);
514 case DUK_DBG_IB_STR2
: {
515 len
= duk__debug_read_uint16_raw(thr
);
516 duk__debug_read_hstring_raw(thr
, len
);
519 case DUK_DBG_IB_BUF4
: {
520 len
= duk__debug_read_uint32_raw(thr
);
521 duk__debug_read_hbuffer_raw(thr
, len
);
524 case DUK_DBG_IB_BUF2
: {
525 len
= duk__debug_read_uint16_raw(thr
);
526 duk__debug_read_hbuffer_raw(thr
, len
);
529 case DUK_DBG_IB_UNDEFINED
: {
530 duk_push_undefined(ctx
);
533 case DUK_DBG_IB_NULL
: {
537 case DUK_DBG_IB_TRUE
: {
541 case DUK_DBG_IB_FALSE
: {
545 case DUK_DBG_IB_NUMBER
: {
547 d
= duk__debug_read_double_raw(thr
);
548 duk_push_number(ctx
, d
);
551 case DUK_DBG_IB_OBJECT
: {
553 duk_debug_skip_byte(thr
);
554 h
= (duk_heaphdr
*) duk__debug_read_pointer_raw(thr
);
555 duk_push_heapptr(thr
, (void *) h
);
558 case DUK_DBG_IB_POINTER
: {
560 ptr
= duk__debug_read_pointer_raw(thr
);
561 duk_push_pointer(thr
, ptr
);
564 case DUK_DBG_IB_LIGHTFUNC
: {
565 /* XXX: Not needed for now, so not implemented. Note that
566 * function pointers may have different size/layout than
569 DUK_D(DUK_DPRINT("reading lightfunc values unimplemented"));
572 case DUK_DBG_IB_HEAPPTR
: {
574 h
= (duk_heaphdr
*) duk__debug_read_pointer_raw(thr
);
575 duk_push_heapptr(thr
, (void *) h
);
578 case DUK_DBG_IB_UNUSED
: /* unused: not accepted in inbound messages */
584 return DUK_GET_TVAL_NEGIDX(thr
, -1);
587 DUK_D(DUK_DPRINT("debug connection error: failed to decode tval"));
588 DUK__SET_CONN_BROKEN(thr
, 1);
593 * Debug connection write primitives
597 DUK_INTERNAL
void duk_debug_write_bytes(duk_hthread
*thr
, const duk_uint8_t
*data
, duk_size_t length
) {
599 const duk_uint8_t
*p
;
603 DUK_ASSERT(thr
!= NULL
);
604 DUK_ASSERT(length
== 0 || data
!= NULL
);
606 DUK_ASSERT(heap
!= NULL
);
608 if (heap
->dbg_write_cb
== NULL
) {
609 DUK_D(DUK_DPRINT("attempt to write %ld bytes in detached state, ignore", (long) length
));
613 /* Avoid doing an actual write callback with length == 0,
614 * because that's reserved for a write flush.
618 DUK_ASSERT(data
!= NULL
);
622 left
= (duk_size_t
) ((data
+ length
) - p
);
626 DUK_ASSERT(heap
->dbg_write_cb
!= NULL
);
627 DUK_ASSERT(left
>= 1);
628 #if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE)
631 got
= heap
->dbg_write_cb(heap
->dbg_udata
, (const char *) p
, left
);
632 if (got
== 0 || got
> left
) {
633 duk__debug_null_most_callbacks(thr
); /* avoid calling write callback in detach1() */
634 DUK_D(DUK_DPRINT("connection error during write"));
635 DUK__SET_CONN_BROKEN(thr
, 1);
642 DUK_INTERNAL
void duk_debug_write_byte(duk_hthread
*thr
, duk_uint8_t x
) {
643 duk_debug_write_bytes(thr
, (const duk_uint8_t
*) &x
, 1);
646 DUK_INTERNAL
void duk_debug_write_unused(duk_hthread
*thr
) {
647 duk_debug_write_byte(thr
, DUK_DBG_IB_UNUSED
);
650 DUK_INTERNAL
void duk_debug_write_undefined(duk_hthread
*thr
) {
651 duk_debug_write_byte(thr
, DUK_DBG_IB_UNDEFINED
);
654 #if defined(DUK_USE_DEBUGGER_INSPECT)
655 DUK_INTERNAL
void duk_debug_write_null(duk_hthread
*thr
) {
656 duk_debug_write_byte(thr
, DUK_DBG_IB_NULL
);
660 DUK_INTERNAL
void duk_debug_write_boolean(duk_hthread
*thr
, duk_uint_t val
) {
661 duk_debug_write_byte(thr
, val
? DUK_DBG_IB_TRUE
: DUK_DBG_IB_FALSE
);
664 /* Write signed 32-bit integer. */
665 DUK_INTERNAL
void duk_debug_write_int(duk_hthread
*thr
, duk_int32_t x
) {
669 DUK_ASSERT(thr
!= NULL
);
671 if (x
>= 0 && x
<= 0x3fL
) {
672 buf
[0] = (duk_uint8_t
) (0x80 + x
);
674 } else if (x
>= 0 && x
<= 0x3fffL
) {
675 buf
[0] = (duk_uint8_t
) (0xc0 + (x
>> 8));
676 buf
[1] = (duk_uint8_t
) (x
& 0xff);
679 /* Signed integers always map to 4 bytes now. */
680 buf
[0] = (duk_uint8_t
) DUK_DBG_IB_INT4
;
681 buf
[1] = (duk_uint8_t
) ((x
>> 24) & 0xff);
682 buf
[2] = (duk_uint8_t
) ((x
>> 16) & 0xff);
683 buf
[3] = (duk_uint8_t
) ((x
>> 8) & 0xff);
684 buf
[4] = (duk_uint8_t
) (x
& 0xff);
687 duk_debug_write_bytes(thr
, buf
, len
);
690 /* Write unsigned 32-bit integer. */
691 DUK_INTERNAL
void duk_debug_write_uint(duk_hthread
*thr
, duk_uint32_t x
) {
692 /* The debugger protocol doesn't support a plain integer encoding for
693 * the full 32-bit unsigned range (only 32-bit signed). For now,
694 * unsigned 32-bit values simply written as signed ones. This is not
695 * a concrete issue except for 32-bit heaphdr fields. Proper solutions
696 * would be to (a) write such integers as IEEE doubles or (b) add an
697 * unsigned 32-bit dvalue.
699 if (x
>= 0x80000000UL
) {
700 DUK_D(DUK_DPRINT("writing unsigned integer 0x%08lx as signed integer",
703 duk_debug_write_int(thr
, (duk_int32_t
) x
);
706 DUK_INTERNAL
void duk_debug_write_strbuf(duk_hthread
*thr
, const char *data
, duk_size_t length
, duk_uint8_t marker_base
) {
710 DUK_ASSERT(thr
!= NULL
);
711 DUK_ASSERT(length
== 0 || data
!= NULL
);
713 if (length
<= 0x1fUL
&& marker_base
== DUK_DBG_IB_STR4
) {
714 /* For strings, special form for short lengths. */
715 buf
[0] = (duk_uint8_t
) (0x60 + length
);
717 } else if (length
<= 0xffffUL
) {
718 buf
[0] = (duk_uint8_t
) (marker_base
+ 1);
719 buf
[1] = (duk_uint8_t
) (length
>> 8);
720 buf
[2] = (duk_uint8_t
) (length
& 0xff);
723 buf
[0] = (duk_uint8_t
) marker_base
;
724 buf
[1] = (duk_uint8_t
) (length
>> 24);
725 buf
[2] = (duk_uint8_t
) ((length
>> 16) & 0xff);
726 buf
[3] = (duk_uint8_t
) ((length
>> 8) & 0xff);
727 buf
[4] = (duk_uint8_t
) (length
& 0xff);
731 duk_debug_write_bytes(thr
, (const duk_uint8_t
*) buf
, buflen
);
732 duk_debug_write_bytes(thr
, (const duk_uint8_t
*) data
, length
);
735 DUK_INTERNAL
void duk_debug_write_string(duk_hthread
*thr
, const char *data
, duk_size_t length
) {
736 duk_debug_write_strbuf(thr
, data
, length
, DUK_DBG_IB_STR4
);
739 DUK_INTERNAL
void duk_debug_write_cstring(duk_hthread
*thr
, const char *data
) {
740 DUK_ASSERT(thr
!= NULL
);
742 duk_debug_write_string(thr
,
744 data
? DUK_STRLEN(data
) : 0);
747 DUK_INTERNAL
void duk_debug_write_hstring(duk_hthread
*thr
, duk_hstring
*h
) {
748 DUK_ASSERT(thr
!= NULL
);
750 /* XXX: differentiate null pointer from empty string? */
751 duk_debug_write_string(thr
,
752 (h
!= NULL
? (const char *) DUK_HSTRING_GET_DATA(h
) : NULL
),
753 (h
!= NULL
? (duk_size_t
) DUK_HSTRING_GET_BYTELEN(h
) : 0));
756 DUK_LOCAL
void duk__debug_write_hstring_safe_top(duk_hthread
*thr
) {
757 duk_context
*ctx
= (duk_context
*) thr
;
758 duk_debug_write_hstring(thr
, duk_safe_to_hstring(ctx
, -1));
761 DUK_INTERNAL
void duk_debug_write_buffer(duk_hthread
*thr
, const char *data
, duk_size_t length
) {
762 duk_debug_write_strbuf(thr
, data
, length
, DUK_DBG_IB_BUF4
);
765 DUK_INTERNAL
void duk_debug_write_hbuffer(duk_hthread
*thr
, duk_hbuffer
*h
) {
766 DUK_ASSERT(thr
!= NULL
);
768 duk_debug_write_buffer(thr
,
769 (h
!= NULL
? (const char *) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h
) : NULL
),
770 (h
!= NULL
? (duk_size_t
) DUK_HBUFFER_GET_SIZE(h
) : 0));
773 DUK_LOCAL
void duk__debug_write_pointer_raw(duk_hthread
*thr
, void *ptr
, duk_uint8_t ibyte
) {
777 DUK_ASSERT(thr
!= NULL
);
778 DUK_ASSERT(sizeof(ptr
) >= 1 && sizeof(ptr
) <= 16);
779 /* ptr may be NULL */
783 duk_debug_write_bytes(thr
, buf
, 2);
785 #if defined(DUK_USE_INTEGER_LE)
786 duk_byteswap_bytes((duk_uint8_t
*) pu
.b
, sizeof(pu
));
788 duk_debug_write_bytes(thr
, (const duk_uint8_t
*) &pu
.p
, (duk_size_t
) sizeof(pu
));
791 DUK_INTERNAL
void duk_debug_write_pointer(duk_hthread
*thr
, void *ptr
) {
792 duk__debug_write_pointer_raw(thr
, ptr
, DUK_DBG_IB_POINTER
);
795 #if defined(DUK_USE_DEBUGGER_DUMPHEAP) || defined(DUK_USE_DEBUGGER_INSPECT)
796 DUK_INTERNAL
void duk_debug_write_heapptr(duk_hthread
*thr
, duk_heaphdr
*h
) {
797 duk__debug_write_pointer_raw(thr
, (void *) h
, DUK_DBG_IB_HEAPPTR
);
799 #endif /* DUK_USE_DEBUGGER_DUMPHEAP || DUK_USE_DEBUGGER_INSPECT */
801 DUK_INTERNAL
void duk_debug_write_hobject(duk_hthread
*thr
, duk_hobject
*obj
) {
805 DUK_ASSERT(thr
!= NULL
);
806 DUK_ASSERT(sizeof(obj
) >= 1 && sizeof(obj
) <= 16);
807 DUK_ASSERT(obj
!= NULL
);
809 buf
[0] = DUK_DBG_IB_OBJECT
;
810 buf
[1] = (duk_uint8_t
) DUK_HOBJECT_GET_CLASS_NUMBER(obj
);
812 duk_debug_write_bytes(thr
, buf
, 3);
814 #if defined(DUK_USE_INTEGER_LE)
815 duk_byteswap_bytes((duk_uint8_t
*) pu
.b
, sizeof(pu
));
817 duk_debug_write_bytes(thr
, (const duk_uint8_t
*) &pu
.p
, (duk_size_t
) sizeof(pu
));
820 DUK_INTERNAL
void duk_debug_write_tval(duk_hthread
*thr
, duk_tval
*tv
) {
821 duk_c_function lf_func
;
822 duk_small_uint_t lf_flags
;
824 duk_double_union du1
;
825 duk_double_union du2
;
828 DUK_ASSERT(thr
!= NULL
);
829 DUK_ASSERT(tv
!= NULL
);
831 switch (DUK_TVAL_GET_TAG(tv
)) {
832 case DUK_TAG_UNDEFINED
:
833 duk_debug_write_byte(thr
, DUK_DBG_IB_UNDEFINED
);
836 duk_debug_write_byte(thr
, DUK_DBG_IB_UNUSED
);
839 duk_debug_write_byte(thr
, DUK_DBG_IB_NULL
);
841 case DUK_TAG_BOOLEAN
:
842 DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv
) == 0 ||
843 DUK_TVAL_GET_BOOLEAN(tv
) == 1);
844 duk_debug_write_boolean(thr
, DUK_TVAL_GET_BOOLEAN(tv
));
846 case DUK_TAG_POINTER
:
847 duk_debug_write_pointer(thr
, (void *) DUK_TVAL_GET_POINTER(tv
));
849 case DUK_TAG_LIGHTFUNC
:
850 DUK_TVAL_GET_LIGHTFUNC(tv
, lf_func
, lf_flags
);
851 buf
[0] = DUK_DBG_IB_LIGHTFUNC
;
852 buf
[1] = (duk_uint8_t
) (lf_flags
>> 8);
853 buf
[2] = (duk_uint8_t
) (lf_flags
& 0xff);
854 buf
[3] = sizeof(lf_func
);
855 duk_debug_write_bytes(thr
, buf
, 4);
856 duk_debug_write_bytes(thr
, (const duk_uint8_t
*) &lf_func
, sizeof(lf_func
));
859 duk_debug_write_hstring(thr
, DUK_TVAL_GET_STRING(tv
));
862 duk_debug_write_hobject(thr
, DUK_TVAL_GET_OBJECT(tv
));
865 duk_debug_write_hbuffer(thr
, DUK_TVAL_GET_BUFFER(tv
));
867 #if defined(DUK_USE_FASTINT)
868 case DUK_TAG_FASTINT
:
871 /* Numbers are normalized to big (network) endian. We can
872 * (but are not required) to use integer dvalues when there's
873 * no loss of precision.
875 * XXX: share check with other code; this check is slow but
876 * reliable and doesn't require careful exponent/mantissa
877 * mask tricks as in the fastint downgrade code.
879 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv
));
880 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
881 du1
.d
= DUK_TVAL_GET_NUMBER(tv
);
882 i32
= (duk_int32_t
) du1
.d
;
883 du2
.d
= (duk_double_t
) i32
;
885 DUK_DD(DUK_DDPRINT("i32=%ld du1=%02x%02x%02x%02x%02x%02x%02x%02x "
886 "du2=%02x%02x%02x%02x%02x%02x%02x%02x",
888 (unsigned int) du1
.uc
[0], (unsigned int) du1
.uc
[1],
889 (unsigned int) du1
.uc
[2], (unsigned int) du1
.uc
[3],
890 (unsigned int) du1
.uc
[4], (unsigned int) du1
.uc
[5],
891 (unsigned int) du1
.uc
[6], (unsigned int) du1
.uc
[7],
892 (unsigned int) du2
.uc
[0], (unsigned int) du2
.uc
[1],
893 (unsigned int) du2
.uc
[2], (unsigned int) du2
.uc
[3],
894 (unsigned int) du2
.uc
[4], (unsigned int) du2
.uc
[5],
895 (unsigned int) du2
.uc
[6], (unsigned int) du2
.uc
[7]));
897 if (DUK_MEMCMP((const void *) du1
.uc
, (const void *) du2
.uc
, sizeof(du1
.uc
)) == 0) {
898 duk_debug_write_int(thr
, i32
);
900 DUK_DBLUNION_DOUBLE_HTON(&du1
);
901 duk_debug_write_byte(thr
, DUK_DBG_IB_NUMBER
);
902 duk_debug_write_bytes(thr
, (const duk_uint8_t
*) du1
.uc
, sizeof(du1
.uc
));
907 #if defined(DUK_USE_DEBUGGER_DUMPHEAP)
908 /* Variant for writing duk_tvals so that any heap allocated values are
909 * written out as tagged heap pointers.
911 DUK_LOCAL
void duk__debug_write_tval_heapptr(duk_hthread
*thr
, duk_tval
*tv
) {
912 if (DUK_TVAL_IS_HEAP_ALLOCATED(tv
)) {
913 duk_heaphdr
*h
= DUK_TVAL_GET_HEAPHDR(tv
);
914 duk_debug_write_heapptr(thr
, h
);
916 duk_debug_write_tval(thr
, tv
);
919 #endif /* DUK_USE_DEBUGGER_DUMPHEAP */
922 * Debug connection message write helpers
926 DUK_INTERNAL
void duk_debug_write_request(duk_hthread
*thr
, duk_small_uint_t command
) {
927 duk_debug_write_byte(thr
, DUK_DBG_IB_REQUEST
);
928 duk_debug_write_int(thr
, command
);
932 DUK_INTERNAL
void duk_debug_write_reply(duk_hthread
*thr
) {
933 duk_debug_write_byte(thr
, DUK_DBG_IB_REPLY
);
936 DUK_INTERNAL
void duk_debug_write_error_eom(duk_hthread
*thr
, duk_small_uint_t err_code
, const char *msg
) {
937 /* Allow NULL 'msg' */
938 duk_debug_write_byte(thr
, DUK_DBG_IB_ERROR
);
939 duk_debug_write_int(thr
, (duk_int32_t
) err_code
);
940 duk_debug_write_cstring(thr
, msg
);
941 duk_debug_write_eom(thr
);
944 DUK_INTERNAL
void duk_debug_write_notify(duk_hthread
*thr
, duk_small_uint_t command
) {
945 duk_debug_write_byte(thr
, DUK_DBG_IB_NOTIFY
);
946 duk_debug_write_int(thr
, command
);
949 DUK_INTERNAL
void duk_debug_write_eom(duk_hthread
*thr
) {
950 duk_debug_write_byte(thr
, DUK_DBG_IB_EOM
);
952 /* As an initial implementation, write flush after every EOM (and the
953 * version identifier). A better implementation would flush only when
954 * Duktape is finished processing messages so that a flush only happens
955 * after all outbound messages are finished on that occasion.
957 duk_debug_write_flush(thr
);
961 * Status message and helpers
964 DUK_INTERNAL duk_uint_fast32_t
duk_debug_curr_line(duk_hthread
*thr
) {
965 duk_context
*ctx
= (duk_context
*) thr
;
967 duk_uint_fast32_t line
;
968 duk_uint_fast32_t pc
;
970 act
= duk_hthread_get_current_activation(thr
); /* may be NULL */
975 /* We're conceptually between two opcodes; act->pc indicates the next
976 * instruction to be executed. This is usually the correct pc/line to
977 * indicate in Status. (For the 'debugger' statement this now reports
978 * the pc/line after the debugger statement because the debugger opcode
979 * has already been executed.)
982 pc
= duk_hthread_get_act_curr_pc(thr
, act
);
984 /* XXX: this should be optimized to be a raw query and avoid valstack
985 * operations if possible.
987 duk_push_tval(ctx
, &act
->tv_func
);
988 line
= duk_hobject_pc2line_query(ctx
, -1, pc
);
993 DUK_INTERNAL
void duk_debug_send_status(duk_hthread
*thr
) {
994 duk_context
*ctx
= (duk_context
*) thr
;
997 duk_debug_write_notify(thr
, DUK_DBG_CMD_STATUS
);
998 duk_debug_write_int(thr
, thr
->heap
->dbg_paused
);
1000 DUK_ASSERT_DISABLE(thr
->callstack_top
>= 0); /* unsigned */
1001 if (thr
->callstack_top
== 0) {
1002 duk_debug_write_undefined(thr
);
1003 duk_debug_write_undefined(thr
);
1004 duk_debug_write_int(thr
, 0);
1005 duk_debug_write_int(thr
, 0);
1007 act
= thr
->callstack
+ thr
->callstack_top
- 1;
1008 duk_push_tval(ctx
, &act
->tv_func
);
1009 duk_get_prop_string(ctx
, -1, "fileName");
1010 duk__debug_write_hstring_safe_top(thr
);
1011 duk_get_prop_string(ctx
, -2, "name");
1012 duk__debug_write_hstring_safe_top(thr
);
1014 /* Report next pc/line to be executed. */
1015 duk_debug_write_uint(thr
, (duk_uint32_t
) duk_debug_curr_line(thr
));
1016 duk_debug_write_uint(thr
, (duk_uint32_t
) duk_hthread_get_act_curr_pc(thr
, act
));
1019 duk_debug_write_eom(thr
);
1022 #if defined(DUK_USE_DEBUGGER_THROW_NOTIFY)
1023 DUK_INTERNAL
void duk_debug_send_throw(duk_hthread
*thr
, duk_bool_t fatal
) {
1025 * NFY <int: 5> <int: fatal> <str: msg> <str: filename> <int: linenumber> EOM
1028 duk_context
*ctx
= (duk_context
*) thr
;
1029 duk_activation
*act
;
1032 DUK_ASSERT(thr
->valstack_top
> thr
->valstack
); /* At least: ... [err] */
1034 duk_debug_write_notify(thr
, DUK_DBG_CMD_THROW
);
1035 duk_debug_write_int(thr
, fatal
);
1037 /* Report thrown value to client coerced to string */
1039 duk__debug_write_hstring_safe_top(thr
);
1042 if (duk_is_error(ctx
, -1)) {
1043 /* Error instance, use augmented error data directly */
1044 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_FILE_NAME
);
1045 duk__debug_write_hstring_safe_top(thr
);
1046 duk_get_prop_stridx(ctx
, -2, DUK_STRIDX_LINE_NUMBER
);
1047 duk_debug_write_uint(thr
, duk_get_uint(ctx
, -1));
1049 /* For anything other than an Error instance, we calculate the error
1050 * location directly from the current activation.
1052 act
= thr
->callstack
+ thr
->callstack_top
- 1;
1053 duk_push_tval(ctx
, &act
->tv_func
);
1054 duk_get_prop_string(ctx
, -1, "fileName");
1055 duk__debug_write_hstring_safe_top(thr
);
1056 pc
= duk_hthread_get_act_prev_pc(thr
, act
);
1057 duk_debug_write_uint(thr
, (duk_uint32_t
) duk_hobject_pc2line_query(ctx
, -2, pc
));
1059 duk_pop_2(ctx
); /* shared pop */
1061 duk_debug_write_eom(thr
);
1063 #endif /* DUK_USE_DEBUGGER_THROW_NOTIFY */
1066 * Debug message processing
1070 DUK_LOCAL duk_bool_t
duk__debug_skip_dvalue(duk_hthread
*thr
) {
1074 x
= duk_debug_read_byte(thr
);
1077 duk_debug_skip_byte(thr
);
1084 duk_debug_skip_bytes(thr
, x
- 0x60);
1088 case DUK_DBG_IB_EOM
:
1089 return 1; /* Return 1: got EOM */
1090 case DUK_DBG_IB_REQUEST
:
1091 case DUK_DBG_IB_REPLY
:
1092 case DUK_DBG_IB_ERROR
:
1093 case DUK_DBG_IB_NOTIFY
:
1095 case DUK_DBG_IB_INT4
:
1096 (void) duk__debug_read_uint32_raw(thr
);
1098 case DUK_DBG_IB_STR4
:
1099 case DUK_DBG_IB_BUF4
:
1100 len
= duk__debug_read_uint32_raw(thr
);
1101 duk_debug_skip_bytes(thr
, len
);
1103 case DUK_DBG_IB_STR2
:
1104 case DUK_DBG_IB_BUF2
:
1105 len
= duk__debug_read_uint16_raw(thr
);
1106 duk_debug_skip_bytes(thr
, len
);
1108 case DUK_DBG_IB_UNUSED
:
1109 case DUK_DBG_IB_UNDEFINED
:
1110 case DUK_DBG_IB_NULL
:
1111 case DUK_DBG_IB_TRUE
:
1112 case DUK_DBG_IB_FALSE
:
1114 case DUK_DBG_IB_NUMBER
:
1115 duk_debug_skip_bytes(thr
, 8);
1117 case DUK_DBG_IB_OBJECT
:
1118 duk_debug_skip_byte(thr
);
1119 len
= duk_debug_read_byte(thr
);
1120 duk_debug_skip_bytes(thr
, len
);
1122 case DUK_DBG_IB_POINTER
:
1123 case DUK_DBG_IB_HEAPPTR
:
1124 len
= duk_debug_read_byte(thr
);
1125 duk_debug_skip_bytes(thr
, len
);
1127 case DUK_DBG_IB_LIGHTFUNC
:
1128 duk_debug_skip_bytes(thr
, 2);
1129 len
= duk_debug_read_byte(thr
);
1130 duk_debug_skip_bytes(thr
, len
);
1139 DUK__SET_CONN_BROKEN(thr
, 1);
1140 return 1; /* Pretend like we got EOM */
1143 /* Skip dvalues to EOM. */
1144 DUK_LOCAL
void duk__debug_skip_to_eom(duk_hthread
*thr
) {
1146 if (duk__debug_skip_dvalue(thr
)) {
1156 DUK_LOCAL
void duk__debug_handle_basic_info(duk_hthread
*thr
, duk_heap
*heap
) {
1158 DUK_D(DUK_DPRINT("debug command Version"));
1160 duk_debug_write_reply(thr
);
1161 duk_debug_write_int(thr
, DUK_VERSION
);
1162 duk_debug_write_cstring(thr
, DUK_GIT_DESCRIBE
);
1163 duk_debug_write_cstring(thr
, DUK_USE_TARGET_INFO
);
1164 #if defined(DUK_USE_DOUBLE_LE)
1165 duk_debug_write_int(thr
, 1);
1166 #elif defined(DUK_USE_DOUBLE_ME)
1167 duk_debug_write_int(thr
, 2);
1168 #elif defined(DUK_USE_DOUBLE_BE)
1169 duk_debug_write_int(thr
, 3);
1171 duk_debug_write_int(thr
, 0);
1173 duk_debug_write_int(thr
, (duk_int_t
) sizeof(void *));
1174 duk_debug_write_eom(thr
);
1177 DUK_LOCAL
void duk__debug_handle_trigger_status(duk_hthread
*thr
, duk_heap
*heap
) {
1179 DUK_D(DUK_DPRINT("debug command TriggerStatus"));
1181 duk_debug_write_reply(thr
);
1182 duk_debug_write_eom(thr
);
1183 heap
->dbg_state_dirty
= 1;
1186 DUK_LOCAL
void duk__debug_handle_pause(duk_hthread
*thr
, duk_heap
*heap
) {
1187 DUK_D(DUK_DPRINT("debug command Pause"));
1189 DUK_HEAP_SET_PAUSED(heap
);
1190 duk_debug_write_reply(thr
);
1191 duk_debug_write_eom(thr
);
1194 DUK_LOCAL
void duk__debug_handle_resume(duk_hthread
*thr
, duk_heap
*heap
) {
1195 DUK_D(DUK_DPRINT("debug command Resume"));
1197 DUK_HEAP_CLEAR_PAUSED(heap
);
1198 duk_debug_write_reply(thr
);
1199 duk_debug_write_eom(thr
);
1202 DUK_LOCAL
void duk__debug_handle_step(duk_hthread
*thr
, duk_heap
*heap
, duk_int32_t cmd
) {
1203 duk_small_uint_t step_type
;
1204 duk_uint_fast32_t line
;
1206 DUK_D(DUK_DPRINT("debug command StepInto/StepOver/StepOut: %d", (int) cmd
));
1208 if (cmd
== DUK_DBG_CMD_STEPINTO
) {
1209 step_type
= DUK_STEP_TYPE_INTO
;
1210 } else if (cmd
== DUK_DBG_CMD_STEPOVER
) {
1211 step_type
= DUK_STEP_TYPE_OVER
;
1213 DUK_ASSERT(cmd
== DUK_DBG_CMD_STEPOUT
);
1214 step_type
= DUK_STEP_TYPE_OUT
;
1217 line
= duk_debug_curr_line(thr
);
1219 heap
->dbg_paused
= 0;
1220 heap
->dbg_step_type
= step_type
;
1221 heap
->dbg_step_thread
= thr
;
1222 heap
->dbg_step_csindex
= thr
->callstack_top
- 1;
1223 heap
->dbg_step_startline
= line
;
1224 heap
->dbg_state_dirty
= 1;
1226 DUK_D(DUK_DPRINT("cannot determine current line, stepinto/stepover/stepout ignored"));
1228 duk_debug_write_reply(thr
);
1229 duk_debug_write_eom(thr
);
1232 DUK_LOCAL
void duk__debug_handle_list_break(duk_hthread
*thr
, duk_heap
*heap
) {
1235 DUK_D(DUK_DPRINT("debug command ListBreak"));
1236 duk_debug_write_reply(thr
);
1237 for (i
= 0; i
< (duk_small_int_t
) heap
->dbg_breakpoint_count
; i
++) {
1238 duk_debug_write_hstring(thr
, heap
->dbg_breakpoints
[i
].filename
);
1239 duk_debug_write_uint(thr
, (duk_uint32_t
) heap
->dbg_breakpoints
[i
].line
);
1241 duk_debug_write_eom(thr
);
1244 DUK_LOCAL
void duk__debug_handle_add_break(duk_hthread
*thr
, duk_heap
*heap
) {
1245 duk_hstring
*filename
;
1246 duk_uint32_t linenumber
;
1247 duk_small_int_t idx
;
1251 filename
= duk_debug_read_hstring(thr
);
1252 linenumber
= (duk_uint32_t
) duk_debug_read_int(thr
);
1253 DUK_D(DUK_DPRINT("debug command AddBreak: %!O:%ld", (duk_hobject
*) filename
, (long) linenumber
));
1254 idx
= duk_debug_add_breakpoint(thr
, filename
, linenumber
);
1256 duk_debug_write_reply(thr
);
1257 duk_debug_write_int(thr
, (duk_int32_t
) idx
);
1258 duk_debug_write_eom(thr
);
1260 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_TOOMANY
, "no space for breakpoint");
1264 DUK_LOCAL
void duk__debug_handle_del_break(duk_hthread
*thr
, duk_heap
*heap
) {
1265 duk_small_uint_t idx
;
1269 DUK_D(DUK_DPRINT("debug command DelBreak"));
1270 idx
= (duk_small_uint_t
) duk_debug_read_int(thr
);
1271 if (duk_debug_remove_breakpoint(thr
, idx
)) {
1272 duk_debug_write_reply(thr
);
1273 duk_debug_write_eom(thr
);
1275 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "invalid breakpoint index");
1279 DUK_LOCAL
void duk__debug_handle_get_var(duk_hthread
*thr
, duk_heap
*heap
) {
1280 duk_context
*ctx
= (duk_context
*) thr
;
1286 DUK_D(DUK_DPRINT("debug command GetVar"));
1288 str
= duk_debug_read_hstring(thr
); /* push to stack */
1289 DUK_ASSERT(str
!= NULL
);
1290 if (duk_debug_peek_byte(thr
) != DUK_DBG_IB_EOM
) {
1291 level
= duk_debug_read_int(thr
); /* optional callstack level */
1292 if (level
>= 0 || -level
> (duk_int32_t
) thr
->callstack_top
) {
1293 DUK_D(DUK_DPRINT("invalid callstack level for GetVar"));
1294 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "invalid callstack level");
1301 if (thr
->callstack_top
> 0) {
1302 rc
= duk_js_getvar_activation(thr
,
1303 thr
->callstack
+ thr
->callstack_top
+ level
,
1307 /* No activation, no variable access. Could also pretend
1308 * we're in the global program context and read stuff off
1309 * the global object.
1311 DUK_D(DUK_DPRINT("callstack empty, no activation -> ignore getvar"));
1315 duk_debug_write_reply(thr
);
1317 duk_debug_write_int(thr
, 1);
1318 DUK_ASSERT(duk_get_tval(ctx
, -2) != NULL
);
1319 duk_debug_write_tval(thr
, duk_get_tval(ctx
, -2));
1321 duk_debug_write_int(thr
, 0);
1322 duk_debug_write_unused(thr
);
1324 duk_debug_write_eom(thr
);
1327 DUK_LOCAL
void duk__debug_handle_put_var(duk_hthread
*thr
, duk_heap
*heap
) {
1333 DUK_D(DUK_DPRINT("debug command PutVar"));
1335 str
= duk_debug_read_hstring(thr
); /* push to stack */
1336 DUK_ASSERT(str
!= NULL
);
1337 tv
= duk_debug_read_tval(thr
);
1342 if (duk_debug_peek_byte(thr
) != DUK_DBG_IB_EOM
) {
1343 level
= duk_debug_read_int(thr
); /* optional callstack level */
1344 if (level
>= 0 || -level
> (duk_int32_t
) thr
->callstack_top
) {
1345 DUK_D(DUK_DPRINT("invalid callstack level for PutVar"));
1346 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "invalid callstack level");
1353 if (thr
->callstack_top
> 0) {
1354 duk_js_putvar_activation(thr
,
1355 thr
->callstack
+ thr
->callstack_top
+ level
,
1360 DUK_D(DUK_DPRINT("callstack empty, no activation -> ignore putvar"));
1363 /* XXX: Current putvar implementation doesn't have a success flag,
1364 * add one and send to debug client?
1366 duk_debug_write_reply(thr
);
1367 duk_debug_write_eom(thr
);
1370 DUK_LOCAL
void duk__debug_handle_get_call_stack(duk_hthread
*thr
, duk_heap
*heap
) {
1371 duk_context
*ctx
= (duk_context
*) thr
;
1372 duk_hthread
*curr_thr
= thr
;
1373 duk_activation
*curr_act
;
1374 duk_uint_fast32_t pc
;
1375 duk_uint_fast32_t line
;
1378 DUK_ASSERT(thr
!= NULL
);
1381 duk_debug_write_reply(thr
);
1382 while (curr_thr
!= NULL
) {
1383 i
= curr_thr
->callstack_top
;
1386 curr_act
= curr_thr
->callstack
+ i
;
1388 /* PC/line semantics here are:
1389 * - For callstack top we're conceptually between two
1390 * opcodes and current PC indicates next line to
1391 * execute, so report that (matches Status).
1392 * - For other activations we're conceptually still
1393 * executing the instruction at PC-1, so report that
1394 * (matches error stacktrace behavior).
1395 * - See: https://github.com/svaarala/duktape/issues/281
1398 /* XXX: optimize to use direct reads, i.e. avoid
1399 * value stack operations.
1401 duk_push_tval(ctx
, &curr_act
->tv_func
);
1402 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_FILE_NAME
);
1403 duk__debug_write_hstring_safe_top(thr
);
1404 duk_get_prop_stridx(ctx
, -2, DUK_STRIDX_NAME
);
1405 duk__debug_write_hstring_safe_top(thr
);
1406 pc
= duk_hthread_get_act_curr_pc(thr
, curr_act
);
1407 if (i
!= curr_thr
->callstack_top
- 1 && pc
> 0) {
1410 line
= duk_hobject_pc2line_query(ctx
, -3, pc
);
1411 duk_debug_write_uint(thr
, (duk_uint32_t
) line
);
1412 duk_debug_write_uint(thr
, (duk_uint32_t
) pc
);
1415 curr_thr
= curr_thr
->resumer
;
1417 /* SCANBUILD: warning about 'thr' potentially being NULL here,
1418 * warning is incorrect because thr != NULL always here.
1420 duk_debug_write_eom(thr
);
1423 DUK_LOCAL
void duk__debug_handle_get_locals(duk_hthread
*thr
, duk_heap
*heap
) {
1424 duk_context
*ctx
= (duk_context
*) thr
;
1425 duk_activation
*curr_act
;
1427 duk_hstring
*varname
;
1431 if (duk_debug_peek_byte(thr
) != DUK_DBG_IB_EOM
) {
1432 level
= duk_debug_read_int(thr
); /* optional callstack level */
1433 if (level
>= 0 || -level
> (duk_int32_t
) thr
->callstack_top
) {
1434 DUK_D(DUK_DPRINT("invalid callstack level for GetLocals"));
1435 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "invalid callstack level");
1438 duk_debug_write_reply(thr
);
1440 duk_debug_write_reply(thr
);
1441 if (thr
->callstack_top
== 0) {
1442 goto callstack_empty
;
1447 curr_act
= thr
->callstack
+ thr
->callstack_top
+ level
;
1449 /* XXX: several nice-to-have improvements here:
1450 * - Use direct reads avoiding value stack operations
1451 * - Avoid triggering getters, indicate getter values to debug client
1452 * - If side effects are possible, add error catching
1455 duk_push_tval(ctx
, &curr_act
->tv_func
);
1456 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_INT_VARMAP
);
1457 if (duk_is_object(ctx
, -1)) {
1458 duk_enum(ctx
, -1, 0 /*enum_flags*/);
1459 while (duk_next(ctx
, -1 /*enum_index*/, 0 /*get_value*/)) {
1460 varname
= duk_get_hstring(ctx
, -1);
1461 DUK_ASSERT(varname
!= NULL
);
1463 duk_js_getvar_activation(thr
, curr_act
, varname
, 0 /*throw_flag*/);
1464 /* [ ... func varmap enum key value this ] */
1465 duk_debug_write_hstring(thr
, duk_get_hstring(ctx
, -3));
1466 duk_debug_write_tval(thr
, duk_get_tval(ctx
, -2));
1467 duk_pop_3(ctx
); /* -> [ ... func varmap enum ] */
1470 DUK_D(DUK_DPRINT("varmap is not an object in GetLocals, ignore"));
1474 duk_debug_write_eom(thr
);
1477 DUK_LOCAL
void duk__debug_handle_eval(duk_hthread
*thr
, duk_heap
*heap
) {
1478 duk_context
*ctx
= (duk_context
*) thr
;
1479 duk_small_uint_t call_flags
;
1481 duk_small_int_t eval_err
;
1486 DUK_D(DUK_DPRINT("debug command Eval"));
1488 /* The eval code is executed within the lexical environment of a specified
1489 * activation. For now, use global object eval() function, with the eval
1490 * considered a 'direct call to eval'.
1492 * Callstack level for debug commands only affects scope -- the callstack
1493 * as seen by, e.g. Duktape.act() will be the same regardless.
1496 /* nargs == 2 so we can pass a callstack level to eval(). */
1497 duk_push_c_function(ctx
, duk_bi_global_object_eval
, 2 /*nargs*/);
1498 duk_push_undefined(ctx
); /* 'this' binding shouldn't matter here */
1500 (void) duk_debug_read_hstring(thr
);
1501 if (duk_debug_peek_byte(thr
) != DUK_DBG_IB_EOM
) {
1502 level
= duk_debug_read_int(thr
); /* optional callstack level */
1503 if (level
>= 0 || -level
> (duk_int32_t
) thr
->callstack_top
) {
1504 DUK_D(DUK_DPRINT("invalid callstack level for Eval"));
1505 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "invalid callstack level");
1512 DUK_ASSERT(level
< 0 && -level
<= (duk_int32_t
) thr
->callstack_top
);
1513 duk_push_int(ctx
, level
- 1); /* compensate for eval() call */
1515 /* [ ... eval "eval" eval_input level ] */
1518 if (thr
->callstack_top
>= (duk_size_t
) -level
) {
1519 duk_activation
*act
;
1522 act
= thr
->callstack
+ thr
->callstack_top
+ level
;
1523 fun
= DUK_ACT_GET_FUNC(act
);
1524 if (fun
!= NULL
&& DUK_HOBJECT_IS_COMPILEDFUNCTION(fun
)) {
1525 /* Direct eval requires that there's a current
1526 * activation and it is an Ecmascript function.
1527 * When Eval is executed from e.g. cooperate API
1528 * call we'll need to do an indirect eval instead.
1530 call_flags
|= DUK_CALL_FLAG_DIRECT_EVAL
;
1534 call_ret
= duk_handle_call_protected(thr
, 2 /*num_stack_args*/, call_flags
);
1536 if (call_ret
== DUK_EXEC_SUCCESS
) {
1538 /* Use result value as is. */
1540 /* For errors a string coerced result is most informative
1541 * right now, as the debug client doesn't have the capability
1542 * to traverse the error object.
1545 duk_safe_to_string(ctx
, -1);
1548 /* [ ... result ] */
1550 duk_debug_write_reply(thr
);
1551 duk_debug_write_int(thr
, (duk_int32_t
) eval_err
);
1552 DUK_ASSERT(duk_get_tval(ctx
, -1) != NULL
);
1553 duk_debug_write_tval(thr
, duk_get_tval(ctx
, -1));
1554 duk_debug_write_eom(thr
);
1557 DUK_LOCAL
void duk__debug_handle_detach(duk_hthread
*thr
, duk_heap
*heap
) {
1559 DUK_D(DUK_DPRINT("debug command Detach"));
1561 duk_debug_write_reply(thr
);
1562 duk_debug_write_eom(thr
);
1564 DUK_D(DUK_DPRINT("debug connection detached, mark broken"));
1565 DUK__SET_CONN_BROKEN(thr
, 0); /* not an error */
1568 DUK_LOCAL
void duk__debug_handle_apprequest(duk_hthread
*thr
, duk_heap
*heap
) {
1569 duk_context
*ctx
= (duk_context
*) thr
;
1572 DUK_D(DUK_DPRINT("debug command AppRequest"));
1574 old_top
= duk_get_top(ctx
); /* save stack top */
1576 if (heap
->dbg_request_cb
!= NULL
) {
1578 duk_idx_t nvalues
= 0;
1581 /* Read tvals from the message and push them onto the valstack,
1582 * then call the request callback to process the request.
1584 while (duk_debug_peek_byte(thr
) != DUK_DBG_IB_EOM
) {
1586 if (!duk_check_stack(ctx
, 1)) {
1587 DUK_D(DUK_DPRINT("failed to allocate space for request dvalue(s)"));
1590 tv
= duk_debug_read_tval(thr
); /* push to stack */
1597 DUK_ASSERT(duk_get_top(ctx
) == old_top
+ nvalues
);
1599 /* Request callback should push values for reply to client onto valstack */
1600 DUK_D(DUK_DPRINT("calling into AppRequest request_cb with nvalues=%ld, old_top=%ld, top=%ld",
1601 (long) nvalues
, (long) old_top
, (long) duk_get_top(ctx
)));
1602 nrets
= heap
->dbg_request_cb(ctx
, heap
->dbg_udata
, nvalues
);
1603 DUK_D(DUK_DPRINT("returned from AppRequest request_cb; nvalues=%ld -> nrets=%ld, old_top=%ld, top=%ld",
1604 (long) nvalues
, (long) nrets
, (long) old_top
, (long) duk_get_top(ctx
)));
1606 DUK_ASSERT(duk_get_top(ctx
) >= old_top
+ nrets
);
1607 if (duk_get_top(ctx
) < old_top
+ nrets
) {
1608 DUK_D(DUK_DPRINT("AppRequest callback doesn't match value stack configuration, "
1609 "top=%ld < old_top=%ld + nrets=%ld; "
1610 "this might mean it's unsafe to continue!",
1611 (long) duk_get_top(ctx
), (long) old_top
, (long) nrets
));
1615 /* Reply with tvals pushed by request callback */
1616 duk_debug_write_byte(thr
, DUK_DBG_IB_REPLY
);
1617 top
= duk_get_top(ctx
);
1618 for (idx
= top
- nrets
; idx
< top
; idx
++) {
1619 duk_debug_write_tval(thr
, DUK_GET_TVAL_POSIDX(ctx
, idx
));
1621 duk_debug_write_eom(thr
);
1623 DUK_ASSERT(duk_get_top(ctx
) >= old_top
+ 1);
1624 if (duk_get_top(ctx
) < old_top
+ 1) {
1625 DUK_D(DUK_DPRINT("request callback return value doesn't match value stack configuration"));
1628 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_APPLICATION
, duk_get_string(ctx
, -1));
1631 duk_set_top(ctx
, old_top
); /* restore stack top */
1633 DUK_D(DUK_DPRINT("no request callback, treat AppRequest as unsupported"));
1634 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNSUPPORTED
, "AppRequest unsupported by target");
1640 duk_set_top(ctx
, old_top
); /* restore stack top */
1641 DUK__SET_CONN_BROKEN(thr
, 1);
1648 #if defined(DUK_USE_DEBUGGER_DUMPHEAP)
1649 /* XXX: this has some overlap with object inspection; remove this and make
1650 * DumpHeap return lists of heapptrs instead?
1652 DUK_LOCAL
void duk__debug_dump_heaphdr(duk_hthread
*thr
, duk_heap
*heap
, duk_heaphdr
*hdr
) {
1655 duk_debug_write_heapptr(thr
, hdr
);
1656 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HEAPHDR_GET_TYPE(hdr
));
1657 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HEAPHDR_GET_FLAGS_RAW(hdr
));
1658 #if defined(DUK_USE_REFERENCE_COUNTING)
1659 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HEAPHDR_GET_REFCOUNT(hdr
));
1661 duk_debug_write_int(thr
, (duk_int32_t
) -1);
1664 switch (DUK_HEAPHDR_GET_TYPE(hdr
)) {
1665 case DUK_HTYPE_STRING
: {
1666 duk_hstring
*h
= (duk_hstring
*) hdr
;
1668 duk_debug_write_uint(thr
, (duk_int32_t
) DUK_HSTRING_GET_BYTELEN(h
));
1669 duk_debug_write_uint(thr
, (duk_int32_t
) DUK_HSTRING_GET_CHARLEN(h
));
1670 duk_debug_write_uint(thr
, (duk_int32_t
) DUK_HSTRING_GET_HASH(h
));
1671 duk_debug_write_hstring(thr
, h
);
1674 case DUK_HTYPE_OBJECT
: {
1675 duk_hobject
*h
= (duk_hobject
*) hdr
;
1677 duk_uint_fast32_t i
;
1679 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HOBJECT_GET_CLASS_NUMBER(h
));
1680 duk_debug_write_heapptr(thr
, (duk_heaphdr
*) DUK_HOBJECT_GET_PROTOTYPE(heap
, h
));
1681 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HOBJECT_GET_ESIZE(h
));
1682 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HOBJECT_GET_ENEXT(h
));
1683 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HOBJECT_GET_ASIZE(h
));
1684 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HOBJECT_GET_HSIZE(h
));
1686 for (i
= 0; i
< (duk_uint_fast32_t
) DUK_HOBJECT_GET_ENEXT(h
); i
++) {
1687 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HOBJECT_E_GET_FLAGS(heap
, h
, i
));
1688 k
= DUK_HOBJECT_E_GET_KEY(heap
, h
, i
);
1689 duk_debug_write_heapptr(thr
, (duk_heaphdr
*) k
);
1691 duk_debug_write_int(thr
, 0); /* isAccessor */
1692 duk_debug_write_unused(thr
);
1695 if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap
, h
, i
)) {
1696 duk_debug_write_int(thr
, 1); /* isAccessor */
1697 duk_debug_write_heapptr(thr
, (duk_heaphdr
*) DUK_HOBJECT_E_GET_VALUE_PTR(heap
, h
, i
)->a
.get
);
1698 duk_debug_write_heapptr(thr
, (duk_heaphdr
*) DUK_HOBJECT_E_GET_VALUE_PTR(heap
, h
, i
)->a
.set
);
1700 duk_debug_write_int(thr
, 0); /* isAccessor */
1702 duk__debug_write_tval_heapptr(thr
, &DUK_HOBJECT_E_GET_VALUE_PTR(heap
, h
, i
)->v
);
1706 for (i
= 0; i
< (duk_uint_fast32_t
) DUK_HOBJECT_GET_ASIZE(h
); i
++) {
1707 /* Note: array dump will include elements beyond
1710 duk__debug_write_tval_heapptr(thr
, DUK_HOBJECT_A_GET_VALUE_PTR(heap
, h
, i
));
1714 case DUK_HTYPE_BUFFER
: {
1715 duk_hbuffer
*h
= (duk_hbuffer
*) hdr
;
1717 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HBUFFER_GET_SIZE(h
));
1718 duk_debug_write_buffer(thr
, (const char *) DUK_HBUFFER_GET_DATA_PTR(heap
, h
), (duk_size_t
) DUK_HBUFFER_GET_SIZE(h
));
1722 DUK_D(DUK_DPRINT("invalid htype: %d", (int) DUK_HEAPHDR_GET_TYPE(hdr
)));
1727 DUK_LOCAL
void duk__debug_dump_heap_allocated(duk_hthread
*thr
, duk_heap
*heap
) {
1730 hdr
= heap
->heap_allocated
;
1731 while (hdr
!= NULL
) {
1732 duk__debug_dump_heaphdr(thr
, heap
, hdr
);
1733 hdr
= DUK_HEAPHDR_GET_NEXT(heap
, hdr
);
1737 #if defined(DUK_USE_STRTAB_CHAIN)
1738 DUK_LOCAL
void duk__debug_dump_strtab_chain(duk_hthread
*thr
, duk_heap
*heap
) {
1739 duk_uint_fast32_t i
, j
;
1740 duk_strtab_entry
*e
;
1741 #if defined(DUK_USE_HEAPPTR16)
1748 for (i
= 0; i
< DUK_STRTAB_CHAIN_SIZE
; i
++) {
1749 e
= heap
->strtable
+ i
;
1750 if (e
->listlen
> 0) {
1751 #if defined(DUK_USE_HEAPPTR16)
1752 lst
= (duk_uint16_t
*) DUK_USE_HEAPPTR_DEC16(heap
->heap_udata
, e
->u
.strlist16
);
1756 DUK_ASSERT(lst
!= NULL
);
1758 for (j
= 0; j
< e
->listlen
; j
++) {
1759 #if defined(DUK_USE_HEAPPTR16)
1760 h
= DUK_USE_HEAPPTR_DEC16(heap
->heap_udata
, lst
[j
]);
1765 duk__debug_dump_heaphdr(thr
, heap
, (duk_heaphdr
*) h
);
1769 #if defined(DUK_USE_HEAPPTR16)
1770 h
= DUK_USE_HEAPPTR_DEC16(heap
->heap_udata
, e
->u
.str16
);
1775 duk__debug_dump_heaphdr(thr
, heap
, (duk_heaphdr
*) h
);
1780 #endif /* DUK_USE_STRTAB_CHAIN */
1782 #if defined(DUK_USE_STRTAB_PROBE)
1783 DUK_LOCAL
void duk__debug_dump_strtab_probe(duk_hthread
*thr
, duk_heap
*heap
) {
1787 for (i
= 0; i
< heap
->st_size
; i
++) {
1788 #if defined(DUK_USE_HEAPPTR16)
1789 h
= DUK_USE_HEAPPTR_DEC16(heap
->heap_udata
, heap
->strtable16
[i
]);
1791 h
= heap
->strtable
[i
];
1793 if (h
== NULL
|| h
== DUK_STRTAB_DELETED_MARKER(heap
)) {
1797 duk__debug_dump_heaphdr(thr
, heap
, (duk_heaphdr
*) h
);
1800 #endif /* DUK_USE_STRTAB_PROBE */
1802 DUK_LOCAL
void duk__debug_handle_dump_heap(duk_hthread
*thr
, duk_heap
*heap
) {
1803 DUK_D(DUK_DPRINT("debug command DumpHeap"));
1805 duk_debug_write_reply(thr
);
1806 duk__debug_dump_heap_allocated(thr
, heap
);
1807 #if defined(DUK_USE_STRTAB_CHAIN)
1808 duk__debug_dump_strtab_chain(thr
, heap
);
1810 #if defined(DUK_USE_STRTAB_PROBE)
1811 duk__debug_dump_strtab_probe(thr
, heap
);
1813 duk_debug_write_eom(thr
);
1815 #endif /* DUK_USE_DEBUGGER_DUMPHEAP */
1817 DUK_LOCAL
void duk__debug_handle_get_bytecode(duk_hthread
*thr
, duk_heap
*heap
) {
1818 duk_activation
*act
;
1819 duk_hcompiledfunction
*fun
= NULL
;
1823 duk_int32_t level
= -1;
1828 DUK_D(DUK_DPRINT("debug command GetBytecode"));
1830 ibyte
= duk_debug_peek_byte(thr
);
1831 if (ibyte
!= DUK_DBG_IB_EOM
) {
1832 tv
= duk_debug_read_tval(thr
);
1837 if (DUK_TVAL_IS_OBJECT(tv
)) {
1838 /* tentative, checked later */
1839 fun
= (duk_hcompiledfunction
*) DUK_TVAL_GET_OBJECT(tv
);
1840 DUK_ASSERT(fun
!= NULL
);
1841 } else if (DUK_TVAL_IS_NUMBER(tv
)) {
1842 level
= (duk_int32_t
) DUK_TVAL_GET_NUMBER(tv
);
1844 DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!T", tv
));
1850 if (level
>= 0 || -level
> (duk_int32_t
) thr
->callstack_top
) {
1851 DUK_D(DUK_DPRINT("invalid callstack level for GetBytecode"));
1854 act
= thr
->callstack
+ thr
->callstack_top
+ level
;
1855 fun
= (duk_hcompiledfunction
*) DUK_ACT_GET_FUNC(act
);
1858 if (fun
== NULL
|| !DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject
*) fun
)) {
1859 DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!O", fun
));
1862 DUK_ASSERT(fun
!= NULL
&& DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject
*) fun
));
1864 duk_debug_write_reply(thr
);
1865 n
= DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(heap
, fun
);
1866 duk_debug_write_int(thr
, (duk_int32_t
) n
);
1867 tv
= DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(heap
, fun
);
1868 for (i
= 0; i
< n
; i
++) {
1869 duk_debug_write_tval(thr
, tv
);
1872 n
= DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(heap
, fun
);
1873 duk_debug_write_int(thr
, (duk_int32_t
) n
);
1874 fn
= DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(heap
, fun
);
1875 for (i
= 0; i
< n
; i
++) {
1876 duk_debug_write_hobject(thr
, *fn
);
1879 duk_debug_write_string(thr
,
1880 (const char *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(heap
, fun
),
1881 (duk_size_t
) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(heap
, fun
));
1882 duk_debug_write_eom(thr
);
1886 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNKNOWN
, "invalid argument");
1890 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "invalid callstack level");
1895 * Object inspection commands: GetHeapObjInfo, GetObjPropDesc,
1896 * GetObjPropDescRange
1899 #if defined(DUK_USE_DEBUGGER_INSPECT)
1902 DUK_LOCAL
const char * const duk__debug_getinfo_heaphdr_keys
[] = {
1908 /* NULL not needed here */
1910 DUK_LOCAL duk_uint_t duk__debug_getinfo_heaphdr_masks
[] = {
1911 DUK_HEAPHDR_FLAG_REACHABLE
,
1912 DUK_HEAPHDR_FLAG_TEMPROOT
,
1913 DUK_HEAPHDR_FLAG_FINALIZABLE
,
1914 DUK_HEAPHDR_FLAG_FINALIZED
,
1915 DUK_HEAPHDR_FLAG_READONLY
,
1919 DUK_LOCAL
const char * const duk__debug_getinfo_hstring_keys
[] = {
1924 "strict_reserved_word",
1925 "eval_or_arguments",
1928 /* NULL not needed here */
1930 DUK_LOCAL duk_uint_t duk__debug_getinfo_hstring_masks
[] = {
1932 DUK_HSTRING_FLAG_ARRIDX
,
1933 DUK_HSTRING_FLAG_INTERNAL
,
1934 DUK_HSTRING_FLAG_RESERVED_WORD
,
1935 DUK_HSTRING_FLAG_STRICT_RESERVED_WORD
,
1936 DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS
,
1938 DUK_HSTRING_FLAG_EXTDATA
,
1941 DUK_LOCAL
const char * const duk__debug_getinfo_hobject_keys
[] = {
1961 /* NULL not needed here */
1963 DUK_LOCAL duk_uint_t duk__debug_getinfo_hobject_masks
[] = {
1964 DUK_HOBJECT_FLAG_EXTENSIBLE
,
1965 DUK_HOBJECT_FLAG_CONSTRUCTABLE
,
1966 DUK_HOBJECT_FLAG_BOUND
,
1967 DUK_HOBJECT_FLAG_COMPILEDFUNCTION
,
1968 DUK_HOBJECT_FLAG_NATIVEFUNCTION
,
1969 DUK_HOBJECT_FLAG_BUFFEROBJECT
,
1970 DUK_HOBJECT_FLAG_THREAD
,
1971 DUK_HOBJECT_FLAG_ARRAY_PART
,
1972 DUK_HOBJECT_FLAG_STRICT
,
1973 DUK_HOBJECT_FLAG_NOTAIL
,
1974 DUK_HOBJECT_FLAG_NEWENV
,
1975 DUK_HOBJECT_FLAG_NAMEBINDING
,
1976 DUK_HOBJECT_FLAG_CREATEARGS
,
1977 DUK_HOBJECT_FLAG_ENVRECCLOSED
,
1978 DUK_HOBJECT_FLAG_EXOTIC_ARRAY
,
1979 DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ
,
1980 DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS
,
1981 DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC
,
1982 DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ
,
1985 DUK_LOCAL
const char * const duk__debug_getinfo_hbuffer_keys
[] = {
1988 /* NULL not needed here */
1990 DUK_LOCAL duk_uint_t duk__debug_getinfo_hbuffer_masks
[] = {
1991 DUK_HBUFFER_FLAG_DYNAMIC
,
1992 DUK_HBUFFER_FLAG_EXTERNAL
,
1996 DUK_LOCAL
void duk__debug_getinfo_flags_key(duk_hthread
*thr
, const char *key
) {
1997 duk_debug_write_uint(thr
, 0);
1998 duk_debug_write_cstring(thr
, key
);
2001 DUK_LOCAL
void duk__debug_getinfo_prop_uint(duk_hthread
*thr
, const char *key
, duk_uint_t val
) {
2002 duk_debug_write_uint(thr
, 0);
2003 duk_debug_write_cstring(thr
, key
);
2004 duk_debug_write_uint(thr
, val
);
2007 DUK_LOCAL
void duk__debug_getinfo_prop_int(duk_hthread
*thr
, const char *key
, duk_int_t val
) {
2008 duk_debug_write_uint(thr
, 0);
2009 duk_debug_write_cstring(thr
, key
);
2010 duk_debug_write_int(thr
, val
);
2013 DUK_LOCAL
void duk__debug_getinfo_prop_bool(duk_hthread
*thr
, const char *key
, duk_bool_t val
) {
2014 duk_debug_write_uint(thr
, 0);
2015 duk_debug_write_cstring(thr
, key
);
2016 duk_debug_write_boolean(thr
, val
);
2019 DUK_LOCAL
void duk__debug_getinfo_bitmask(duk_hthread
*thr
, const char * const * keys
, duk_uint_t
*masks
, duk_uint_t flags
) {
2029 DUK_ASSERT(key
!= NULL
);
2031 DUK_DD(DUK_DDPRINT("inspect bitmask: key=%s, mask=0x%08lx, flags=0x%08lx", key
, (unsigned long) mask
, (unsigned long) flags
));
2032 duk__debug_getinfo_prop_bool(thr
, key
, flags
& mask
);
2036 /* Inspect a property using a virtual index into a conceptual property list
2037 * consisting of (1) all array part items from [0,a_size[ (even when above
2038 * .length) and (2) all entry part items from [0,e_next[. Unused slots are
2039 * indicated using dvalue 'unused'.
2041 DUK_LOCAL duk_bool_t
duk__debug_getprop_index(duk_hthread
*thr
, duk_heap
*heap
, duk_hobject
*h_obj
, duk_uint_t idx
) {
2045 duk_hobject
*h_getset
;
2050 a_size
= DUK_HOBJECT_GET_ASIZE(h_obj
);
2052 duk_debug_write_uint(thr
, DUK_PROPDESC_FLAGS_WEC
);
2053 duk_debug_write_uint(thr
, idx
);
2054 tv
= DUK_HOBJECT_A_GET_VALUE_PTR(heap
, h_obj
, idx
);
2055 duk_debug_write_tval(thr
, tv
);
2060 if (idx
>= DUK_HOBJECT_GET_ENEXT(h_obj
)) {
2064 h_key
= DUK_HOBJECT_E_GET_KEY(heap
, h_obj
, idx
);
2065 if (h_key
== NULL
) {
2066 duk_debug_write_uint(thr
, 0);
2067 duk_debug_write_null(thr
);
2068 duk_debug_write_unused(thr
);
2072 flags
= DUK_HOBJECT_E_GET_FLAGS(heap
, h_obj
, idx
);
2073 if (DUK_HSTRING_HAS_INTERNAL(h_key
)) {
2074 flags
|= DUK_DBG_PROPFLAG_INTERNAL
;
2076 duk_debug_write_uint(thr
, flags
);
2077 duk_debug_write_hstring(thr
, h_key
);
2078 if (flags
& DUK_PROPDESC_FLAG_ACCESSOR
) {
2079 h_getset
= DUK_HOBJECT_E_GET_VALUE_GETTER(heap
, h_obj
, idx
);
2081 duk_debug_write_hobject(thr
, h_getset
);
2083 duk_debug_write_null(thr
);
2085 h_getset
= DUK_HOBJECT_E_GET_VALUE_SETTER(heap
, h_obj
, idx
);
2087 duk_debug_write_hobject(thr
, h_getset
);
2089 duk_debug_write_null(thr
);
2092 tv
= DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap
, h_obj
, idx
);
2093 duk_debug_write_tval(thr
, tv
);
2098 DUK_LOCAL
void duk__debug_handle_get_heap_obj_info(duk_hthread
*thr
, duk_heap
*heap
) {
2101 DUK_D(DUK_DPRINT("debug command GetHeapObjInfo"));
2104 h
= duk_debug_read_any_ptr(thr
);
2106 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNKNOWN
, "invalid target");
2110 duk_debug_write_reply(thr
);
2112 /* As with all inspection code, we rely on the debug client providing
2113 * a valid, non-stale pointer: there's no portable way to safely
2114 * validate the pointer here.
2117 duk__debug_getinfo_flags_key(thr
, "heapptr");
2118 duk_debug_write_heapptr(thr
, h
);
2120 /* XXX: comes out as signed now */
2121 duk__debug_getinfo_prop_uint(thr
, "heaphdr_flags", (duk_uint_t
) DUK_HEAPHDR_GET_FLAGS(h
));
2122 duk__debug_getinfo_prop_uint(thr
, "heaphdr_type", (duk_uint_t
) DUK_HEAPHDR_GET_TYPE(h
));
2123 #if defined(DUK_USE_REFERENCE_COUNTING)
2124 duk__debug_getinfo_prop_uint(thr
, "refcount", (duk_uint_t
) DUK_HEAPHDR_GET_REFCOUNT(h
));
2127 duk__debug_getinfo_bitmask(thr
,
2128 duk__debug_getinfo_heaphdr_keys
,
2129 duk__debug_getinfo_heaphdr_masks
,
2130 DUK_HEAPHDR_GET_FLAGS_RAW(h
));
2133 switch (DUK_HEAPHDR_GET_TYPE(h
)) {
2134 case DUK_HTYPE_STRING
: {
2137 h_str
= (duk_hstring
*) h
;
2138 duk__debug_getinfo_bitmask(thr
,
2139 duk__debug_getinfo_hstring_keys
,
2140 duk__debug_getinfo_hstring_masks
,
2141 DUK_HEAPHDR_GET_FLAGS_RAW(h
));
2142 duk__debug_getinfo_prop_uint(thr
, "bytelen", DUK_HSTRING_GET_BYTELEN(h_str
));
2143 duk__debug_getinfo_prop_uint(thr
, "charlen", DUK_HSTRING_GET_CHARLEN(h_str
));
2144 duk__debug_getinfo_prop_uint(thr
, "hash", DUK_HSTRING_GET_HASH(h_str
));
2145 duk__debug_getinfo_flags_key(thr
, "data");
2146 duk_debug_write_hstring(thr
, h_str
);
2149 case DUK_HTYPE_OBJECT
: {
2151 duk_hobject
*h_proto
;
2153 h_obj
= (duk_hobject
*) h
;
2154 h_proto
= DUK_HOBJECT_GET_PROTOTYPE(heap
, h_obj
);
2156 /* duk_hobject specific fields. */
2157 duk__debug_getinfo_bitmask(thr
,
2158 duk__debug_getinfo_hobject_keys
,
2159 duk__debug_getinfo_hobject_masks
,
2160 DUK_HEAPHDR_GET_FLAGS_RAW(h
));
2161 duk__debug_getinfo_prop_uint(thr
, "class_number", DUK_HOBJECT_GET_CLASS_NUMBER(h_obj
));
2162 duk__debug_getinfo_flags_key(thr
, "class_name");
2163 duk_debug_write_hstring(thr
, DUK_HOBJECT_GET_CLASS_STRING(heap
, h_obj
));
2164 duk__debug_getinfo_flags_key(thr
, "prototype");
2165 if (h_proto
!= NULL
) {
2166 duk_debug_write_hobject(thr
, h_proto
);
2168 duk_debug_write_null(thr
);
2170 duk__debug_getinfo_flags_key(thr
, "props");
2171 duk_debug_write_pointer(thr
, (void *) DUK_HOBJECT_GET_PROPS(heap
, h_obj
));
2172 duk__debug_getinfo_prop_uint(thr
, "e_size", (duk_uint_t
) DUK_HOBJECT_GET_ESIZE(h_obj
));
2173 duk__debug_getinfo_prop_uint(thr
, "e_next", (duk_uint_t
) DUK_HOBJECT_GET_ENEXT(h_obj
));
2174 duk__debug_getinfo_prop_uint(thr
, "a_size", (duk_uint_t
) DUK_HOBJECT_GET_ASIZE(h_obj
));
2175 duk__debug_getinfo_prop_uint(thr
, "h_size", (duk_uint_t
) DUK_HOBJECT_GET_HSIZE(h_obj
));
2177 /* duk_hnativefunction specific fields. */
2178 if (DUK_HOBJECT_IS_NATIVEFUNCTION(h_obj
)) {
2179 duk_hnativefunction
*h_fun
;
2180 h_fun
= (duk_hnativefunction
*) h_obj
;
2182 duk__debug_getinfo_prop_int(thr
, "nargs", h_fun
->nargs
);
2183 duk__debug_getinfo_prop_int(thr
, "magic", h_fun
->magic
);
2184 duk__debug_getinfo_prop_bool(thr
, "varargs", h_fun
->magic
== DUK_HNATIVEFUNCTION_NARGS_VARARGS
);
2185 /* Native function pointer may be different from a void pointer,
2186 * and we serialize it from memory directly now (no byte swapping etc).
2188 duk__debug_getinfo_flags_key(thr
, "funcptr");
2189 duk_debug_write_buffer(thr
, (const char *) &h_fun
->func
, sizeof(h_fun
->func
));
2192 if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj
)) {
2193 duk_hcompiledfunction
*h_fun
;
2195 h_fun
= (duk_hcompiledfunction
*) h_obj
;
2197 duk__debug_getinfo_prop_int(thr
, "nregs", h_fun
->nregs
);
2198 duk__debug_getinfo_prop_int(thr
, "nargs", h_fun
->nargs
);
2199 duk__debug_getinfo_prop_uint(thr
, "start_line", h_fun
->start_line
);
2200 duk__debug_getinfo_prop_uint(thr
, "end_line", h_fun
->end_line
);
2201 h_buf
= (duk_hbuffer
*) DUK_HCOMPILEDFUNCTION_GET_DATA(thr
->heap
, h_fun
);
2202 if (h_buf
!= NULL
) {
2203 duk__debug_getinfo_flags_key(thr
, "data");
2204 duk_debug_write_heapptr(thr
, (duk_heaphdr
*) h_buf
);
2208 if (DUK_HOBJECT_IS_THREAD(h_obj
)) {
2209 /* XXX: Currently no inspection of threads, e.g. value stack, call
2210 * stack, catch stack, etc.
2213 h_thr
= (duk_hthread
*) h_obj
;
2217 if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj
)) {
2218 duk_hbufferobject
*h_bufobj
;
2219 h_bufobj
= (duk_hbufferobject
*) h_obj
;
2221 duk__debug_getinfo_prop_uint(thr
, "slice_offset", h_bufobj
->offset
);
2222 duk__debug_getinfo_prop_uint(thr
, "slice_length", h_bufobj
->length
);
2223 duk__debug_getinfo_prop_uint(thr
, "elem_shift", (duk_uint_t
) h_bufobj
->shift
);
2224 duk__debug_getinfo_prop_uint(thr
, "elem_type", (duk_uint_t
) h_bufobj
->elem_type
);
2225 duk__debug_getinfo_prop_bool(thr
, "is_view", (duk_uint_t
) h_bufobj
->is_view
);
2226 if (h_bufobj
->buf
!= NULL
) {
2227 duk__debug_getinfo_flags_key(thr
, "buffer");
2228 duk_debug_write_heapptr(thr
, (duk_heaphdr
*) h_bufobj
->buf
);
2233 case DUK_HTYPE_BUFFER
: {
2236 h_buf
= (duk_hbuffer
*) h
;
2237 duk__debug_getinfo_bitmask(thr
,
2238 duk__debug_getinfo_hbuffer_keys
,
2239 duk__debug_getinfo_hbuffer_masks
,
2240 DUK_HEAPHDR_GET_FLAGS_RAW(h
));
2241 duk__debug_getinfo_prop_uint(thr
, "size", (duk_uint_t
) DUK_HBUFFER_GET_SIZE(h_buf
));
2242 duk__debug_getinfo_flags_key(thr
, "dataptr");
2243 duk_debug_write_pointer(thr
, (void *) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h_buf
));
2244 duk__debug_getinfo_flags_key(thr
, "data");
2245 duk_debug_write_hbuffer(thr
, h_buf
); /* tolerates NULL h_buf */
2249 /* Since we already started writing the reply, just emit nothing. */
2250 DUK_D(DUK_DPRINT("inspect target pointer has invalid heaphdr type"));
2254 duk_debug_write_eom(thr
);
2257 DUK_LOCAL
void duk__debug_handle_get_obj_prop_desc(duk_hthread
*thr
, duk_heap
*heap
) {
2263 DUK_D(DUK_DPRINT("debug command GetObjPropDesc"));
2266 h
= duk_debug_read_any_ptr(thr
);
2268 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNKNOWN
, "invalid target");
2271 h_key
= duk_debug_read_hstring(thr
);
2272 if (h
== NULL
|| DUK_HEAPHDR_GET_TYPE(h
) != DUK_HTYPE_OBJECT
|| h_key
== NULL
) {
2275 h_obj
= (duk_hobject
*) h
;
2277 if (duk_hobject_get_own_propdesc(thr
, h_obj
, h_key
, &desc
, 0 /*flags*/)) {
2278 duk_int_t virtual_idx
;
2281 /* To use the shared helper need the virtual index. */
2282 DUK_ASSERT(desc
.e_idx
>= 0 || desc
.a_idx
>= 0);
2283 virtual_idx
= (desc
.a_idx
>= 0 ? desc
.a_idx
:
2284 (duk_int_t
) DUK_HOBJECT_GET_ASIZE(h_obj
) + desc
.e_idx
);
2286 duk_debug_write_reply(thr
);
2287 rc
= duk__debug_getprop_index(thr
, heap
, h_obj
, (duk_uint_t
) virtual_idx
);
2288 DUK_ASSERT(rc
== 1);
2290 duk_debug_write_eom(thr
);
2292 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "not found");
2297 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNKNOWN
, "invalid args");
2300 DUK_LOCAL
void duk__debug_handle_get_obj_prop_desc_range(duk_hthread
*thr
, duk_heap
*heap
) {
2303 duk_uint_t idx
, idx_start
, idx_end
;
2305 DUK_D(DUK_DPRINT("debug command GetObjPropDescRange"));
2308 h
= duk_debug_read_any_ptr(thr
);
2309 idx_start
= duk_debug_read_int(thr
);
2310 idx_end
= duk_debug_read_int(thr
);
2311 if (h
== NULL
|| DUK_HEAPHDR_GET_TYPE(h
) != DUK_HTYPE_OBJECT
) {
2314 h_obj
= (duk_hobject
*) h
;
2316 /* The index range space is conceptually the array part followed by the
2317 * entry part. Unlike normal enumeration all slots are exposed here as
2318 * is and return 'unused' if the slots are not in active use. In particular
2319 * the array part is included for the full a_size regardless of what the
2323 duk_debug_write_reply(thr
);
2324 for (idx
= idx_start
; idx
< idx_end
; idx
++) {
2325 if (!duk__debug_getprop_index(thr
, heap
, h_obj
, idx
)) {
2329 duk_debug_write_eom(thr
);
2333 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNKNOWN
, "invalid args");
2336 #endif /* DUK_USE_DEBUGGER_INSPECT */
2339 * Process incoming debug requests
2341 * Individual request handlers can push temporaries on the value stack and
2342 * rely on duk__debug_process_message() to restore the value stack top
2346 /* Process one debug message. Automatically restore value stack top to its
2347 * entry value, so that individual message handlers don't need exact value
2348 * stack handling which is convenient.
2350 DUK_LOCAL
void duk__debug_process_message(duk_hthread
*thr
) {
2351 duk_context
*ctx
= (duk_context
*) thr
;
2355 duk_idx_t entry_top
;
2357 DUK_ASSERT(thr
!= NULL
);
2359 DUK_ASSERT(heap
!= NULL
);
2362 entry_top
= duk_get_top(ctx
);
2364 x
= duk_debug_read_byte(thr
);
2366 case DUK_DBG_IB_REQUEST
: {
2367 cmd
= duk_debug_read_int(thr
);
2369 case DUK_DBG_CMD_BASICINFO
: {
2370 duk__debug_handle_basic_info(thr
, heap
);
2373 case DUK_DBG_CMD_TRIGGERSTATUS
: {
2374 duk__debug_handle_trigger_status(thr
, heap
);
2377 case DUK_DBG_CMD_PAUSE
: {
2378 duk__debug_handle_pause(thr
, heap
);
2381 case DUK_DBG_CMD_RESUME
: {
2382 duk__debug_handle_resume(thr
, heap
);
2385 case DUK_DBG_CMD_STEPINTO
:
2386 case DUK_DBG_CMD_STEPOVER
:
2387 case DUK_DBG_CMD_STEPOUT
: {
2388 duk__debug_handle_step(thr
, heap
, cmd
);
2391 case DUK_DBG_CMD_LISTBREAK
: {
2392 duk__debug_handle_list_break(thr
, heap
);
2395 case DUK_DBG_CMD_ADDBREAK
: {
2396 duk__debug_handle_add_break(thr
, heap
);
2399 case DUK_DBG_CMD_DELBREAK
: {
2400 duk__debug_handle_del_break(thr
, heap
);
2403 case DUK_DBG_CMD_GETVAR
: {
2404 duk__debug_handle_get_var(thr
, heap
);
2407 case DUK_DBG_CMD_PUTVAR
: {
2408 duk__debug_handle_put_var(thr
, heap
);
2411 case DUK_DBG_CMD_GETCALLSTACK
: {
2412 duk__debug_handle_get_call_stack(thr
, heap
);
2415 case DUK_DBG_CMD_GETLOCALS
: {
2416 duk__debug_handle_get_locals(thr
, heap
);
2419 case DUK_DBG_CMD_EVAL
: {
2420 duk__debug_handle_eval(thr
, heap
);
2423 case DUK_DBG_CMD_DETACH
: {
2424 /* The actual detached_cb call is postponed to message loop so
2425 * we don't need any special precautions here (just skip to EOM
2426 * on the already closed connection).
2428 duk__debug_handle_detach(thr
, heap
);
2431 #if defined(DUK_USE_DEBUGGER_DUMPHEAP)
2432 case DUK_DBG_CMD_DUMPHEAP
: {
2433 duk__debug_handle_dump_heap(thr
, heap
);
2436 #endif /* DUK_USE_DEBUGGER_DUMPHEAP */
2437 case DUK_DBG_CMD_GETBYTECODE
: {
2438 duk__debug_handle_get_bytecode(thr
, heap
);
2441 case DUK_DBG_CMD_APPREQUEST
: {
2442 duk__debug_handle_apprequest(thr
, heap
);
2445 #if defined(DUK_USE_DEBUGGER_INSPECT)
2446 case DUK_DBG_CMD_GETHEAPOBJINFO
: {
2447 duk__debug_handle_get_heap_obj_info(thr
, heap
);
2450 case DUK_DBG_CMD_GETOBJPROPDESC
: {
2451 duk__debug_handle_get_obj_prop_desc(thr
, heap
);
2454 case DUK_DBG_CMD_GETOBJPROPDESCRANGE
: {
2455 duk__debug_handle_get_obj_prop_desc_range(thr
, heap
);
2458 #endif /* DUK_USE_DEBUGGER_INSPECT */
2460 DUK_D(DUK_DPRINT("debug command unsupported: %d", (int) cmd
));
2461 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNSUPPORTED
, "unsupported command");
2466 case DUK_DBG_IB_REPLY
: {
2467 DUK_D(DUK_DPRINT("debug reply, skipping"));
2470 case DUK_DBG_IB_ERROR
: {
2471 DUK_D(DUK_DPRINT("debug error, skipping"));
2474 case DUK_DBG_IB_NOTIFY
: {
2475 DUK_D(DUK_DPRINT("debug notify, skipping"));
2479 DUK_D(DUK_DPRINT("invalid initial byte, drop connection: %d", (int) x
));
2482 } /* switch initial byte */
2484 DUK_ASSERT(duk_get_top(ctx
) >= entry_top
);
2485 duk_set_top(ctx
, entry_top
);
2486 duk__debug_skip_to_eom(thr
);
2490 DUK_ASSERT(duk_get_top(ctx
) >= entry_top
);
2491 duk_set_top(ctx
, entry_top
);
2492 DUK__SET_CONN_BROKEN(thr
, 1);
2496 DUK_LOCAL
void duk__check_resend_status(duk_hthread
*thr
) {
2497 if (thr
->heap
->dbg_read_cb
!= NULL
&& thr
->heap
->dbg_state_dirty
) {
2498 duk_debug_send_status(thr
);
2499 thr
->heap
->dbg_state_dirty
= 0;
2503 DUK_INTERNAL duk_bool_t
duk_debug_process_messages(duk_hthread
*thr
, duk_bool_t no_block
) {
2504 duk_context
*ctx
= (duk_context
*) thr
;
2505 #if defined(DUK_USE_ASSERTIONS)
2506 duk_idx_t entry_top
;
2508 duk_bool_t retval
= 0;
2510 DUK_ASSERT(thr
!= NULL
);
2512 DUK_ASSERT(thr
->heap
!= NULL
);
2513 #if defined(DUK_USE_ASSERTIONS)
2514 entry_top
= duk_get_top(ctx
);
2517 DUK_D(DUK_DPRINT("process debug messages: read_cb=%s, no_block=%ld, detaching=%ld, processing=%ld",
2518 thr
->heap
->dbg_read_cb
? "not NULL" : "NULL", (long) no_block
,
2519 (long) thr
->heap
->dbg_detaching
, (long) thr
->heap
->dbg_processing
));
2520 DUK_DD(DUK_DDPRINT("top at entry: %ld", (long) duk_get_top(ctx
)));
2522 /* thr->heap->dbg_detaching may be != 0 if a debugger write outside
2523 * the message loop caused a transport error and detach1() to run.
2525 DUK_ASSERT(thr
->heap
->dbg_detaching
== 0 || thr
->heap
->dbg_detaching
== 1);
2526 DUK_ASSERT(thr
->heap
->dbg_processing
== 0);
2527 thr
->heap
->dbg_processing
= 1;
2529 /* Ensure dirty state causes a Status even if never process any
2530 * messages. This is expected by the bytecode executor when in
2531 * the running state.
2533 duk__check_resend_status(thr
);
2536 /* Process messages until we're no longer paused or we peek
2537 * and see there's nothing to read right now.
2539 DUK_DD(DUK_DDPRINT("top at loop top: %ld", (long) duk_get_top(ctx
)));
2540 DUK_ASSERT(thr
->heap
->dbg_processing
== 1);
2542 while (thr
->heap
->dbg_read_cb
== NULL
&& thr
->heap
->dbg_detaching
) {
2543 /* Detach is pending; can be triggered from outside the
2544 * debugger loop (e.g. Status notify write error) or by
2545 * previous message handling. Call detached callback
2546 * here, in a controlled state, to ensure a possible
2547 * reattach inside the detached_cb is handled correctly.
2549 * Recheck for detach in a while loop: an immediate
2550 * reattach involves a call to duk_debugger_attach()
2551 * which writes a debugger handshake line immediately
2552 * inside the API call. If the transport write fails
2553 * for that handshake, we can immediately end up in a
2554 * "transport broken, detaching" case several times here.
2555 * Loop back until we're either cleanly attached or
2558 * NOTE: Reset dbg_processing = 1 forcibly, in case we
2559 * re-attached; duk_debugger_attach() sets dbg_processing
2560 * to 0 at the moment.
2563 DUK_D(DUK_DPRINT("detach pending (dbg_read_cb == NULL, dbg_detaching != 0), call detach2"));
2565 duk__debug_do_detach2(thr
->heap
);
2566 thr
->heap
->dbg_processing
= 1; /* may be set to 0 by duk_debugger_attach() inside callback */
2568 DUK_D(DUK_DPRINT("after detach2 (and possible reattach): dbg_read_cb=%s, dbg_detaching=%ld",
2569 thr
->heap
->dbg_read_cb
? "not NULL" : "NULL", (long) thr
->heap
->dbg_detaching
));
2571 DUK_ASSERT(thr
->heap
->dbg_detaching
== 0); /* true even with reattach */
2572 DUK_ASSERT(thr
->heap
->dbg_processing
== 1); /* even after a detach and possible reattach */
2574 if (thr
->heap
->dbg_read_cb
== NULL
) {
2575 DUK_D(DUK_DPRINT("debug connection broken (and not detaching), stop processing messages"));
2579 if (!thr
->heap
->dbg_paused
|| no_block
) {
2580 if (!duk_debug_read_peek(thr
)) {
2581 /* Note: peek cannot currently trigger a detach
2582 * so the dbg_detaching == 0 assert outside the
2585 DUK_D(DUK_DPRINT("processing debug message, peek indicated no data, stop processing messages"));
2588 DUK_D(DUK_DPRINT("processing debug message, peek indicated there is data, handle it"));
2590 DUK_D(DUK_DPRINT("paused, process debug message, blocking if necessary"));
2593 duk__check_resend_status(thr
);
2594 duk__debug_process_message(thr
);
2595 duk__check_resend_status(thr
);
2597 retval
= 1; /* processed one or more messages */
2600 DUK_ASSERT(thr
->heap
->dbg_detaching
== 0);
2601 DUK_ASSERT(thr
->heap
->dbg_processing
== 1);
2602 thr
->heap
->dbg_processing
= 0;
2604 /* As an initial implementation, read flush after exiting the message
2605 * loop. If transport is broken, this is a no-op (with debug logs).
2607 duk_debug_read_flush(thr
); /* this cannot initiate a detach */
2608 DUK_ASSERT(thr
->heap
->dbg_detaching
== 0);
2610 DUK_DD(DUK_DDPRINT("top at exit: %ld", (long) duk_get_top(ctx
)));
2612 #if defined(DUK_USE_ASSERTIONS)
2613 /* Easy to get wrong, so assert for it. */
2614 DUK_ASSERT(entry_top
== duk_get_top(ctx
));
2621 * Halt execution helper
2624 /* Halt execution and enter a debugger message loop until execution is resumed
2625 * by the client. PC for the current activation may be temporarily decremented
2626 * so that the "current" instruction will be shown by the client. This helper
2627 * is callable from anywhere, also outside bytecode executor.
2630 DUK_INTERNAL
void duk_debug_halt_execution(duk_hthread
*thr
, duk_bool_t use_prev_pc
) {
2631 duk_activation
*act
;
2632 duk_hcompiledfunction
*fun
;
2633 duk_instr_t
*old_pc
= NULL
;
2635 DUK_ASSERT(thr
!= NULL
);
2636 DUK_ASSERT(thr
->heap
!= NULL
);
2637 DUK_ASSERT(DUK_HEAP_IS_DEBUGGER_ATTACHED(thr
->heap
));
2638 DUK_ASSERT(thr
->heap
->dbg_processing
== 0);
2640 DUK_HEAP_SET_PAUSED(thr
->heap
);
2642 act
= duk_hthread_get_current_activation(thr
);
2644 /* NOTE: act may be NULL if an error is thrown outside of any activation,
2645 * which may happen in the case of, e.g. syntax errors.
2648 /* Decrement PC if that was requested, this requires a PC sync. */
2650 duk_hthread_sync_currpc(thr
);
2651 old_pc
= act
->curr_pc
;
2652 fun
= (duk_hcompiledfunction
*) DUK_ACT_GET_FUNC(act
);
2654 /* Short circuit if is safe: if act->curr_pc != NULL, 'fun' is
2655 * guaranteed to be a non-NULL Ecmascript function.
2657 DUK_ASSERT(act
->curr_pc
== NULL
||
2658 (fun
!= NULL
&& DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject
*) fun
)));
2660 act
->curr_pc
!= NULL
&&
2661 act
->curr_pc
> DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr
->heap
, fun
)) {
2666 /* Process debug messages until we are no longer paused. */
2668 /* NOTE: This is a bit fragile. It's important to ensure that
2669 * duk_debug_process_messages() never throws an error or
2670 * act->curr_pc will never be reset.
2673 thr
->heap
->dbg_state_dirty
= 1;
2674 while (thr
->heap
->dbg_paused
) {
2675 DUK_ASSERT(DUK_HEAP_IS_DEBUGGER_ATTACHED(thr
->heap
));
2676 DUK_ASSERT(thr
->heap
->dbg_processing
);
2677 duk_debug_process_messages(thr
, 0 /*no_block*/);
2680 /* XXX: Decrementing and restoring act->curr_pc works now, but if the
2681 * debugger message loop gains the ability to adjust the current PC
2682 * (e.g. a forced jump) restoring the PC here will break. Another
2683 * approach would be to use a state flag for the "decrement 1 from
2684 * topmost activation's PC" and take it into account whenever dealing
2688 act
->curr_pc
= old_pc
; /* restore PC */
2693 * Breakpoint management
2696 DUK_INTERNAL duk_small_int_t
duk_debug_add_breakpoint(duk_hthread
*thr
, duk_hstring
*filename
, duk_uint32_t line
) {
2700 /* Caller must trigger recomputation of active breakpoint list. To
2701 * ensure stale values are not used if that doesn't happen, clear the
2702 * active breakpoint list here.
2705 DUK_ASSERT(thr
!= NULL
);
2706 DUK_ASSERT(filename
!= NULL
);
2708 DUK_ASSERT(heap
!= NULL
);
2710 if (heap
->dbg_breakpoint_count
>= DUK_HEAP_MAX_BREAKPOINTS
) {
2711 DUK_D(DUK_DPRINT("failed to add breakpoint for %O:%ld, all breakpoint slots used",
2712 (duk_heaphdr
*) filename
, (long) line
));
2715 heap
->dbg_breakpoints_active
[0] = (duk_breakpoint
*) NULL
;
2716 b
= heap
->dbg_breakpoints
+ (heap
->dbg_breakpoint_count
++);
2717 b
->filename
= filename
;
2719 DUK_HSTRING_INCREF(thr
, filename
);
2721 return heap
->dbg_breakpoint_count
- 1; /* index */
2724 DUK_INTERNAL duk_bool_t
duk_debug_remove_breakpoint(duk_hthread
*thr
, duk_small_uint_t breakpoint_index
) {
2728 duk_size_t move_size
;
2730 /* Caller must trigger recomputation of active breakpoint list. To
2731 * ensure stale values are not used if that doesn't happen, clear the
2732 * active breakpoint list here.
2735 DUK_ASSERT(thr
!= NULL
);
2737 DUK_ASSERT(heap
!= NULL
);
2738 DUK_ASSERT(DUK_HEAP_IS_DEBUGGER_ATTACHED(thr
->heap
));
2739 DUK_ASSERT_DISABLE(breakpoint_index
>= 0); /* unsigned */
2741 if (breakpoint_index
>= heap
->dbg_breakpoint_count
) {
2742 DUK_D(DUK_DPRINT("invalid breakpoint index: %ld", (long) breakpoint_index
));
2745 b
= heap
->dbg_breakpoints
+ breakpoint_index
;
2748 DUK_ASSERT(h
!= NULL
);
2750 move_size
= sizeof(duk_breakpoint
) * (heap
->dbg_breakpoint_count
- breakpoint_index
- 1);
2751 if (move_size
> 0) {
2752 DUK_MEMMOVE((void *) b
,
2753 (const void *) (b
+ 1),
2754 (size_t) move_size
);
2756 heap
->dbg_breakpoint_count
--;
2757 heap
->dbg_breakpoints_active
[0] = (duk_breakpoint
*) NULL
;
2759 DUK_HSTRING_DECREF(thr
, h
); /* side effects */
2760 DUK_UNREF(h
); /* w/o refcounting */
2762 /* Breakpoint entries above the used area are left as garbage. */
2767 #undef DUK__SET_CONN_BROKEN
2769 #else /* DUK_USE_DEBUGGER_SUPPORT */
2771 /* No debugger support. */
2773 #endif /* DUK_USE_DEBUGGER_SUPPORT */