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 act
= thr
->callstack
+ thr
->callstack_top
- 1;
1017 duk_debug_write_uint(thr
, (duk_uint32_t
) duk_hthread_get_act_curr_pc(thr
, act
));
1020 duk_debug_write_eom(thr
);
1023 #if defined(DUK_USE_DEBUGGER_THROW_NOTIFY)
1024 DUK_INTERNAL
void duk_debug_send_throw(duk_hthread
*thr
, duk_bool_t fatal
) {
1026 * NFY <int: 5> <int: fatal> <str: msg> <str: filename> <int: linenumber> EOM
1029 duk_context
*ctx
= (duk_context
*) thr
;
1030 duk_activation
*act
;
1033 DUK_ASSERT(thr
->valstack_top
> thr
->valstack
); /* At least: ... [err] */
1035 duk_debug_write_notify(thr
, DUK_DBG_CMD_THROW
);
1036 duk_debug_write_int(thr
, fatal
);
1038 /* Report thrown value to client coerced to string */
1040 duk__debug_write_hstring_safe_top(thr
);
1043 if (duk_is_error(ctx
, -1)) {
1044 /* Error instance, use augmented error data directly */
1045 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_FILE_NAME
);
1046 duk__debug_write_hstring_safe_top(thr
);
1047 duk_get_prop_stridx(ctx
, -2, DUK_STRIDX_LINE_NUMBER
);
1048 duk_debug_write_uint(thr
, duk_get_uint(ctx
, -1));
1051 /* For anything other than an Error instance, we calculate the
1052 * error location directly from the current activation if one
1055 if (thr
->callstack_top
> 0) {
1056 act
= thr
->callstack
+ thr
->callstack_top
- 1;
1057 duk_push_tval(ctx
, &act
->tv_func
);
1058 duk_get_prop_string(ctx
, -1, "fileName");
1059 duk__debug_write_hstring_safe_top(thr
);
1060 act
= thr
->callstack
+ thr
->callstack_top
- 1;
1061 pc
= duk_hthread_get_act_prev_pc(thr
, act
);
1062 duk_debug_write_uint(thr
, (duk_uint32_t
) duk_hobject_pc2line_query(ctx
, -2, pc
));
1065 /* Can happen if duk_throw() is called on an empty
1068 duk_debug_write_cstring(thr
, "");
1069 duk_debug_write_uint(thr
, 0);
1073 duk_debug_write_eom(thr
);
1075 #endif /* DUK_USE_DEBUGGER_THROW_NOTIFY */
1078 * Debug message processing
1082 DUK_LOCAL duk_bool_t
duk__debug_skip_dvalue(duk_hthread
*thr
) {
1086 x
= duk_debug_read_byte(thr
);
1089 duk_debug_skip_byte(thr
);
1096 duk_debug_skip_bytes(thr
, x
- 0x60);
1100 case DUK_DBG_IB_EOM
:
1101 return 1; /* Return 1: got EOM */
1102 case DUK_DBG_IB_REQUEST
:
1103 case DUK_DBG_IB_REPLY
:
1104 case DUK_DBG_IB_ERROR
:
1105 case DUK_DBG_IB_NOTIFY
:
1107 case DUK_DBG_IB_INT4
:
1108 (void) duk__debug_read_uint32_raw(thr
);
1110 case DUK_DBG_IB_STR4
:
1111 case DUK_DBG_IB_BUF4
:
1112 len
= duk__debug_read_uint32_raw(thr
);
1113 duk_debug_skip_bytes(thr
, len
);
1115 case DUK_DBG_IB_STR2
:
1116 case DUK_DBG_IB_BUF2
:
1117 len
= duk__debug_read_uint16_raw(thr
);
1118 duk_debug_skip_bytes(thr
, len
);
1120 case DUK_DBG_IB_UNUSED
:
1121 case DUK_DBG_IB_UNDEFINED
:
1122 case DUK_DBG_IB_NULL
:
1123 case DUK_DBG_IB_TRUE
:
1124 case DUK_DBG_IB_FALSE
:
1126 case DUK_DBG_IB_NUMBER
:
1127 duk_debug_skip_bytes(thr
, 8);
1129 case DUK_DBG_IB_OBJECT
:
1130 duk_debug_skip_byte(thr
);
1131 len
= duk_debug_read_byte(thr
);
1132 duk_debug_skip_bytes(thr
, len
);
1134 case DUK_DBG_IB_POINTER
:
1135 case DUK_DBG_IB_HEAPPTR
:
1136 len
= duk_debug_read_byte(thr
);
1137 duk_debug_skip_bytes(thr
, len
);
1139 case DUK_DBG_IB_LIGHTFUNC
:
1140 duk_debug_skip_bytes(thr
, 2);
1141 len
= duk_debug_read_byte(thr
);
1142 duk_debug_skip_bytes(thr
, len
);
1151 DUK__SET_CONN_BROKEN(thr
, 1);
1152 return 1; /* Pretend like we got EOM */
1155 /* Skip dvalues to EOM. */
1156 DUK_LOCAL
void duk__debug_skip_to_eom(duk_hthread
*thr
) {
1158 if (duk__debug_skip_dvalue(thr
)) {
1168 DUK_LOCAL
void duk__debug_handle_basic_info(duk_hthread
*thr
, duk_heap
*heap
) {
1170 DUK_D(DUK_DPRINT("debug command Version"));
1172 duk_debug_write_reply(thr
);
1173 duk_debug_write_int(thr
, DUK_VERSION
);
1174 duk_debug_write_cstring(thr
, DUK_GIT_DESCRIBE
);
1175 duk_debug_write_cstring(thr
, DUK_USE_TARGET_INFO
);
1176 #if defined(DUK_USE_DOUBLE_LE)
1177 duk_debug_write_int(thr
, 1);
1178 #elif defined(DUK_USE_DOUBLE_ME)
1179 duk_debug_write_int(thr
, 2);
1180 #elif defined(DUK_USE_DOUBLE_BE)
1181 duk_debug_write_int(thr
, 3);
1183 duk_debug_write_int(thr
, 0);
1185 duk_debug_write_int(thr
, (duk_int_t
) sizeof(void *));
1186 duk_debug_write_eom(thr
);
1189 DUK_LOCAL
void duk__debug_handle_trigger_status(duk_hthread
*thr
, duk_heap
*heap
) {
1191 DUK_D(DUK_DPRINT("debug command TriggerStatus"));
1193 duk_debug_write_reply(thr
);
1194 duk_debug_write_eom(thr
);
1195 heap
->dbg_state_dirty
= 1;
1198 DUK_LOCAL
void duk__debug_handle_pause(duk_hthread
*thr
, duk_heap
*heap
) {
1199 DUK_D(DUK_DPRINT("debug command Pause"));
1201 DUK_HEAP_SET_PAUSED(heap
);
1202 duk_debug_write_reply(thr
);
1203 duk_debug_write_eom(thr
);
1206 DUK_LOCAL
void duk__debug_handle_resume(duk_hthread
*thr
, duk_heap
*heap
) {
1207 DUK_D(DUK_DPRINT("debug command Resume"));
1209 DUK_HEAP_CLEAR_PAUSED(heap
);
1210 duk_debug_write_reply(thr
);
1211 duk_debug_write_eom(thr
);
1214 DUK_LOCAL
void duk__debug_handle_step(duk_hthread
*thr
, duk_heap
*heap
, duk_int32_t cmd
) {
1215 duk_small_uint_t step_type
;
1216 duk_uint_fast32_t line
;
1218 DUK_D(DUK_DPRINT("debug command StepInto/StepOver/StepOut: %d", (int) cmd
));
1220 if (cmd
== DUK_DBG_CMD_STEPINTO
) {
1221 step_type
= DUK_STEP_TYPE_INTO
;
1222 } else if (cmd
== DUK_DBG_CMD_STEPOVER
) {
1223 step_type
= DUK_STEP_TYPE_OVER
;
1225 DUK_ASSERT(cmd
== DUK_DBG_CMD_STEPOUT
);
1226 step_type
= DUK_STEP_TYPE_OUT
;
1229 line
= duk_debug_curr_line(thr
);
1231 heap
->dbg_paused
= 0;
1232 heap
->dbg_step_type
= step_type
;
1233 heap
->dbg_step_thread
= thr
;
1234 heap
->dbg_step_csindex
= thr
->callstack_top
- 1;
1235 heap
->dbg_step_startline
= line
;
1236 heap
->dbg_state_dirty
= 1;
1238 DUK_D(DUK_DPRINT("cannot determine current line, stepinto/stepover/stepout ignored"));
1240 duk_debug_write_reply(thr
);
1241 duk_debug_write_eom(thr
);
1244 DUK_LOCAL
void duk__debug_handle_list_break(duk_hthread
*thr
, duk_heap
*heap
) {
1247 DUK_D(DUK_DPRINT("debug command ListBreak"));
1248 duk_debug_write_reply(thr
);
1249 for (i
= 0; i
< (duk_small_int_t
) heap
->dbg_breakpoint_count
; i
++) {
1250 duk_debug_write_hstring(thr
, heap
->dbg_breakpoints
[i
].filename
);
1251 duk_debug_write_uint(thr
, (duk_uint32_t
) heap
->dbg_breakpoints
[i
].line
);
1253 duk_debug_write_eom(thr
);
1256 DUK_LOCAL
void duk__debug_handle_add_break(duk_hthread
*thr
, duk_heap
*heap
) {
1257 duk_hstring
*filename
;
1258 duk_uint32_t linenumber
;
1259 duk_small_int_t idx
;
1263 filename
= duk_debug_read_hstring(thr
);
1264 linenumber
= (duk_uint32_t
) duk_debug_read_int(thr
);
1265 DUK_D(DUK_DPRINT("debug command AddBreak: %!O:%ld", (duk_hobject
*) filename
, (long) linenumber
));
1266 idx
= duk_debug_add_breakpoint(thr
, filename
, linenumber
);
1268 duk_debug_write_reply(thr
);
1269 duk_debug_write_int(thr
, (duk_int32_t
) idx
);
1270 duk_debug_write_eom(thr
);
1272 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_TOOMANY
, "no space for breakpoint");
1276 DUK_LOCAL
void duk__debug_handle_del_break(duk_hthread
*thr
, duk_heap
*heap
) {
1277 duk_small_uint_t idx
;
1281 DUK_D(DUK_DPRINT("debug command DelBreak"));
1282 idx
= (duk_small_uint_t
) duk_debug_read_int(thr
);
1283 if (duk_debug_remove_breakpoint(thr
, idx
)) {
1284 duk_debug_write_reply(thr
);
1285 duk_debug_write_eom(thr
);
1287 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "invalid breakpoint index");
1291 DUK_LOCAL
void duk__debug_handle_get_var(duk_hthread
*thr
, duk_heap
*heap
) {
1292 duk_context
*ctx
= (duk_context
*) thr
;
1298 DUK_D(DUK_DPRINT("debug command GetVar"));
1300 str
= duk_debug_read_hstring(thr
); /* push to stack */
1301 DUK_ASSERT(str
!= NULL
);
1302 if (duk_debug_peek_byte(thr
) != DUK_DBG_IB_EOM
) {
1303 level
= duk_debug_read_int(thr
); /* optional callstack level */
1304 if (level
>= 0 || -level
> (duk_int32_t
) thr
->callstack_top
) {
1305 DUK_D(DUK_DPRINT("invalid callstack level for GetVar"));
1306 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "invalid callstack level");
1313 if (thr
->callstack_top
> 0) {
1314 rc
= duk_js_getvar_activation(thr
,
1315 thr
->callstack
+ thr
->callstack_top
+ level
,
1319 /* No activation, no variable access. Could also pretend
1320 * we're in the global program context and read stuff off
1321 * the global object.
1323 DUK_D(DUK_DPRINT("callstack empty, no activation -> ignore getvar"));
1327 duk_debug_write_reply(thr
);
1329 duk_debug_write_int(thr
, 1);
1330 DUK_ASSERT(duk_get_tval(ctx
, -2) != NULL
);
1331 duk_debug_write_tval(thr
, duk_get_tval(ctx
, -2));
1333 duk_debug_write_int(thr
, 0);
1334 duk_debug_write_unused(thr
);
1336 duk_debug_write_eom(thr
);
1339 DUK_LOCAL
void duk__debug_handle_put_var(duk_hthread
*thr
, duk_heap
*heap
) {
1345 DUK_D(DUK_DPRINT("debug command PutVar"));
1347 str
= duk_debug_read_hstring(thr
); /* push to stack */
1348 DUK_ASSERT(str
!= NULL
);
1349 tv
= duk_debug_read_tval(thr
);
1354 if (duk_debug_peek_byte(thr
) != DUK_DBG_IB_EOM
) {
1355 level
= duk_debug_read_int(thr
); /* optional callstack level */
1356 if (level
>= 0 || -level
> (duk_int32_t
) thr
->callstack_top
) {
1357 DUK_D(DUK_DPRINT("invalid callstack level for PutVar"));
1358 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "invalid callstack level");
1365 if (thr
->callstack_top
> 0) {
1366 duk_js_putvar_activation(thr
,
1367 thr
->callstack
+ thr
->callstack_top
+ level
,
1372 DUK_D(DUK_DPRINT("callstack empty, no activation -> ignore putvar"));
1375 /* XXX: Current putvar implementation doesn't have a success flag,
1376 * add one and send to debug client?
1378 duk_debug_write_reply(thr
);
1379 duk_debug_write_eom(thr
);
1382 DUK_LOCAL
void duk__debug_handle_get_call_stack(duk_hthread
*thr
, duk_heap
*heap
) {
1383 duk_context
*ctx
= (duk_context
*) thr
;
1384 duk_hthread
*curr_thr
= thr
;
1385 duk_activation
*curr_act
;
1386 duk_uint_fast32_t pc
;
1387 duk_uint_fast32_t line
;
1390 DUK_ASSERT(thr
!= NULL
);
1393 duk_debug_write_reply(thr
);
1394 while (curr_thr
!= NULL
) {
1395 i
= curr_thr
->callstack_top
;
1398 curr_act
= curr_thr
->callstack
+ i
;
1400 /* PC/line semantics here are:
1401 * - For callstack top we're conceptually between two
1402 * opcodes and current PC indicates next line to
1403 * execute, so report that (matches Status).
1404 * - For other activations we're conceptually still
1405 * executing the instruction at PC-1, so report that
1406 * (matches error stacktrace behavior).
1407 * - See: https://github.com/svaarala/duktape/issues/281
1410 /* XXX: optimize to use direct reads, i.e. avoid
1411 * value stack operations.
1413 duk_push_tval(ctx
, &curr_act
->tv_func
);
1414 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_FILE_NAME
);
1415 duk__debug_write_hstring_safe_top(thr
);
1416 duk_get_prop_stridx(ctx
, -2, DUK_STRIDX_NAME
);
1417 duk__debug_write_hstring_safe_top(thr
);
1418 pc
= duk_hthread_get_act_curr_pc(thr
, curr_act
);
1419 if (i
!= curr_thr
->callstack_top
- 1 && pc
> 0) {
1422 line
= duk_hobject_pc2line_query(ctx
, -3, pc
);
1423 duk_debug_write_uint(thr
, (duk_uint32_t
) line
);
1424 duk_debug_write_uint(thr
, (duk_uint32_t
) pc
);
1427 curr_thr
= curr_thr
->resumer
;
1429 /* SCANBUILD: warning about 'thr' potentially being NULL here,
1430 * warning is incorrect because thr != NULL always here.
1432 duk_debug_write_eom(thr
);
1435 DUK_LOCAL
void duk__debug_handle_get_locals(duk_hthread
*thr
, duk_heap
*heap
) {
1436 duk_context
*ctx
= (duk_context
*) thr
;
1437 duk_activation
*curr_act
;
1439 duk_hstring
*varname
;
1443 if (duk_debug_peek_byte(thr
) != DUK_DBG_IB_EOM
) {
1444 level
= duk_debug_read_int(thr
); /* optional callstack level */
1445 if (level
>= 0 || -level
> (duk_int32_t
) thr
->callstack_top
) {
1446 DUK_D(DUK_DPRINT("invalid callstack level for GetLocals"));
1447 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "invalid callstack level");
1450 duk_debug_write_reply(thr
);
1452 duk_debug_write_reply(thr
);
1453 if (thr
->callstack_top
== 0) {
1454 goto callstack_empty
;
1459 curr_act
= thr
->callstack
+ thr
->callstack_top
+ level
;
1461 /* XXX: several nice-to-have improvements here:
1462 * - Use direct reads avoiding value stack operations
1463 * - Avoid triggering getters, indicate getter values to debug client
1464 * - If side effects are possible, add error catching
1467 duk_push_tval(ctx
, &curr_act
->tv_func
);
1468 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_INT_VARMAP
);
1469 if (duk_is_object(ctx
, -1)) {
1470 duk_enum(ctx
, -1, 0 /*enum_flags*/);
1471 while (duk_next(ctx
, -1 /*enum_index*/, 0 /*get_value*/)) {
1472 varname
= duk_get_hstring(ctx
, -1);
1473 DUK_ASSERT(varname
!= NULL
);
1475 duk_js_getvar_activation(thr
, curr_act
, varname
, 0 /*throw_flag*/);
1476 /* [ ... func varmap enum key value this ] */
1477 duk_debug_write_hstring(thr
, duk_get_hstring(ctx
, -3));
1478 duk_debug_write_tval(thr
, duk_get_tval(ctx
, -2));
1479 duk_pop_3(ctx
); /* -> [ ... func varmap enum ] */
1482 DUK_D(DUK_DPRINT("varmap is not an object in GetLocals, ignore"));
1486 duk_debug_write_eom(thr
);
1489 DUK_LOCAL
void duk__debug_handle_eval(duk_hthread
*thr
, duk_heap
*heap
) {
1490 duk_context
*ctx
= (duk_context
*) thr
;
1491 duk_small_uint_t call_flags
;
1493 duk_small_int_t eval_err
;
1498 DUK_D(DUK_DPRINT("debug command Eval"));
1500 /* The eval code is executed within the lexical environment of a specified
1501 * activation. For now, use global object eval() function, with the eval
1502 * considered a 'direct call to eval'.
1504 * Callstack level for debug commands only affects scope -- the callstack
1505 * as seen by, e.g. Duktape.act() will be the same regardless.
1508 /* nargs == 2 so we can pass a callstack level to eval(). */
1509 duk_push_c_function(ctx
, duk_bi_global_object_eval
, 2 /*nargs*/);
1510 duk_push_undefined(ctx
); /* 'this' binding shouldn't matter here */
1512 (void) duk_debug_read_hstring(thr
);
1513 if (duk_debug_peek_byte(thr
) != DUK_DBG_IB_EOM
) {
1514 level
= duk_debug_read_int(thr
); /* optional callstack level */
1515 if (level
>= 0 || -level
> (duk_int32_t
) thr
->callstack_top
) {
1516 DUK_D(DUK_DPRINT("invalid callstack level for Eval"));
1517 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "invalid callstack level");
1524 DUK_ASSERT(level
< 0 && -level
<= (duk_int32_t
) thr
->callstack_top
);
1525 duk_push_int(ctx
, level
- 1); /* compensate for eval() call */
1527 /* [ ... eval "eval" eval_input level ] */
1530 if (thr
->callstack_top
>= (duk_size_t
) -level
) {
1531 duk_activation
*act
;
1534 act
= thr
->callstack
+ thr
->callstack_top
+ level
;
1535 fun
= DUK_ACT_GET_FUNC(act
);
1536 if (fun
!= NULL
&& DUK_HOBJECT_IS_COMPILEDFUNCTION(fun
)) {
1537 /* Direct eval requires that there's a current
1538 * activation and it is an Ecmascript function.
1539 * When Eval is executed from e.g. cooperate API
1540 * call we'll need to do an indirect eval instead.
1542 call_flags
|= DUK_CALL_FLAG_DIRECT_EVAL
;
1546 call_ret
= duk_handle_call_protected(thr
, 2 /*num_stack_args*/, call_flags
);
1548 if (call_ret
== DUK_EXEC_SUCCESS
) {
1550 /* Use result value as is. */
1552 /* For errors a string coerced result is most informative
1553 * right now, as the debug client doesn't have the capability
1554 * to traverse the error object.
1557 duk_safe_to_string(ctx
, -1);
1560 /* [ ... result ] */
1562 duk_debug_write_reply(thr
);
1563 duk_debug_write_int(thr
, (duk_int32_t
) eval_err
);
1564 DUK_ASSERT(duk_get_tval(ctx
, -1) != NULL
);
1565 duk_debug_write_tval(thr
, duk_get_tval(ctx
, -1));
1566 duk_debug_write_eom(thr
);
1569 DUK_LOCAL
void duk__debug_handle_detach(duk_hthread
*thr
, duk_heap
*heap
) {
1571 DUK_D(DUK_DPRINT("debug command Detach"));
1573 duk_debug_write_reply(thr
);
1574 duk_debug_write_eom(thr
);
1576 DUK_D(DUK_DPRINT("debug connection detached, mark broken"));
1577 DUK__SET_CONN_BROKEN(thr
, 0); /* not an error */
1580 DUK_LOCAL
void duk__debug_handle_apprequest(duk_hthread
*thr
, duk_heap
*heap
) {
1581 duk_context
*ctx
= (duk_context
*) thr
;
1584 DUK_D(DUK_DPRINT("debug command AppRequest"));
1586 old_top
= duk_get_top(ctx
); /* save stack top */
1588 if (heap
->dbg_request_cb
!= NULL
) {
1590 duk_idx_t nvalues
= 0;
1593 /* Read tvals from the message and push them onto the valstack,
1594 * then call the request callback to process the request.
1596 while (duk_debug_peek_byte(thr
) != DUK_DBG_IB_EOM
) {
1598 if (!duk_check_stack(ctx
, 1)) {
1599 DUK_D(DUK_DPRINT("failed to allocate space for request dvalue(s)"));
1602 tv
= duk_debug_read_tval(thr
); /* push to stack */
1609 DUK_ASSERT(duk_get_top(ctx
) == old_top
+ nvalues
);
1611 /* Request callback should push values for reply to client onto valstack */
1612 DUK_D(DUK_DPRINT("calling into AppRequest request_cb with nvalues=%ld, old_top=%ld, top=%ld",
1613 (long) nvalues
, (long) old_top
, (long) duk_get_top(ctx
)));
1614 nrets
= heap
->dbg_request_cb(ctx
, heap
->dbg_udata
, nvalues
);
1615 DUK_D(DUK_DPRINT("returned from AppRequest request_cb; nvalues=%ld -> nrets=%ld, old_top=%ld, top=%ld",
1616 (long) nvalues
, (long) nrets
, (long) old_top
, (long) duk_get_top(ctx
)));
1618 DUK_ASSERT(duk_get_top(ctx
) >= old_top
+ nrets
);
1619 if (duk_get_top(ctx
) < old_top
+ nrets
) {
1620 DUK_D(DUK_DPRINT("AppRequest callback doesn't match value stack configuration, "
1621 "top=%ld < old_top=%ld + nrets=%ld; "
1622 "this might mean it's unsafe to continue!",
1623 (long) duk_get_top(ctx
), (long) old_top
, (long) nrets
));
1627 /* Reply with tvals pushed by request callback */
1628 duk_debug_write_byte(thr
, DUK_DBG_IB_REPLY
);
1629 top
= duk_get_top(ctx
);
1630 for (idx
= top
- nrets
; idx
< top
; idx
++) {
1631 duk_debug_write_tval(thr
, DUK_GET_TVAL_POSIDX(ctx
, idx
));
1633 duk_debug_write_eom(thr
);
1635 DUK_ASSERT(duk_get_top(ctx
) >= old_top
+ 1);
1636 if (duk_get_top(ctx
) < old_top
+ 1) {
1637 DUK_D(DUK_DPRINT("request callback return value doesn't match value stack configuration"));
1640 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_APPLICATION
, duk_get_string(ctx
, -1));
1643 duk_set_top(ctx
, old_top
); /* restore stack top */
1645 DUK_D(DUK_DPRINT("no request callback, treat AppRequest as unsupported"));
1646 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNSUPPORTED
, "AppRequest unsupported by target");
1652 duk_set_top(ctx
, old_top
); /* restore stack top */
1653 DUK__SET_CONN_BROKEN(thr
, 1);
1660 #if defined(DUK_USE_DEBUGGER_DUMPHEAP)
1661 /* XXX: this has some overlap with object inspection; remove this and make
1662 * DumpHeap return lists of heapptrs instead?
1664 DUK_LOCAL
void duk__debug_dump_heaphdr(duk_hthread
*thr
, duk_heap
*heap
, duk_heaphdr
*hdr
) {
1667 duk_debug_write_heapptr(thr
, hdr
);
1668 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HEAPHDR_GET_TYPE(hdr
));
1669 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HEAPHDR_GET_FLAGS_RAW(hdr
));
1670 #if defined(DUK_USE_REFERENCE_COUNTING)
1671 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HEAPHDR_GET_REFCOUNT(hdr
));
1673 duk_debug_write_int(thr
, (duk_int32_t
) -1);
1676 switch (DUK_HEAPHDR_GET_TYPE(hdr
)) {
1677 case DUK_HTYPE_STRING
: {
1678 duk_hstring
*h
= (duk_hstring
*) hdr
;
1680 duk_debug_write_uint(thr
, (duk_int32_t
) DUK_HSTRING_GET_BYTELEN(h
));
1681 duk_debug_write_uint(thr
, (duk_int32_t
) DUK_HSTRING_GET_CHARLEN(h
));
1682 duk_debug_write_uint(thr
, (duk_int32_t
) DUK_HSTRING_GET_HASH(h
));
1683 duk_debug_write_hstring(thr
, h
);
1686 case DUK_HTYPE_OBJECT
: {
1687 duk_hobject
*h
= (duk_hobject
*) hdr
;
1689 duk_uint_fast32_t i
;
1691 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HOBJECT_GET_CLASS_NUMBER(h
));
1692 duk_debug_write_heapptr(thr
, (duk_heaphdr
*) DUK_HOBJECT_GET_PROTOTYPE(heap
, h
));
1693 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HOBJECT_GET_ESIZE(h
));
1694 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HOBJECT_GET_ENEXT(h
));
1695 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HOBJECT_GET_ASIZE(h
));
1696 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HOBJECT_GET_HSIZE(h
));
1698 for (i
= 0; i
< (duk_uint_fast32_t
) DUK_HOBJECT_GET_ENEXT(h
); i
++) {
1699 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HOBJECT_E_GET_FLAGS(heap
, h
, i
));
1700 k
= DUK_HOBJECT_E_GET_KEY(heap
, h
, i
);
1701 duk_debug_write_heapptr(thr
, (duk_heaphdr
*) k
);
1703 duk_debug_write_int(thr
, 0); /* isAccessor */
1704 duk_debug_write_unused(thr
);
1707 if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap
, h
, i
)) {
1708 duk_debug_write_int(thr
, 1); /* isAccessor */
1709 duk_debug_write_heapptr(thr
, (duk_heaphdr
*) DUK_HOBJECT_E_GET_VALUE_PTR(heap
, h
, i
)->a
.get
);
1710 duk_debug_write_heapptr(thr
, (duk_heaphdr
*) DUK_HOBJECT_E_GET_VALUE_PTR(heap
, h
, i
)->a
.set
);
1712 duk_debug_write_int(thr
, 0); /* isAccessor */
1714 duk__debug_write_tval_heapptr(thr
, &DUK_HOBJECT_E_GET_VALUE_PTR(heap
, h
, i
)->v
);
1718 for (i
= 0; i
< (duk_uint_fast32_t
) DUK_HOBJECT_GET_ASIZE(h
); i
++) {
1719 /* Note: array dump will include elements beyond
1722 duk__debug_write_tval_heapptr(thr
, DUK_HOBJECT_A_GET_VALUE_PTR(heap
, h
, i
));
1726 case DUK_HTYPE_BUFFER
: {
1727 duk_hbuffer
*h
= (duk_hbuffer
*) hdr
;
1729 duk_debug_write_uint(thr
, (duk_uint32_t
) DUK_HBUFFER_GET_SIZE(h
));
1730 duk_debug_write_buffer(thr
, (const char *) DUK_HBUFFER_GET_DATA_PTR(heap
, h
), (duk_size_t
) DUK_HBUFFER_GET_SIZE(h
));
1734 DUK_D(DUK_DPRINT("invalid htype: %d", (int) DUK_HEAPHDR_GET_TYPE(hdr
)));
1739 DUK_LOCAL
void duk__debug_dump_heap_allocated(duk_hthread
*thr
, duk_heap
*heap
) {
1742 hdr
= heap
->heap_allocated
;
1743 while (hdr
!= NULL
) {
1744 duk__debug_dump_heaphdr(thr
, heap
, hdr
);
1745 hdr
= DUK_HEAPHDR_GET_NEXT(heap
, hdr
);
1749 #if defined(DUK_USE_STRTAB_CHAIN)
1750 DUK_LOCAL
void duk__debug_dump_strtab_chain(duk_hthread
*thr
, duk_heap
*heap
) {
1751 duk_uint_fast32_t i
, j
;
1752 duk_strtab_entry
*e
;
1753 #if defined(DUK_USE_HEAPPTR16)
1760 for (i
= 0; i
< DUK_STRTAB_CHAIN_SIZE
; i
++) {
1761 e
= heap
->strtable
+ i
;
1762 if (e
->listlen
> 0) {
1763 #if defined(DUK_USE_HEAPPTR16)
1764 lst
= (duk_uint16_t
*) DUK_USE_HEAPPTR_DEC16(heap
->heap_udata
, e
->u
.strlist16
);
1768 DUK_ASSERT(lst
!= NULL
);
1770 for (j
= 0; j
< e
->listlen
; j
++) {
1771 #if defined(DUK_USE_HEAPPTR16)
1772 h
= DUK_USE_HEAPPTR_DEC16(heap
->heap_udata
, lst
[j
]);
1777 duk__debug_dump_heaphdr(thr
, heap
, (duk_heaphdr
*) h
);
1781 #if defined(DUK_USE_HEAPPTR16)
1782 h
= DUK_USE_HEAPPTR_DEC16(heap
->heap_udata
, e
->u
.str16
);
1787 duk__debug_dump_heaphdr(thr
, heap
, (duk_heaphdr
*) h
);
1792 #endif /* DUK_USE_STRTAB_CHAIN */
1794 #if defined(DUK_USE_STRTAB_PROBE)
1795 DUK_LOCAL
void duk__debug_dump_strtab_probe(duk_hthread
*thr
, duk_heap
*heap
) {
1799 for (i
= 0; i
< heap
->st_size
; i
++) {
1800 #if defined(DUK_USE_HEAPPTR16)
1801 h
= DUK_USE_HEAPPTR_DEC16(heap
->heap_udata
, heap
->strtable16
[i
]);
1803 h
= heap
->strtable
[i
];
1805 if (h
== NULL
|| h
== DUK_STRTAB_DELETED_MARKER(heap
)) {
1809 duk__debug_dump_heaphdr(thr
, heap
, (duk_heaphdr
*) h
);
1812 #endif /* DUK_USE_STRTAB_PROBE */
1814 DUK_LOCAL
void duk__debug_handle_dump_heap(duk_hthread
*thr
, duk_heap
*heap
) {
1815 DUK_D(DUK_DPRINT("debug command DumpHeap"));
1817 duk_debug_write_reply(thr
);
1818 duk__debug_dump_heap_allocated(thr
, heap
);
1819 #if defined(DUK_USE_STRTAB_CHAIN)
1820 duk__debug_dump_strtab_chain(thr
, heap
);
1822 #if defined(DUK_USE_STRTAB_PROBE)
1823 duk__debug_dump_strtab_probe(thr
, heap
);
1825 duk_debug_write_eom(thr
);
1827 #endif /* DUK_USE_DEBUGGER_DUMPHEAP */
1829 DUK_LOCAL
void duk__debug_handle_get_bytecode(duk_hthread
*thr
, duk_heap
*heap
) {
1830 duk_activation
*act
;
1831 duk_hcompiledfunction
*fun
= NULL
;
1835 duk_int32_t level
= -1;
1840 DUK_D(DUK_DPRINT("debug command GetBytecode"));
1842 ibyte
= duk_debug_peek_byte(thr
);
1843 if (ibyte
!= DUK_DBG_IB_EOM
) {
1844 tv
= duk_debug_read_tval(thr
);
1849 if (DUK_TVAL_IS_OBJECT(tv
)) {
1850 /* tentative, checked later */
1851 fun
= (duk_hcompiledfunction
*) DUK_TVAL_GET_OBJECT(tv
);
1852 DUK_ASSERT(fun
!= NULL
);
1853 } else if (DUK_TVAL_IS_NUMBER(tv
)) {
1854 level
= (duk_int32_t
) DUK_TVAL_GET_NUMBER(tv
);
1856 DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!T", tv
));
1862 if (level
>= 0 || -level
> (duk_int32_t
) thr
->callstack_top
) {
1863 DUK_D(DUK_DPRINT("invalid callstack level for GetBytecode"));
1866 act
= thr
->callstack
+ thr
->callstack_top
+ level
;
1867 fun
= (duk_hcompiledfunction
*) DUK_ACT_GET_FUNC(act
);
1870 if (fun
== NULL
|| !DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject
*) fun
)) {
1871 DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!O", fun
));
1874 DUK_ASSERT(fun
!= NULL
&& DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject
*) fun
));
1876 duk_debug_write_reply(thr
);
1877 n
= DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(heap
, fun
);
1878 duk_debug_write_int(thr
, (duk_int32_t
) n
);
1879 tv
= DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(heap
, fun
);
1880 for (i
= 0; i
< n
; i
++) {
1881 duk_debug_write_tval(thr
, tv
);
1884 n
= DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(heap
, fun
);
1885 duk_debug_write_int(thr
, (duk_int32_t
) n
);
1886 fn
= DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(heap
, fun
);
1887 for (i
= 0; i
< n
; i
++) {
1888 duk_debug_write_hobject(thr
, *fn
);
1891 duk_debug_write_string(thr
,
1892 (const char *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(heap
, fun
),
1893 (duk_size_t
) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(heap
, fun
));
1894 duk_debug_write_eom(thr
);
1898 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNKNOWN
, "invalid argument");
1902 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "invalid callstack level");
1907 * Object inspection commands: GetHeapObjInfo, GetObjPropDesc,
1908 * GetObjPropDescRange
1911 #if defined(DUK_USE_DEBUGGER_INSPECT)
1914 DUK_LOCAL
const char * const duk__debug_getinfo_heaphdr_keys
[] = {
1920 /* NULL not needed here */
1922 DUK_LOCAL duk_uint_t duk__debug_getinfo_heaphdr_masks
[] = {
1923 DUK_HEAPHDR_FLAG_REACHABLE
,
1924 DUK_HEAPHDR_FLAG_TEMPROOT
,
1925 DUK_HEAPHDR_FLAG_FINALIZABLE
,
1926 DUK_HEAPHDR_FLAG_FINALIZED
,
1927 DUK_HEAPHDR_FLAG_READONLY
,
1931 DUK_LOCAL
const char * const duk__debug_getinfo_hstring_keys
[] = {
1936 "strict_reserved_word",
1937 "eval_or_arguments",
1940 /* NULL not needed here */
1942 DUK_LOCAL duk_uint_t duk__debug_getinfo_hstring_masks
[] = {
1944 DUK_HSTRING_FLAG_ARRIDX
,
1945 DUK_HSTRING_FLAG_INTERNAL
,
1946 DUK_HSTRING_FLAG_RESERVED_WORD
,
1947 DUK_HSTRING_FLAG_STRICT_RESERVED_WORD
,
1948 DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS
,
1950 DUK_HSTRING_FLAG_EXTDATA
,
1953 DUK_LOCAL
const char * const duk__debug_getinfo_hobject_keys
[] = {
1973 /* NULL not needed here */
1975 DUK_LOCAL duk_uint_t duk__debug_getinfo_hobject_masks
[] = {
1976 DUK_HOBJECT_FLAG_EXTENSIBLE
,
1977 DUK_HOBJECT_FLAG_CONSTRUCTABLE
,
1978 DUK_HOBJECT_FLAG_BOUND
,
1979 DUK_HOBJECT_FLAG_COMPILEDFUNCTION
,
1980 DUK_HOBJECT_FLAG_NATIVEFUNCTION
,
1981 DUK_HOBJECT_FLAG_BUFFEROBJECT
,
1982 DUK_HOBJECT_FLAG_THREAD
,
1983 DUK_HOBJECT_FLAG_ARRAY_PART
,
1984 DUK_HOBJECT_FLAG_STRICT
,
1985 DUK_HOBJECT_FLAG_NOTAIL
,
1986 DUK_HOBJECT_FLAG_NEWENV
,
1987 DUK_HOBJECT_FLAG_NAMEBINDING
,
1988 DUK_HOBJECT_FLAG_CREATEARGS
,
1989 DUK_HOBJECT_FLAG_ENVRECCLOSED
,
1990 DUK_HOBJECT_FLAG_EXOTIC_ARRAY
,
1991 DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ
,
1992 DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS
,
1993 DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC
,
1994 DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ
,
1997 DUK_LOCAL
const char * const duk__debug_getinfo_hbuffer_keys
[] = {
2000 /* NULL not needed here */
2002 DUK_LOCAL duk_uint_t duk__debug_getinfo_hbuffer_masks
[] = {
2003 DUK_HBUFFER_FLAG_DYNAMIC
,
2004 DUK_HBUFFER_FLAG_EXTERNAL
,
2008 DUK_LOCAL
void duk__debug_getinfo_flags_key(duk_hthread
*thr
, const char *key
) {
2009 duk_debug_write_uint(thr
, 0);
2010 duk_debug_write_cstring(thr
, key
);
2013 DUK_LOCAL
void duk__debug_getinfo_prop_uint(duk_hthread
*thr
, const char *key
, duk_uint_t val
) {
2014 duk_debug_write_uint(thr
, 0);
2015 duk_debug_write_cstring(thr
, key
);
2016 duk_debug_write_uint(thr
, val
);
2019 DUK_LOCAL
void duk__debug_getinfo_prop_int(duk_hthread
*thr
, const char *key
, duk_int_t val
) {
2020 duk_debug_write_uint(thr
, 0);
2021 duk_debug_write_cstring(thr
, key
);
2022 duk_debug_write_int(thr
, val
);
2025 DUK_LOCAL
void duk__debug_getinfo_prop_bool(duk_hthread
*thr
, const char *key
, duk_bool_t val
) {
2026 duk_debug_write_uint(thr
, 0);
2027 duk_debug_write_cstring(thr
, key
);
2028 duk_debug_write_boolean(thr
, val
);
2031 DUK_LOCAL
void duk__debug_getinfo_bitmask(duk_hthread
*thr
, const char * const * keys
, duk_uint_t
*masks
, duk_uint_t flags
) {
2041 DUK_ASSERT(key
!= NULL
);
2043 DUK_DD(DUK_DDPRINT("inspect bitmask: key=%s, mask=0x%08lx, flags=0x%08lx", key
, (unsigned long) mask
, (unsigned long) flags
));
2044 duk__debug_getinfo_prop_bool(thr
, key
, flags
& mask
);
2048 /* Inspect a property using a virtual index into a conceptual property list
2049 * consisting of (1) all array part items from [0,a_size[ (even when above
2050 * .length) and (2) all entry part items from [0,e_next[. Unused slots are
2051 * indicated using dvalue 'unused'.
2053 DUK_LOCAL duk_bool_t
duk__debug_getprop_index(duk_hthread
*thr
, duk_heap
*heap
, duk_hobject
*h_obj
, duk_uint_t idx
) {
2057 duk_hobject
*h_getset
;
2062 a_size
= DUK_HOBJECT_GET_ASIZE(h_obj
);
2064 duk_debug_write_uint(thr
, DUK_PROPDESC_FLAGS_WEC
);
2065 duk_debug_write_uint(thr
, idx
);
2066 tv
= DUK_HOBJECT_A_GET_VALUE_PTR(heap
, h_obj
, idx
);
2067 duk_debug_write_tval(thr
, tv
);
2072 if (idx
>= DUK_HOBJECT_GET_ENEXT(h_obj
)) {
2076 h_key
= DUK_HOBJECT_E_GET_KEY(heap
, h_obj
, idx
);
2077 if (h_key
== NULL
) {
2078 duk_debug_write_uint(thr
, 0);
2079 duk_debug_write_null(thr
);
2080 duk_debug_write_unused(thr
);
2084 flags
= DUK_HOBJECT_E_GET_FLAGS(heap
, h_obj
, idx
);
2085 if (DUK_HSTRING_HAS_INTERNAL(h_key
)) {
2086 flags
|= DUK_DBG_PROPFLAG_INTERNAL
;
2088 duk_debug_write_uint(thr
, flags
);
2089 duk_debug_write_hstring(thr
, h_key
);
2090 if (flags
& DUK_PROPDESC_FLAG_ACCESSOR
) {
2091 h_getset
= DUK_HOBJECT_E_GET_VALUE_GETTER(heap
, h_obj
, idx
);
2093 duk_debug_write_hobject(thr
, h_getset
);
2095 duk_debug_write_null(thr
);
2097 h_getset
= DUK_HOBJECT_E_GET_VALUE_SETTER(heap
, h_obj
, idx
);
2099 duk_debug_write_hobject(thr
, h_getset
);
2101 duk_debug_write_null(thr
);
2104 tv
= DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap
, h_obj
, idx
);
2105 duk_debug_write_tval(thr
, tv
);
2110 DUK_LOCAL
void duk__debug_handle_get_heap_obj_info(duk_hthread
*thr
, duk_heap
*heap
) {
2113 DUK_D(DUK_DPRINT("debug command GetHeapObjInfo"));
2116 h
= duk_debug_read_any_ptr(thr
);
2118 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNKNOWN
, "invalid target");
2122 duk_debug_write_reply(thr
);
2124 /* As with all inspection code, we rely on the debug client providing
2125 * a valid, non-stale pointer: there's no portable way to safely
2126 * validate the pointer here.
2129 duk__debug_getinfo_flags_key(thr
, "heapptr");
2130 duk_debug_write_heapptr(thr
, h
);
2132 /* XXX: comes out as signed now */
2133 duk__debug_getinfo_prop_uint(thr
, "heaphdr_flags", (duk_uint_t
) DUK_HEAPHDR_GET_FLAGS(h
));
2134 duk__debug_getinfo_prop_uint(thr
, "heaphdr_type", (duk_uint_t
) DUK_HEAPHDR_GET_TYPE(h
));
2135 #if defined(DUK_USE_REFERENCE_COUNTING)
2136 duk__debug_getinfo_prop_uint(thr
, "refcount", (duk_uint_t
) DUK_HEAPHDR_GET_REFCOUNT(h
));
2139 duk__debug_getinfo_bitmask(thr
,
2140 duk__debug_getinfo_heaphdr_keys
,
2141 duk__debug_getinfo_heaphdr_masks
,
2142 DUK_HEAPHDR_GET_FLAGS_RAW(h
));
2145 switch (DUK_HEAPHDR_GET_TYPE(h
)) {
2146 case DUK_HTYPE_STRING
: {
2149 h_str
= (duk_hstring
*) h
;
2150 duk__debug_getinfo_bitmask(thr
,
2151 duk__debug_getinfo_hstring_keys
,
2152 duk__debug_getinfo_hstring_masks
,
2153 DUK_HEAPHDR_GET_FLAGS_RAW(h
));
2154 duk__debug_getinfo_prop_uint(thr
, "bytelen", DUK_HSTRING_GET_BYTELEN(h_str
));
2155 duk__debug_getinfo_prop_uint(thr
, "charlen", DUK_HSTRING_GET_CHARLEN(h_str
));
2156 duk__debug_getinfo_prop_uint(thr
, "hash", DUK_HSTRING_GET_HASH(h_str
));
2157 duk__debug_getinfo_flags_key(thr
, "data");
2158 duk_debug_write_hstring(thr
, h_str
);
2161 case DUK_HTYPE_OBJECT
: {
2163 duk_hobject
*h_proto
;
2165 h_obj
= (duk_hobject
*) h
;
2166 h_proto
= DUK_HOBJECT_GET_PROTOTYPE(heap
, h_obj
);
2168 /* duk_hobject specific fields. */
2169 duk__debug_getinfo_bitmask(thr
,
2170 duk__debug_getinfo_hobject_keys
,
2171 duk__debug_getinfo_hobject_masks
,
2172 DUK_HEAPHDR_GET_FLAGS_RAW(h
));
2173 duk__debug_getinfo_prop_uint(thr
, "class_number", DUK_HOBJECT_GET_CLASS_NUMBER(h_obj
));
2174 duk__debug_getinfo_flags_key(thr
, "class_name");
2175 duk_debug_write_hstring(thr
, DUK_HOBJECT_GET_CLASS_STRING(heap
, h_obj
));
2176 duk__debug_getinfo_flags_key(thr
, "prototype");
2177 if (h_proto
!= NULL
) {
2178 duk_debug_write_hobject(thr
, h_proto
);
2180 duk_debug_write_null(thr
);
2182 duk__debug_getinfo_flags_key(thr
, "props");
2183 duk_debug_write_pointer(thr
, (void *) DUK_HOBJECT_GET_PROPS(heap
, h_obj
));
2184 duk__debug_getinfo_prop_uint(thr
, "e_size", (duk_uint_t
) DUK_HOBJECT_GET_ESIZE(h_obj
));
2185 duk__debug_getinfo_prop_uint(thr
, "e_next", (duk_uint_t
) DUK_HOBJECT_GET_ENEXT(h_obj
));
2186 duk__debug_getinfo_prop_uint(thr
, "a_size", (duk_uint_t
) DUK_HOBJECT_GET_ASIZE(h_obj
));
2187 duk__debug_getinfo_prop_uint(thr
, "h_size", (duk_uint_t
) DUK_HOBJECT_GET_HSIZE(h_obj
));
2189 /* duk_hnativefunction specific fields. */
2190 if (DUK_HOBJECT_IS_NATIVEFUNCTION(h_obj
)) {
2191 duk_hnativefunction
*h_fun
;
2192 h_fun
= (duk_hnativefunction
*) h_obj
;
2194 duk__debug_getinfo_prop_int(thr
, "nargs", h_fun
->nargs
);
2195 duk__debug_getinfo_prop_int(thr
, "magic", h_fun
->magic
);
2196 duk__debug_getinfo_prop_bool(thr
, "varargs", h_fun
->magic
== DUK_HNATIVEFUNCTION_NARGS_VARARGS
);
2197 /* Native function pointer may be different from a void pointer,
2198 * and we serialize it from memory directly now (no byte swapping etc).
2200 duk__debug_getinfo_flags_key(thr
, "funcptr");
2201 duk_debug_write_buffer(thr
, (const char *) &h_fun
->func
, sizeof(h_fun
->func
));
2204 if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj
)) {
2205 duk_hcompiledfunction
*h_fun
;
2207 h_fun
= (duk_hcompiledfunction
*) h_obj
;
2209 duk__debug_getinfo_prop_int(thr
, "nregs", h_fun
->nregs
);
2210 duk__debug_getinfo_prop_int(thr
, "nargs", h_fun
->nargs
);
2211 duk__debug_getinfo_prop_uint(thr
, "start_line", h_fun
->start_line
);
2212 duk__debug_getinfo_prop_uint(thr
, "end_line", h_fun
->end_line
);
2213 h_buf
= (duk_hbuffer
*) DUK_HCOMPILEDFUNCTION_GET_DATA(thr
->heap
, h_fun
);
2214 if (h_buf
!= NULL
) {
2215 duk__debug_getinfo_flags_key(thr
, "data");
2216 duk_debug_write_heapptr(thr
, (duk_heaphdr
*) h_buf
);
2220 if (DUK_HOBJECT_IS_THREAD(h_obj
)) {
2221 /* XXX: Currently no inspection of threads, e.g. value stack, call
2222 * stack, catch stack, etc.
2225 h_thr
= (duk_hthread
*) h_obj
;
2229 if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj
)) {
2230 duk_hbufferobject
*h_bufobj
;
2231 h_bufobj
= (duk_hbufferobject
*) h_obj
;
2233 duk__debug_getinfo_prop_uint(thr
, "slice_offset", h_bufobj
->offset
);
2234 duk__debug_getinfo_prop_uint(thr
, "slice_length", h_bufobj
->length
);
2235 duk__debug_getinfo_prop_uint(thr
, "elem_shift", (duk_uint_t
) h_bufobj
->shift
);
2236 duk__debug_getinfo_prop_uint(thr
, "elem_type", (duk_uint_t
) h_bufobj
->elem_type
);
2237 duk__debug_getinfo_prop_bool(thr
, "is_view", (duk_uint_t
) h_bufobj
->is_view
);
2238 if (h_bufobj
->buf
!= NULL
) {
2239 duk__debug_getinfo_flags_key(thr
, "buffer");
2240 duk_debug_write_heapptr(thr
, (duk_heaphdr
*) h_bufobj
->buf
);
2245 case DUK_HTYPE_BUFFER
: {
2248 h_buf
= (duk_hbuffer
*) h
;
2249 duk__debug_getinfo_bitmask(thr
,
2250 duk__debug_getinfo_hbuffer_keys
,
2251 duk__debug_getinfo_hbuffer_masks
,
2252 DUK_HEAPHDR_GET_FLAGS_RAW(h
));
2253 duk__debug_getinfo_prop_uint(thr
, "size", (duk_uint_t
) DUK_HBUFFER_GET_SIZE(h_buf
));
2254 duk__debug_getinfo_flags_key(thr
, "dataptr");
2255 duk_debug_write_pointer(thr
, (void *) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h_buf
));
2256 duk__debug_getinfo_flags_key(thr
, "data");
2257 duk_debug_write_hbuffer(thr
, h_buf
); /* tolerates NULL h_buf */
2261 /* Since we already started writing the reply, just emit nothing. */
2262 DUK_D(DUK_DPRINT("inspect target pointer has invalid heaphdr type"));
2266 duk_debug_write_eom(thr
);
2269 DUK_LOCAL
void duk__debug_handle_get_obj_prop_desc(duk_hthread
*thr
, duk_heap
*heap
) {
2275 DUK_D(DUK_DPRINT("debug command GetObjPropDesc"));
2278 h
= duk_debug_read_any_ptr(thr
);
2280 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNKNOWN
, "invalid target");
2283 h_key
= duk_debug_read_hstring(thr
);
2284 if (h
== NULL
|| DUK_HEAPHDR_GET_TYPE(h
) != DUK_HTYPE_OBJECT
|| h_key
== NULL
) {
2287 h_obj
= (duk_hobject
*) h
;
2289 if (duk_hobject_get_own_propdesc(thr
, h_obj
, h_key
, &desc
, 0 /*flags*/)) {
2290 duk_int_t virtual_idx
;
2293 /* To use the shared helper need the virtual index. */
2294 DUK_ASSERT(desc
.e_idx
>= 0 || desc
.a_idx
>= 0);
2295 virtual_idx
= (desc
.a_idx
>= 0 ? desc
.a_idx
:
2296 (duk_int_t
) DUK_HOBJECT_GET_ASIZE(h_obj
) + desc
.e_idx
);
2298 duk_debug_write_reply(thr
);
2299 rc
= duk__debug_getprop_index(thr
, heap
, h_obj
, (duk_uint_t
) virtual_idx
);
2300 DUK_ASSERT(rc
== 1);
2302 duk_debug_write_eom(thr
);
2304 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_NOTFOUND
, "not found");
2309 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNKNOWN
, "invalid args");
2312 DUK_LOCAL
void duk__debug_handle_get_obj_prop_desc_range(duk_hthread
*thr
, duk_heap
*heap
) {
2315 duk_uint_t idx
, idx_start
, idx_end
;
2317 DUK_D(DUK_DPRINT("debug command GetObjPropDescRange"));
2320 h
= duk_debug_read_any_ptr(thr
);
2321 idx_start
= duk_debug_read_int(thr
);
2322 idx_end
= duk_debug_read_int(thr
);
2323 if (h
== NULL
|| DUK_HEAPHDR_GET_TYPE(h
) != DUK_HTYPE_OBJECT
) {
2326 h_obj
= (duk_hobject
*) h
;
2328 /* The index range space is conceptually the array part followed by the
2329 * entry part. Unlike normal enumeration all slots are exposed here as
2330 * is and return 'unused' if the slots are not in active use. In particular
2331 * the array part is included for the full a_size regardless of what the
2335 duk_debug_write_reply(thr
);
2336 for (idx
= idx_start
; idx
< idx_end
; idx
++) {
2337 if (!duk__debug_getprop_index(thr
, heap
, h_obj
, idx
)) {
2341 duk_debug_write_eom(thr
);
2345 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNKNOWN
, "invalid args");
2348 #endif /* DUK_USE_DEBUGGER_INSPECT */
2351 * Process incoming debug requests
2353 * Individual request handlers can push temporaries on the value stack and
2354 * rely on duk__debug_process_message() to restore the value stack top
2358 /* Process one debug message. Automatically restore value stack top to its
2359 * entry value, so that individual message handlers don't need exact value
2360 * stack handling which is convenient.
2362 DUK_LOCAL
void duk__debug_process_message(duk_hthread
*thr
) {
2363 duk_context
*ctx
= (duk_context
*) thr
;
2367 duk_idx_t entry_top
;
2369 DUK_ASSERT(thr
!= NULL
);
2371 DUK_ASSERT(heap
!= NULL
);
2374 entry_top
= duk_get_top(ctx
);
2376 x
= duk_debug_read_byte(thr
);
2378 case DUK_DBG_IB_REQUEST
: {
2379 cmd
= duk_debug_read_int(thr
);
2381 case DUK_DBG_CMD_BASICINFO
: {
2382 duk__debug_handle_basic_info(thr
, heap
);
2385 case DUK_DBG_CMD_TRIGGERSTATUS
: {
2386 duk__debug_handle_trigger_status(thr
, heap
);
2389 case DUK_DBG_CMD_PAUSE
: {
2390 duk__debug_handle_pause(thr
, heap
);
2393 case DUK_DBG_CMD_RESUME
: {
2394 duk__debug_handle_resume(thr
, heap
);
2397 case DUK_DBG_CMD_STEPINTO
:
2398 case DUK_DBG_CMD_STEPOVER
:
2399 case DUK_DBG_CMD_STEPOUT
: {
2400 duk__debug_handle_step(thr
, heap
, cmd
);
2403 case DUK_DBG_CMD_LISTBREAK
: {
2404 duk__debug_handle_list_break(thr
, heap
);
2407 case DUK_DBG_CMD_ADDBREAK
: {
2408 duk__debug_handle_add_break(thr
, heap
);
2411 case DUK_DBG_CMD_DELBREAK
: {
2412 duk__debug_handle_del_break(thr
, heap
);
2415 case DUK_DBG_CMD_GETVAR
: {
2416 duk__debug_handle_get_var(thr
, heap
);
2419 case DUK_DBG_CMD_PUTVAR
: {
2420 duk__debug_handle_put_var(thr
, heap
);
2423 case DUK_DBG_CMD_GETCALLSTACK
: {
2424 duk__debug_handle_get_call_stack(thr
, heap
);
2427 case DUK_DBG_CMD_GETLOCALS
: {
2428 duk__debug_handle_get_locals(thr
, heap
);
2431 case DUK_DBG_CMD_EVAL
: {
2432 duk__debug_handle_eval(thr
, heap
);
2435 case DUK_DBG_CMD_DETACH
: {
2436 /* The actual detached_cb call is postponed to message loop so
2437 * we don't need any special precautions here (just skip to EOM
2438 * on the already closed connection).
2440 duk__debug_handle_detach(thr
, heap
);
2443 #if defined(DUK_USE_DEBUGGER_DUMPHEAP)
2444 case DUK_DBG_CMD_DUMPHEAP
: {
2445 duk__debug_handle_dump_heap(thr
, heap
);
2448 #endif /* DUK_USE_DEBUGGER_DUMPHEAP */
2449 case DUK_DBG_CMD_GETBYTECODE
: {
2450 duk__debug_handle_get_bytecode(thr
, heap
);
2453 case DUK_DBG_CMD_APPREQUEST
: {
2454 duk__debug_handle_apprequest(thr
, heap
);
2457 #if defined(DUK_USE_DEBUGGER_INSPECT)
2458 case DUK_DBG_CMD_GETHEAPOBJINFO
: {
2459 duk__debug_handle_get_heap_obj_info(thr
, heap
);
2462 case DUK_DBG_CMD_GETOBJPROPDESC
: {
2463 duk__debug_handle_get_obj_prop_desc(thr
, heap
);
2466 case DUK_DBG_CMD_GETOBJPROPDESCRANGE
: {
2467 duk__debug_handle_get_obj_prop_desc_range(thr
, heap
);
2470 #endif /* DUK_USE_DEBUGGER_INSPECT */
2472 DUK_D(DUK_DPRINT("debug command unsupported: %d", (int) cmd
));
2473 duk_debug_write_error_eom(thr
, DUK_DBG_ERR_UNSUPPORTED
, "unsupported command");
2478 case DUK_DBG_IB_REPLY
: {
2479 DUK_D(DUK_DPRINT("debug reply, skipping"));
2482 case DUK_DBG_IB_ERROR
: {
2483 DUK_D(DUK_DPRINT("debug error, skipping"));
2486 case DUK_DBG_IB_NOTIFY
: {
2487 DUK_D(DUK_DPRINT("debug notify, skipping"));
2491 DUK_D(DUK_DPRINT("invalid initial byte, drop connection: %d", (int) x
));
2494 } /* switch initial byte */
2496 DUK_ASSERT(duk_get_top(ctx
) >= entry_top
);
2497 duk_set_top(ctx
, entry_top
);
2498 duk__debug_skip_to_eom(thr
);
2502 DUK_ASSERT(duk_get_top(ctx
) >= entry_top
);
2503 duk_set_top(ctx
, entry_top
);
2504 DUK__SET_CONN_BROKEN(thr
, 1);
2508 DUK_LOCAL
void duk__check_resend_status(duk_hthread
*thr
) {
2509 if (thr
->heap
->dbg_read_cb
!= NULL
&& thr
->heap
->dbg_state_dirty
) {
2510 duk_debug_send_status(thr
);
2511 thr
->heap
->dbg_state_dirty
= 0;
2515 DUK_INTERNAL duk_bool_t
duk_debug_process_messages(duk_hthread
*thr
, duk_bool_t no_block
) {
2516 duk_context
*ctx
= (duk_context
*) thr
;
2517 #if defined(DUK_USE_ASSERTIONS)
2518 duk_idx_t entry_top
;
2520 duk_bool_t retval
= 0;
2522 DUK_ASSERT(thr
!= NULL
);
2524 DUK_ASSERT(thr
->heap
!= NULL
);
2525 #if defined(DUK_USE_ASSERTIONS)
2526 entry_top
= duk_get_top(ctx
);
2529 DUK_D(DUK_DPRINT("process debug messages: read_cb=%s, no_block=%ld, detaching=%ld, processing=%ld",
2530 thr
->heap
->dbg_read_cb
? "not NULL" : "NULL", (long) no_block
,
2531 (long) thr
->heap
->dbg_detaching
, (long) thr
->heap
->dbg_processing
));
2532 DUK_DD(DUK_DDPRINT("top at entry: %ld", (long) duk_get_top(ctx
)));
2534 /* thr->heap->dbg_detaching may be != 0 if a debugger write outside
2535 * the message loop caused a transport error and detach1() to run.
2537 DUK_ASSERT(thr
->heap
->dbg_detaching
== 0 || thr
->heap
->dbg_detaching
== 1);
2538 DUK_ASSERT(thr
->heap
->dbg_processing
== 0);
2539 thr
->heap
->dbg_processing
= 1;
2541 /* Ensure dirty state causes a Status even if never process any
2542 * messages. This is expected by the bytecode executor when in
2543 * the running state.
2545 duk__check_resend_status(thr
);
2548 /* Process messages until we're no longer paused or we peek
2549 * and see there's nothing to read right now.
2551 DUK_DD(DUK_DDPRINT("top at loop top: %ld", (long) duk_get_top(ctx
)));
2552 DUK_ASSERT(thr
->heap
->dbg_processing
== 1);
2554 while (thr
->heap
->dbg_read_cb
== NULL
&& thr
->heap
->dbg_detaching
) {
2555 /* Detach is pending; can be triggered from outside the
2556 * debugger loop (e.g. Status notify write error) or by
2557 * previous message handling. Call detached callback
2558 * here, in a controlled state, to ensure a possible
2559 * reattach inside the detached_cb is handled correctly.
2561 * Recheck for detach in a while loop: an immediate
2562 * reattach involves a call to duk_debugger_attach()
2563 * which writes a debugger handshake line immediately
2564 * inside the API call. If the transport write fails
2565 * for that handshake, we can immediately end up in a
2566 * "transport broken, detaching" case several times here.
2567 * Loop back until we're either cleanly attached or
2570 * NOTE: Reset dbg_processing = 1 forcibly, in case we
2571 * re-attached; duk_debugger_attach() sets dbg_processing
2572 * to 0 at the moment.
2575 DUK_D(DUK_DPRINT("detach pending (dbg_read_cb == NULL, dbg_detaching != 0), call detach2"));
2577 duk__debug_do_detach2(thr
->heap
);
2578 thr
->heap
->dbg_processing
= 1; /* may be set to 0 by duk_debugger_attach() inside callback */
2580 DUK_D(DUK_DPRINT("after detach2 (and possible reattach): dbg_read_cb=%s, dbg_detaching=%ld",
2581 thr
->heap
->dbg_read_cb
? "not NULL" : "NULL", (long) thr
->heap
->dbg_detaching
));
2583 DUK_ASSERT(thr
->heap
->dbg_detaching
== 0); /* true even with reattach */
2584 DUK_ASSERT(thr
->heap
->dbg_processing
== 1); /* even after a detach and possible reattach */
2586 if (thr
->heap
->dbg_read_cb
== NULL
) {
2587 DUK_D(DUK_DPRINT("debug connection broken (and not detaching), stop processing messages"));
2591 if (!thr
->heap
->dbg_paused
|| no_block
) {
2592 if (!duk_debug_read_peek(thr
)) {
2593 /* Note: peek cannot currently trigger a detach
2594 * so the dbg_detaching == 0 assert outside the
2597 DUK_D(DUK_DPRINT("processing debug message, peek indicated no data, stop processing messages"));
2600 DUK_D(DUK_DPRINT("processing debug message, peek indicated there is data, handle it"));
2602 DUK_D(DUK_DPRINT("paused, process debug message, blocking if necessary"));
2605 duk__check_resend_status(thr
);
2606 duk__debug_process_message(thr
);
2607 duk__check_resend_status(thr
);
2609 retval
= 1; /* processed one or more messages */
2612 DUK_ASSERT(thr
->heap
->dbg_detaching
== 0);
2613 DUK_ASSERT(thr
->heap
->dbg_processing
== 1);
2614 thr
->heap
->dbg_processing
= 0;
2616 /* As an initial implementation, read flush after exiting the message
2617 * loop. If transport is broken, this is a no-op (with debug logs).
2619 duk_debug_read_flush(thr
); /* this cannot initiate a detach */
2620 DUK_ASSERT(thr
->heap
->dbg_detaching
== 0);
2622 DUK_DD(DUK_DDPRINT("top at exit: %ld", (long) duk_get_top(ctx
)));
2624 #if defined(DUK_USE_ASSERTIONS)
2625 /* Easy to get wrong, so assert for it. */
2626 DUK_ASSERT(entry_top
== duk_get_top(ctx
));
2633 * Halt execution helper
2636 /* Halt execution and enter a debugger message loop until execution is resumed
2637 * by the client. PC for the current activation may be temporarily decremented
2638 * so that the "current" instruction will be shown by the client. This helper
2639 * is callable from anywhere, also outside bytecode executor.
2642 DUK_INTERNAL
void duk_debug_halt_execution(duk_hthread
*thr
, duk_bool_t use_prev_pc
) {
2643 duk_activation
*act
;
2644 duk_hcompiledfunction
*fun
;
2645 duk_instr_t
*old_pc
= NULL
;
2647 DUK_ASSERT(thr
!= NULL
);
2648 DUK_ASSERT(thr
->heap
!= NULL
);
2649 DUK_ASSERT(DUK_HEAP_IS_DEBUGGER_ATTACHED(thr
->heap
));
2650 DUK_ASSERT(thr
->heap
->dbg_processing
== 0);
2652 DUK_HEAP_SET_PAUSED(thr
->heap
);
2654 act
= duk_hthread_get_current_activation(thr
);
2656 /* NOTE: act may be NULL if an error is thrown outside of any activation,
2657 * which may happen in the case of, e.g. syntax errors.
2660 /* Decrement PC if that was requested, this requires a PC sync. */
2662 duk_hthread_sync_currpc(thr
);
2663 old_pc
= act
->curr_pc
;
2664 fun
= (duk_hcompiledfunction
*) DUK_ACT_GET_FUNC(act
);
2666 /* Short circuit if is safe: if act->curr_pc != NULL, 'fun' is
2667 * guaranteed to be a non-NULL Ecmascript function.
2669 DUK_ASSERT(act
->curr_pc
== NULL
||
2670 (fun
!= NULL
&& DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject
*) fun
)));
2672 act
->curr_pc
!= NULL
&&
2673 act
->curr_pc
> DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr
->heap
, fun
)) {
2678 /* Process debug messages until we are no longer paused. */
2680 /* NOTE: This is a bit fragile. It's important to ensure that
2681 * duk_debug_process_messages() never throws an error or
2682 * act->curr_pc will never be reset.
2685 thr
->heap
->dbg_state_dirty
= 1;
2686 while (thr
->heap
->dbg_paused
) {
2687 DUK_ASSERT(DUK_HEAP_IS_DEBUGGER_ATTACHED(thr
->heap
));
2688 DUK_ASSERT(thr
->heap
->dbg_processing
);
2689 duk_debug_process_messages(thr
, 0 /*no_block*/);
2692 /* XXX: Decrementing and restoring act->curr_pc works now, but if the
2693 * debugger message loop gains the ability to adjust the current PC
2694 * (e.g. a forced jump) restoring the PC here will break. Another
2695 * approach would be to use a state flag for the "decrement 1 from
2696 * topmost activation's PC" and take it into account whenever dealing
2700 act
->curr_pc
= old_pc
; /* restore PC */
2705 * Breakpoint management
2708 DUK_INTERNAL duk_small_int_t
duk_debug_add_breakpoint(duk_hthread
*thr
, duk_hstring
*filename
, duk_uint32_t line
) {
2712 /* Caller must trigger recomputation of active breakpoint list. To
2713 * ensure stale values are not used if that doesn't happen, clear the
2714 * active breakpoint list here.
2717 DUK_ASSERT(thr
!= NULL
);
2718 DUK_ASSERT(filename
!= NULL
);
2720 DUK_ASSERT(heap
!= NULL
);
2722 if (heap
->dbg_breakpoint_count
>= DUK_HEAP_MAX_BREAKPOINTS
) {
2723 DUK_D(DUK_DPRINT("failed to add breakpoint for %O:%ld, all breakpoint slots used",
2724 (duk_heaphdr
*) filename
, (long) line
));
2727 heap
->dbg_breakpoints_active
[0] = (duk_breakpoint
*) NULL
;
2728 b
= heap
->dbg_breakpoints
+ (heap
->dbg_breakpoint_count
++);
2729 b
->filename
= filename
;
2731 DUK_HSTRING_INCREF(thr
, filename
);
2733 return heap
->dbg_breakpoint_count
- 1; /* index */
2736 DUK_INTERNAL duk_bool_t
duk_debug_remove_breakpoint(duk_hthread
*thr
, duk_small_uint_t breakpoint_index
) {
2740 duk_size_t move_size
;
2742 /* Caller must trigger recomputation of active breakpoint list. To
2743 * ensure stale values are not used if that doesn't happen, clear the
2744 * active breakpoint list here.
2747 DUK_ASSERT(thr
!= NULL
);
2749 DUK_ASSERT(heap
!= NULL
);
2750 DUK_ASSERT(DUK_HEAP_IS_DEBUGGER_ATTACHED(thr
->heap
));
2751 DUK_ASSERT_DISABLE(breakpoint_index
>= 0); /* unsigned */
2753 if (breakpoint_index
>= heap
->dbg_breakpoint_count
) {
2754 DUK_D(DUK_DPRINT("invalid breakpoint index: %ld", (long) breakpoint_index
));
2757 b
= heap
->dbg_breakpoints
+ breakpoint_index
;
2760 DUK_ASSERT(h
!= NULL
);
2762 move_size
= sizeof(duk_breakpoint
) * (heap
->dbg_breakpoint_count
- breakpoint_index
- 1);
2763 if (move_size
> 0) {
2764 DUK_MEMMOVE((void *) b
,
2765 (const void *) (b
+ 1),
2766 (size_t) move_size
);
2768 heap
->dbg_breakpoint_count
--;
2769 heap
->dbg_breakpoints_active
[0] = (duk_breakpoint
*) NULL
;
2771 DUK_HSTRING_DECREF(thr
, h
); /* side effects */
2772 DUK_UNREF(h
); /* w/o refcounting */
2774 /* Breakpoint entries above the used area are left as garbage. */
2779 #undef DUK__SET_CONN_BROKEN
2781 #else /* DUK_USE_DEBUGGER_SUPPORT */
2783 /* No debugger support. */
2785 #endif /* DUK_USE_DEBUGGER_SUPPORT */