]> git.proxmox.com Git - ceph.git/blame - ceph/src/civetweb/src/third_party/duktape-1.5.2/src-separate/duk_debugger.c
buildsys: switch source download to quincy
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.5.2 / src-separate / duk_debugger.c
CommitLineData
11fdf7f2
TL
1/*
2 * Duktape debugger
3 */
4
5#include "duk_internal.h"
6
7#if defined(DUK_USE_DEBUGGER_SUPPORT)
8
9/*
10 * Helper structs
11 */
12
13typedef union {
14 void *p;
15 duk_uint_t b[1];
16 /* Use b[] to access the size of the union, which is strictly not
17 * correct. Can't use fixed size unless there's feature detection
18 * for pointer byte size.
19 */
20} duk__ptr_union;
21
22/*
23 * Detach handling
24 */
25
26#define DUK__SET_CONN_BROKEN(thr,reason) do { \
27 /* For now shared handler is fine. */ \
28 duk__debug_do_detach1((thr)->heap, (reason)); \
29 } while (0)
30
31DUK_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.
38 */
39
40 if (heap->dbg_detaching) {
41 DUK_D(DUK_DPRINT("debugger already detaching, ignore detach1"));
42 return;
43 }
44
45 DUK_D(DUK_DPRINT("debugger transport detaching, marking transport broken"));
46
47 heap->dbg_detaching = 1; /* prevent multiple in-progress detaches */
48
49 if (heap->dbg_write_cb != NULL) {
50 duk_hthread *thr;
51
52 thr = heap->heap_thread;
53 DUK_ASSERT(thr != NULL);
54
55 duk_debug_write_notify(thr, DUK_DBG_CMD_DETACHING);
56 duk_debug_write_int(thr, reason);
57 duk_debug_write_eom(thr);
58 }
59
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 */
69 heap->dbg_paused = 0;
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;
77
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.
82 *
83 * XXX: clear breakpoint on either attach or detach?
84 */
85 heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL;
86}
87
88DUK_LOCAL void duk__debug_do_detach2(duk_heap *heap) {
89 duk_debug_detached_function detached_cb;
90 void *detached_udata;
91
92 /* Safe to call multiple times. */
93
94 detached_cb = heap->dbg_detached_cb;
95 detached_udata = heap->dbg_udata;
96 heap->dbg_detached_cb = NULL;
97 heap->dbg_udata = NULL;
98
99 if (detached_cb) {
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.
103 */
104 DUK_D(DUK_DPRINT("detached during message loop, delayed call to detached_cb"));
105 detached_cb(detached_udata);
106 }
107
108 heap->dbg_detaching = 0;
109}
110
111DUK_INTERNAL void duk_debug_do_detach(duk_heap *heap) {
112 duk__debug_do_detach1(heap, 0);
113 duk__debug_do_detach2(heap);
114}
115
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.
120 */
121DUK_LOCAL void duk__debug_null_most_callbacks(duk_hthread *thr) {
122 duk_heap *heap;
123 heap = thr->heap;
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 */
132}
133
134/*
135 * Debug connection peek and flush primitives
136 */
137
138DUK_INTERNAL duk_bool_t duk_debug_read_peek(duk_hthread *thr) {
139 duk_heap *heap;
140
141 DUK_ASSERT(thr != NULL);
142 heap = thr->heap;
143 DUK_ASSERT(heap != NULL);
144
145 if (heap->dbg_read_cb == NULL) {
146 DUK_D(DUK_DPRINT("attempt to peek in detached state, return zero (= no data)"));
147 return 0;
148 }
149 if (heap->dbg_peek_cb == NULL) {
150 DUK_DD(DUK_DDPRINT("no peek callback, return zero (= no data)"));
151 return 0;
152 }
153
154 return (duk_bool_t) (heap->dbg_peek_cb(heap->dbg_udata) > 0);
155}
156
157DUK_INTERNAL void duk_debug_read_flush(duk_hthread *thr) {
158 duk_heap *heap;
159
160 DUK_ASSERT(thr != NULL);
161 heap = thr->heap;
162 DUK_ASSERT(heap != NULL);
163
164 if (heap->dbg_read_cb == NULL) {
165 DUK_D(DUK_DPRINT("attempt to read flush in detached state, ignore"));
166 return;
167 }
168 if (heap->dbg_read_flush_cb == NULL) {
169 DUK_DD(DUK_DDPRINT("no read flush callback, ignore"));
170 return;
171 }
172
173 heap->dbg_read_flush_cb(heap->dbg_udata);
174}
175
176DUK_INTERNAL void duk_debug_write_flush(duk_hthread *thr) {
177 duk_heap *heap;
178
179 DUK_ASSERT(thr != NULL);
180 heap = thr->heap;
181 DUK_ASSERT(heap != NULL);
182
183 if (heap->dbg_read_cb == NULL) {
184 DUK_D(DUK_DPRINT("attempt to write flush in detached state, ignore"));
185 return;
186 }
187 if (heap->dbg_write_flush_cb == NULL) {
188 DUK_DD(DUK_DDPRINT("no write flush callback, ignore"));
189 return;
190 }
191
192 heap->dbg_write_flush_cb(heap->dbg_udata);
193}
194
195/*
196 * Debug connection skip primitives
197 */
198
199/* Skip fully. */
200DUK_INTERNAL void duk_debug_skip_bytes(duk_hthread *thr, duk_size_t length) {
201 duk_uint8_t dummy[64];
202 duk_size_t now;
203
204 DUK_ASSERT(thr != NULL);
205
206 while (length > 0) {
207 now = (length > sizeof(dummy) ? sizeof(dummy) : length);
208 duk_debug_read_bytes(thr, dummy, now);
209 length -= now;
210 }
211}
212
213DUK_INTERNAL void duk_debug_skip_byte(duk_hthread *thr) {
214 DUK_ASSERT(thr != NULL);
215
216 (void) duk_debug_read_byte(thr);
217}
218
219/*
220 * Debug connection read primitives
221 */
222
223/* Peek ahead in the stream one byte. */
224DUK_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.
228 */
229
230 duk_uint8_t x;
231
232 x = duk_debug_read_byte(thr);
233 thr->heap->dbg_have_next_byte = 1;
234 thr->heap->dbg_next_byte = x;
235 return x;
236}
237
238/* Read fully. */
239DUK_INTERNAL void duk_debug_read_bytes(duk_hthread *thr, duk_uint8_t *data, duk_size_t length) {
240 duk_heap *heap;
241 duk_uint8_t *p;
242 duk_size_t left;
243 duk_size_t got;
244
245 DUK_ASSERT(thr != NULL);
246 heap = thr->heap;
247 DUK_ASSERT(heap != NULL);
248
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));
251 goto fail;
252 }
253
254 /* NOTE: length may be zero */
255 p = data;
256 if (length >= 1 && heap->dbg_have_next_byte) {
257 heap->dbg_have_next_byte = 0;
258 *p++ = heap->dbg_next_byte;
259 }
260 for (;;) {
261 left = (duk_size_t) ((data + length) - p);
262 if (left == 0) {
263 break;
264 }
265 DUK_ASSERT(heap->dbg_read_cb != NULL);
266 DUK_ASSERT(left >= 1);
267#if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE)
268 left = 1;
269#endif
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);
275 goto fail;
276 }
277 p += got;
278 }
279 return;
280
281 fail:
282 DUK_MEMZERO((void *) data, (size_t) length);
283}
284
285DUK_INTERNAL duk_uint8_t duk_debug_read_byte(duk_hthread *thr) {
286 duk_uint8_t x;
287
288 x = 0; /* just in case callback is broken and won't write 'x' */
289 duk_debug_read_bytes(thr, &x, 1);
290 return x;
291}
292
293DUK_LOCAL duk_uint32_t duk__debug_read_uint32_raw(duk_hthread *thr) {
294 duk_uint8_t buf[4];
295
296 DUK_ASSERT(thr != NULL);
297
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];
303}
304
305DUK_LOCAL duk_uint32_t duk__debug_read_int32_raw(duk_hthread *thr) {
306 return (duk_int32_t) duk__debug_read_uint32_raw(thr);
307}
308
309DUK_LOCAL duk_uint16_t duk__debug_read_uint16_raw(duk_hthread *thr) {
310 duk_uint8_t buf[2];
311
312 DUK_ASSERT(thr != NULL);
313
314 duk_debug_read_bytes(thr, buf, 2);
315 return ((duk_uint16_t) buf[0] << 8) |
316 (duk_uint16_t) buf[1];
317}
318
319DUK_INTERNAL duk_int32_t duk_debug_read_int(duk_hthread *thr) {
320 duk_small_uint_t x;
321 duk_small_uint_t t;
322
323 DUK_ASSERT(thr != NULL);
324
325 x = duk_debug_read_byte(thr);
326 if (x >= 0xc0) {
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);
333 }
334
335 DUK_D(DUK_DPRINT("debug connection error: failed to decode int"));
336 DUK__SET_CONN_BROKEN(thr, 1);
337 return 0;
338}
339
340DUK_LOCAL duk_hstring *duk__debug_read_hstring_raw(duk_hthread *thr, duk_uint32_t len) {
341 duk_context *ctx = (duk_context *) thr;
342 duk_uint8_t buf[31];
343 duk_uint8_t *p;
344
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);
348 } else {
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);
353 }
354
355 return duk_require_hstring(ctx, -1);
356}
357
358DUK_INTERNAL duk_hstring *duk_debug_read_hstring(duk_hthread *thr) {
359 duk_context *ctx = (duk_context *) thr;
360 duk_small_uint_t x;
361 duk_uint32_t len;
362
363 DUK_ASSERT(thr != NULL);
364
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);
373 } else {
374 goto fail;
375 }
376
377 return duk__debug_read_hstring_raw(thr, len);
378
379 fail:
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);
384}
385
386DUK_LOCAL duk_hbuffer *duk__debug_read_hbuffer_raw(duk_hthread *thr, duk_uint32_t len) {
387 duk_context *ctx = (duk_context *) thr;
388 duk_uint8_t *p;
389
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);
393
394 return duk_require_hbuffer(ctx, -1);
395}
396
397DUK_LOCAL void *duk__debug_read_pointer_raw(duk_hthread *thr) {
398 duk_small_uint_t x;
399 duk__ptr_union pu;
400
401 DUK_ASSERT(thr != NULL);
402
403 x = duk_debug_read_byte(thr);
404 if (x != sizeof(pu)) {
405 goto fail;
406 }
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));
410#endif
411 return (void *) pu.p;
412
413 fail:
414 DUK_D(DUK_DPRINT("debug connection error: failed to decode pointer"));
415 DUK__SET_CONN_BROKEN(thr, 1);
416 return (void *) NULL;
417}
418
419DUK_LOCAL duk_double_t duk__debug_read_double_raw(duk_hthread *thr) {
420 duk_double_union du;
421
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);
425 return du.d;
426}
427
428#if 0
429DUK_INTERNAL duk_heaphdr *duk_debug_read_heapptr(duk_hthread *thr) {
430 duk_small_uint_t x;
431
432 DUK_ASSERT(thr != NULL);
433
434 x = duk_debug_read_byte(thr);
435 if (x != DUK_DBG_IB_HEAPPTR) {
436 goto fail;
437 }
438
439 return (duk_heaphdr *) duk__debug_read_pointer_raw(thr);
440
441 fail:
442 DUK_D(DUK_DPRINT("debug connection error: failed to decode heapptr"));
443 DUK__SET_CONN_BROKEN(thr, 1);
444 return NULL;
445}
446#endif
447
448DUK_INTERNAL duk_heaphdr *duk_debug_read_any_ptr(duk_hthread *thr) {
449 duk_small_uint_t x;
450
451 DUK_ASSERT(thr != NULL);
452
453 x = duk_debug_read_byte(thr);
454 switch (x) {
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.
460 */
461 if (x == DUK_DBG_IB_OBJECT) {
462 duk_debug_skip_byte(thr);
463 }
464 break;
465 default:
466 goto fail;
467 }
468
469 return (duk_heaphdr *) duk__debug_read_pointer_raw(thr);
470
471 fail:
472 DUK_D(DUK_DPRINT("debug connection error: failed to decode any pointer (object, pointer, heapptr)"));
473 DUK__SET_CONN_BROKEN(thr, 1);
474 return NULL;
475}
476
477DUK_INTERNAL duk_tval *duk_debug_read_tval(duk_hthread *thr) {
478 duk_context *ctx = (duk_context *) thr;
479 duk_uint8_t x;
480 duk_uint_t t;
481 duk_uint32_t len;
482
483 DUK_ASSERT(thr != NULL);
484
485 x = duk_debug_read_byte(thr);
486
487 if (x >= 0xc0) {
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);
491 goto return_ptr;
492 }
493 if (x >= 0x80) {
494 duk_push_uint(ctx, (duk_uint_t) (x - 0x80));
495 goto return_ptr;
496 }
497 if (x >= 0x60) {
498 len = (duk_uint32_t) (x - 0x60);
499 duk__debug_read_hstring_raw(thr, len);
500 goto return_ptr;
501 }
502
503 switch (x) {
504 case DUK_DBG_IB_INT4: {
505 duk_int32_t i = duk__debug_read_int32_raw(thr);
506 duk_push_i32(ctx, i);
507 break;
508 }
509 case DUK_DBG_IB_STR4: {
510 len = duk__debug_read_uint32_raw(thr);
511 duk__debug_read_hstring_raw(thr, len);
512 break;
513 }
514 case DUK_DBG_IB_STR2: {
515 len = duk__debug_read_uint16_raw(thr);
516 duk__debug_read_hstring_raw(thr, len);
517 break;
518 }
519 case DUK_DBG_IB_BUF4: {
520 len = duk__debug_read_uint32_raw(thr);
521 duk__debug_read_hbuffer_raw(thr, len);
522 break;
523 }
524 case DUK_DBG_IB_BUF2: {
525 len = duk__debug_read_uint16_raw(thr);
526 duk__debug_read_hbuffer_raw(thr, len);
527 break;
528 }
529 case DUK_DBG_IB_UNDEFINED: {
530 duk_push_undefined(ctx);
531 break;
532 }
533 case DUK_DBG_IB_NULL: {
534 duk_push_null(ctx);
535 break;
536 }
537 case DUK_DBG_IB_TRUE: {
538 duk_push_true(ctx);
539 break;
540 }
541 case DUK_DBG_IB_FALSE: {
542 duk_push_false(ctx);
543 break;
544 }
545 case DUK_DBG_IB_NUMBER: {
546 duk_double_t d;
547 d = duk__debug_read_double_raw(thr);
548 duk_push_number(ctx, d);
549 break;
550 }
551 case DUK_DBG_IB_OBJECT: {
552 duk_heaphdr *h;
553 duk_debug_skip_byte(thr);
554 h = (duk_heaphdr *) duk__debug_read_pointer_raw(thr);
555 duk_push_heapptr(thr, (void *) h);
556 break;
557 }
558 case DUK_DBG_IB_POINTER: {
559 void *ptr;
560 ptr = duk__debug_read_pointer_raw(thr);
561 duk_push_pointer(thr, ptr);
562 break;
563 }
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
567 * a void pointer.
568 */
569 DUK_D(DUK_DPRINT("reading lightfunc values unimplemented"));
570 goto fail;
571 }
572 case DUK_DBG_IB_HEAPPTR: {
573 duk_heaphdr *h;
574 h = (duk_heaphdr *) duk__debug_read_pointer_raw(thr);
575 duk_push_heapptr(thr, (void *) h);
576 break;
577 }
578 case DUK_DBG_IB_UNUSED: /* unused: not accepted in inbound messages */
579 default:
580 goto fail;
581 }
582
583 return_ptr:
584 return DUK_GET_TVAL_NEGIDX(thr, -1);
585
586 fail:
587 DUK_D(DUK_DPRINT("debug connection error: failed to decode tval"));
588 DUK__SET_CONN_BROKEN(thr, 1);
589 return NULL;
590}
591
592/*
593 * Debug connection write primitives
594 */
595
596/* Write fully. */
597DUK_INTERNAL void duk_debug_write_bytes(duk_hthread *thr, const duk_uint8_t *data, duk_size_t length) {
598 duk_heap *heap;
599 const duk_uint8_t *p;
600 duk_size_t left;
601 duk_size_t got;
602
603 DUK_ASSERT(thr != NULL);
604 DUK_ASSERT(length == 0 || data != NULL);
605 heap = thr->heap;
606 DUK_ASSERT(heap != NULL);
607
608 if (heap->dbg_write_cb == NULL) {
609 DUK_D(DUK_DPRINT("attempt to write %ld bytes in detached state, ignore", (long) length));
610 return;
611 }
612 if (length == 0) {
613 /* Avoid doing an actual write callback with length == 0,
614 * because that's reserved for a write flush.
615 */
616 return;
617 }
618 DUK_ASSERT(data != NULL);
619
620 p = data;
621 for (;;) {
622 left = (duk_size_t) ((data + length) - p);
623 if (left == 0) {
624 break;
625 }
626 DUK_ASSERT(heap->dbg_write_cb != NULL);
627 DUK_ASSERT(left >= 1);
628#if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE)
629 left = 1;
630#endif
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);
636 return;
637 }
638 p += got;
639 }
640}
641
642DUK_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);
644}
645
646DUK_INTERNAL void duk_debug_write_unused(duk_hthread *thr) {
647 duk_debug_write_byte(thr, DUK_DBG_IB_UNUSED);
648}
649
650DUK_INTERNAL void duk_debug_write_undefined(duk_hthread *thr) {
651 duk_debug_write_byte(thr, DUK_DBG_IB_UNDEFINED);
652}
653
654#if defined(DUK_USE_DEBUGGER_INSPECT)
655DUK_INTERNAL void duk_debug_write_null(duk_hthread *thr) {
656 duk_debug_write_byte(thr, DUK_DBG_IB_NULL);
657}
658#endif
659
660DUK_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);
662}
663
664/* Write signed 32-bit integer. */
665DUK_INTERNAL void duk_debug_write_int(duk_hthread *thr, duk_int32_t x) {
666 duk_uint8_t buf[5];
667 duk_size_t len;
668
669 DUK_ASSERT(thr != NULL);
670
671 if (x >= 0 && x <= 0x3fL) {
672 buf[0] = (duk_uint8_t) (0x80 + x);
673 len = 1;
674 } else if (x >= 0 && x <= 0x3fffL) {
675 buf[0] = (duk_uint8_t) (0xc0 + (x >> 8));
676 buf[1] = (duk_uint8_t) (x & 0xff);
677 len = 2;
678 } else {
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);
685 len = 5;
686 }
687 duk_debug_write_bytes(thr, buf, len);
688}
689
690/* Write unsigned 32-bit integer. */
691DUK_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.
698 */
699 if (x >= 0x80000000UL) {
700 DUK_D(DUK_DPRINT("writing unsigned integer 0x%08lx as signed integer",
701 (long) x));
702 }
703 duk_debug_write_int(thr, (duk_int32_t) x);
704}
705
706DUK_INTERNAL void duk_debug_write_strbuf(duk_hthread *thr, const char *data, duk_size_t length, duk_uint8_t marker_base) {
707 duk_uint8_t buf[5];
708 duk_size_t buflen;
709
710 DUK_ASSERT(thr != NULL);
711 DUK_ASSERT(length == 0 || data != NULL);
712
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);
716 buflen = 1;
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);
721 buflen = 3;
722 } else {
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);
728 buflen = 5;
729 }
730
731 duk_debug_write_bytes(thr, (const duk_uint8_t *) buf, buflen);
732 duk_debug_write_bytes(thr, (const duk_uint8_t *) data, length);
733}
734
735DUK_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);
737}
738
739DUK_INTERNAL void duk_debug_write_cstring(duk_hthread *thr, const char *data) {
740 DUK_ASSERT(thr != NULL);
741
742 duk_debug_write_string(thr,
743 data,
744 data ? DUK_STRLEN(data) : 0);
745}
746
747DUK_INTERNAL void duk_debug_write_hstring(duk_hthread *thr, duk_hstring *h) {
748 DUK_ASSERT(thr != NULL);
749
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));
754}
755
756DUK_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));
759}
760
761DUK_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);
763}
764
765DUK_INTERNAL void duk_debug_write_hbuffer(duk_hthread *thr, duk_hbuffer *h) {
766 DUK_ASSERT(thr != NULL);
767
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));
771}
772
773DUK_LOCAL void duk__debug_write_pointer_raw(duk_hthread *thr, void *ptr, duk_uint8_t ibyte) {
774 duk_uint8_t buf[2];
775 duk__ptr_union pu;
776
777 DUK_ASSERT(thr != NULL);
778 DUK_ASSERT(sizeof(ptr) >= 1 && sizeof(ptr) <= 16);
779 /* ptr may be NULL */
780
781 buf[0] = ibyte;
782 buf[1] = sizeof(pu);
783 duk_debug_write_bytes(thr, buf, 2);
784 pu.p = (void *) ptr;
785#if defined(DUK_USE_INTEGER_LE)
786 duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu));
787#endif
788 duk_debug_write_bytes(thr, (const duk_uint8_t *) &pu.p, (duk_size_t) sizeof(pu));
789}
790
791DUK_INTERNAL void duk_debug_write_pointer(duk_hthread *thr, void *ptr) {
792 duk__debug_write_pointer_raw(thr, ptr, DUK_DBG_IB_POINTER);
793}
794
795#if defined(DUK_USE_DEBUGGER_DUMPHEAP) || defined(DUK_USE_DEBUGGER_INSPECT)
796DUK_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);
798}
799#endif /* DUK_USE_DEBUGGER_DUMPHEAP || DUK_USE_DEBUGGER_INSPECT */
800
801DUK_INTERNAL void duk_debug_write_hobject(duk_hthread *thr, duk_hobject *obj) {
802 duk_uint8_t buf[3];
803 duk__ptr_union pu;
804
805 DUK_ASSERT(thr != NULL);
806 DUK_ASSERT(sizeof(obj) >= 1 && sizeof(obj) <= 16);
807 DUK_ASSERT(obj != NULL);
808
809 buf[0] = DUK_DBG_IB_OBJECT;
810 buf[1] = (duk_uint8_t) DUK_HOBJECT_GET_CLASS_NUMBER(obj);
811 buf[2] = sizeof(pu);
812 duk_debug_write_bytes(thr, buf, 3);
813 pu.p = (void *) obj;
814#if defined(DUK_USE_INTEGER_LE)
815 duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu));
816#endif
817 duk_debug_write_bytes(thr, (const duk_uint8_t *) &pu.p, (duk_size_t) sizeof(pu));
818}
819
820DUK_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;
823 duk_uint8_t buf[4];
824 duk_double_union du1;
825 duk_double_union du2;
826 duk_int32_t i32;
827
828 DUK_ASSERT(thr != NULL);
829 DUK_ASSERT(tv != NULL);
830
831 switch (DUK_TVAL_GET_TAG(tv)) {
832 case DUK_TAG_UNDEFINED:
833 duk_debug_write_byte(thr, DUK_DBG_IB_UNDEFINED);
834 break;
835 case DUK_TAG_UNUSED:
836 duk_debug_write_byte(thr, DUK_DBG_IB_UNUSED);
837 break;
838 case DUK_TAG_NULL:
839 duk_debug_write_byte(thr, DUK_DBG_IB_NULL);
840 break;
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));
845 break;
846 case DUK_TAG_POINTER:
847 duk_debug_write_pointer(thr, (void *) DUK_TVAL_GET_POINTER(tv));
848 break;
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));
857 break;
858 case DUK_TAG_STRING:
859 duk_debug_write_hstring(thr, DUK_TVAL_GET_STRING(tv));
860 break;
861 case DUK_TAG_OBJECT:
862 duk_debug_write_hobject(thr, DUK_TVAL_GET_OBJECT(tv));
863 break;
864 case DUK_TAG_BUFFER:
865 duk_debug_write_hbuffer(thr, DUK_TVAL_GET_BUFFER(tv));
866 break;
867#if defined(DUK_USE_FASTINT)
868 case DUK_TAG_FASTINT:
869#endif
870 default:
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.
874 *
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.
878 */
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;
884
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",
887 (long) i32,
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]));
896
897 if (DUK_MEMCMP((const void *) du1.uc, (const void *) du2.uc, sizeof(du1.uc)) == 0) {
898 duk_debug_write_int(thr, i32);
899 } else {
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));
903 }
904 }
905}
906
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.
910 */
911DUK_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);
915 } else {
916 duk_debug_write_tval(thr, tv);
917 }
918}
919#endif /* DUK_USE_DEBUGGER_DUMPHEAP */
920
921/*
922 * Debug connection message write helpers
923 */
924
925#if 0 /* unused */
926DUK_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);
929}
930#endif
931
932DUK_INTERNAL void duk_debug_write_reply(duk_hthread *thr) {
933 duk_debug_write_byte(thr, DUK_DBG_IB_REPLY);
934}
935
936DUK_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);
942}
943
944DUK_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);
947}
948
949DUK_INTERNAL void duk_debug_write_eom(duk_hthread *thr) {
950 duk_debug_write_byte(thr, DUK_DBG_IB_EOM);
951
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.
956 */
957 duk_debug_write_flush(thr);
958}
959
960/*
961 * Status message and helpers
962 */
963
964DUK_INTERNAL duk_uint_fast32_t duk_debug_curr_line(duk_hthread *thr) {
965 duk_context *ctx = (duk_context *) thr;
966 duk_activation *act;
967 duk_uint_fast32_t line;
968 duk_uint_fast32_t pc;
969
970 act = duk_hthread_get_current_activation(thr); /* may be NULL */
971 if (act == NULL) {
972 return 0;
973 }
974
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.)
980 */
981
982 pc = duk_hthread_get_act_curr_pc(thr, act);
983
984 /* XXX: this should be optimized to be a raw query and avoid valstack
985 * operations if possible.
986 */
987 duk_push_tval(ctx, &act->tv_func);
988 line = duk_hobject_pc2line_query(ctx, -1, pc);
989 duk_pop(ctx);
990 return line;
991}
992
993DUK_INTERNAL void duk_debug_send_status(duk_hthread *thr) {
994 duk_context *ctx = (duk_context *) thr;
995 duk_activation *act;
996
997 duk_debug_write_notify(thr, DUK_DBG_CMD_STATUS);
998 duk_debug_write_int(thr, thr->heap->dbg_paused);
999
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);
1006 } else {
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);
1013 duk_pop_3(ctx);
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));
1017 }
1018
1019 duk_debug_write_eom(thr);
1020}
1021
1022#if defined(DUK_USE_DEBUGGER_THROW_NOTIFY)
1023DUK_INTERNAL void duk_debug_send_throw(duk_hthread *thr, duk_bool_t fatal) {
1024 /*
1025 * NFY <int: 5> <int: fatal> <str: msg> <str: filename> <int: linenumber> EOM
1026 */
1027
1028 duk_context *ctx = (duk_context *) thr;
1029 duk_activation *act;
1030 duk_uint32_t pc;
1031
1032 DUK_ASSERT(thr->valstack_top > thr->valstack); /* At least: ... [err] */
1033
1034 duk_debug_write_notify(thr, DUK_DBG_CMD_THROW);
1035 duk_debug_write_int(thr, fatal);
1036
1037 /* Report thrown value to client coerced to string */
1038 duk_dup(ctx, -1);
1039 duk__debug_write_hstring_safe_top(thr);
1040 duk_pop(ctx);
1041
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));
1048 } else {
1049 /* For anything other than an Error instance, we calculate the error
1050 * location directly from the current activation.
1051 */
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));
1058 }
1059 duk_pop_2(ctx); /* shared pop */
1060
1061 duk_debug_write_eom(thr);
1062}
1063#endif /* DUK_USE_DEBUGGER_THROW_NOTIFY */
1064
1065/*
1066 * Debug message processing
1067 */
1068
1069/* Skip dvalue. */
1070DUK_LOCAL duk_bool_t duk__debug_skip_dvalue(duk_hthread *thr) {
1071 duk_uint8_t x;
1072 duk_uint32_t len;
1073
1074 x = duk_debug_read_byte(thr);
1075
1076 if (x >= 0xc0) {
1077 duk_debug_skip_byte(thr);
1078 return 0;
1079 }
1080 if (x >= 0x80) {
1081 return 0;
1082 }
1083 if (x >= 0x60) {
1084 duk_debug_skip_bytes(thr, x - 0x60);
1085 return 0;
1086 }
1087 switch(x) {
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:
1094 break;
1095 case DUK_DBG_IB_INT4:
1096 (void) duk__debug_read_uint32_raw(thr);
1097 break;
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);
1102 break;
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);
1107 break;
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:
1113 break;
1114 case DUK_DBG_IB_NUMBER:
1115 duk_debug_skip_bytes(thr, 8);
1116 break;
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);
1121 break;
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);
1126 break;
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);
1131 break;
1132 default:
1133 goto fail;
1134 }
1135
1136 return 0;
1137
1138 fail:
1139 DUK__SET_CONN_BROKEN(thr, 1);
1140 return 1; /* Pretend like we got EOM */
1141}
1142
1143/* Skip dvalues to EOM. */
1144DUK_LOCAL void duk__debug_skip_to_eom(duk_hthread *thr) {
1145 for (;;) {
1146 if (duk__debug_skip_dvalue(thr)) {
1147 break;
1148 }
1149 }
1150}
1151
1152/*
1153 * Simple commands
1154 */
1155
1156DUK_LOCAL void duk__debug_handle_basic_info(duk_hthread *thr, duk_heap *heap) {
1157 DUK_UNREF(heap);
1158 DUK_D(DUK_DPRINT("debug command Version"));
1159
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);
1170#else
1171 duk_debug_write_int(thr, 0);
1172#endif
1173 duk_debug_write_int(thr, (duk_int_t) sizeof(void *));
1174 duk_debug_write_eom(thr);
1175}
1176
1177DUK_LOCAL void duk__debug_handle_trigger_status(duk_hthread *thr, duk_heap *heap) {
1178 DUK_UNREF(heap);
1179 DUK_D(DUK_DPRINT("debug command TriggerStatus"));
1180
1181 duk_debug_write_reply(thr);
1182 duk_debug_write_eom(thr);
1183 heap->dbg_state_dirty = 1;
1184}
1185
1186DUK_LOCAL void duk__debug_handle_pause(duk_hthread *thr, duk_heap *heap) {
1187 DUK_D(DUK_DPRINT("debug command Pause"));
1188
1189 DUK_HEAP_SET_PAUSED(heap);
1190 duk_debug_write_reply(thr);
1191 duk_debug_write_eom(thr);
1192}
1193
1194DUK_LOCAL void duk__debug_handle_resume(duk_hthread *thr, duk_heap *heap) {
1195 DUK_D(DUK_DPRINT("debug command Resume"));
1196
1197 DUK_HEAP_CLEAR_PAUSED(heap);
1198 duk_debug_write_reply(thr);
1199 duk_debug_write_eom(thr);
1200}
1201
1202DUK_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;
1205
1206 DUK_D(DUK_DPRINT("debug command StepInto/StepOver/StepOut: %d", (int) cmd));
1207
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;
1212 } else {
1213 DUK_ASSERT(cmd == DUK_DBG_CMD_STEPOUT);
1214 step_type = DUK_STEP_TYPE_OUT;
1215 }
1216
1217 line = duk_debug_curr_line(thr);
1218 if (line > 0) {
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;
1225 } else {
1226 DUK_D(DUK_DPRINT("cannot determine current line, stepinto/stepover/stepout ignored"));
1227 }
1228 duk_debug_write_reply(thr);
1229 duk_debug_write_eom(thr);
1230}
1231
1232DUK_LOCAL void duk__debug_handle_list_break(duk_hthread *thr, duk_heap *heap) {
1233 duk_small_int_t i;
1234
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);
1240 }
1241 duk_debug_write_eom(thr);
1242}
1243
1244DUK_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;
1248
1249 DUK_UNREF(heap);
1250
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);
1255 if (idx >= 0) {
1256 duk_debug_write_reply(thr);
1257 duk_debug_write_int(thr, (duk_int32_t) idx);
1258 duk_debug_write_eom(thr);
1259 } else {
1260 duk_debug_write_error_eom(thr, DUK_DBG_ERR_TOOMANY, "no space for breakpoint");
1261 }
1262}
1263
1264DUK_LOCAL void duk__debug_handle_del_break(duk_hthread *thr, duk_heap *heap) {
1265 duk_small_uint_t idx;
1266
1267 DUK_UNREF(heap);
1268
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);
1274 } else {
1275 duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid breakpoint index");
1276 }
1277}
1278
1279DUK_LOCAL void duk__debug_handle_get_var(duk_hthread *thr, duk_heap *heap) {
1280 duk_context *ctx = (duk_context *) thr;
1281 duk_hstring *str;
1282 duk_bool_t rc;
1283 duk_int32_t level;
1284
1285 DUK_UNREF(heap);
1286 DUK_D(DUK_DPRINT("debug command GetVar"));
1287
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");
1295 return;
1296 }
1297 } else {
1298 level = -1;
1299 }
1300
1301 if (thr->callstack_top > 0) {
1302 rc = duk_js_getvar_activation(thr,
1303 thr->callstack + thr->callstack_top + level,
1304 str,
1305 0);
1306 } else {
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.
1310 */
1311 DUK_D(DUK_DPRINT("callstack empty, no activation -> ignore getvar"));
1312 rc = 0;
1313 }
1314
1315 duk_debug_write_reply(thr);
1316 if (rc) {
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));
1320 } else {
1321 duk_debug_write_int(thr, 0);
1322 duk_debug_write_unused(thr);
1323 }
1324 duk_debug_write_eom(thr);
1325}
1326
1327DUK_LOCAL void duk__debug_handle_put_var(duk_hthread *thr, duk_heap *heap) {
1328 duk_hstring *str;
1329 duk_tval *tv;
1330 duk_int32_t level;
1331
1332 DUK_UNREF(heap);
1333 DUK_D(DUK_DPRINT("debug command PutVar"));
1334
1335 str = duk_debug_read_hstring(thr); /* push to stack */
1336 DUK_ASSERT(str != NULL);
1337 tv = duk_debug_read_tval(thr);
1338 if (tv == NULL) {
1339 /* detached */
1340 return;
1341 }
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");
1347 return;
1348 }
1349 } else {
1350 level = -1;
1351 }
1352
1353 if (thr->callstack_top > 0) {
1354 duk_js_putvar_activation(thr,
1355 thr->callstack + thr->callstack_top + level,
1356 str,
1357 tv,
1358 0);
1359 } else {
1360 DUK_D(DUK_DPRINT("callstack empty, no activation -> ignore putvar"));
1361 }
1362
1363 /* XXX: Current putvar implementation doesn't have a success flag,
1364 * add one and send to debug client?
1365 */
1366 duk_debug_write_reply(thr);
1367 duk_debug_write_eom(thr);
1368}
1369
1370DUK_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;
1376 duk_size_t i;
1377
1378 DUK_ASSERT(thr != NULL);
1379 DUK_UNREF(heap);
1380
1381 duk_debug_write_reply(thr);
1382 while (curr_thr != NULL) {
1383 i = curr_thr->callstack_top;
1384 while (i > 0) {
1385 i--;
1386 curr_act = curr_thr->callstack + i;
1387
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
1396 */
1397
1398 /* XXX: optimize to use direct reads, i.e. avoid
1399 * value stack operations.
1400 */
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) {
1408 pc--;
1409 }
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);
1413 duk_pop_3(ctx);
1414 }
1415 curr_thr = curr_thr->resumer;
1416 }
1417 /* SCANBUILD: warning about 'thr' potentially being NULL here,
1418 * warning is incorrect because thr != NULL always here.
1419 */
1420 duk_debug_write_eom(thr);
1421}
1422
1423DUK_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;
1426 duk_int32_t level;
1427 duk_hstring *varname;
1428
1429 DUK_UNREF(heap);
1430
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");
1436 return;
1437 }
1438 duk_debug_write_reply(thr);
1439 } else {
1440 duk_debug_write_reply(thr);
1441 if (thr->callstack_top == 0) {
1442 goto callstack_empty;
1443 }
1444 level = -1;
1445 }
1446
1447 curr_act = thr->callstack + thr->callstack_top + level;
1448
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
1453 */
1454
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);
1462
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 ] */
1468 }
1469 } else {
1470 DUK_D(DUK_DPRINT("varmap is not an object in GetLocals, ignore"));
1471 }
1472
1473 callstack_empty:
1474 duk_debug_write_eom(thr);
1475}
1476
1477DUK_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;
1480 duk_int_t call_ret;
1481 duk_small_int_t eval_err;
1482 duk_int32_t level;
1483
1484 DUK_UNREF(heap);
1485
1486 DUK_D(DUK_DPRINT("debug command Eval"));
1487
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'.
1491 *
1492 * Callstack level for debug commands only affects scope -- the callstack
1493 * as seen by, e.g. Duktape.act() will be the same regardless.
1494 */
1495
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 */
1499
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");
1506 return;
1507 }
1508 }
1509 else {
1510 level = -1;
1511 }
1512 DUK_ASSERT(level < 0 && -level <= (duk_int32_t) thr->callstack_top);
1513 duk_push_int(ctx, level - 1); /* compensate for eval() call */
1514
1515 /* [ ... eval "eval" eval_input level ] */
1516
1517 call_flags = 0;
1518 if (thr->callstack_top >= (duk_size_t) -level) {
1519 duk_activation *act;
1520 duk_hobject *fun;
1521
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.
1529 */
1530 call_flags |= DUK_CALL_FLAG_DIRECT_EVAL;
1531 }
1532 }
1533
1534 call_ret = duk_handle_call_protected(thr, 2 /*num_stack_args*/, call_flags);
1535
1536 if (call_ret == DUK_EXEC_SUCCESS) {
1537 eval_err = 0;
1538 /* Use result value as is. */
1539 } else {
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.
1543 */
1544 eval_err = 1;
1545 duk_safe_to_string(ctx, -1);
1546 }
1547
1548 /* [ ... result ] */
1549
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);
1555}
1556
1557DUK_LOCAL void duk__debug_handle_detach(duk_hthread *thr, duk_heap *heap) {
1558 DUK_UNREF(heap);
1559 DUK_D(DUK_DPRINT("debug command Detach"));
1560
1561 duk_debug_write_reply(thr);
1562 duk_debug_write_eom(thr);
1563
1564 DUK_D(DUK_DPRINT("debug connection detached, mark broken"));
1565 DUK__SET_CONN_BROKEN(thr, 0); /* not an error */
1566}
1567
1568DUK_LOCAL void duk__debug_handle_apprequest(duk_hthread *thr, duk_heap *heap) {
1569 duk_context *ctx = (duk_context *) thr;
1570 duk_idx_t old_top;
1571
1572 DUK_D(DUK_DPRINT("debug command AppRequest"));
1573
1574 old_top = duk_get_top(ctx); /* save stack top */
1575
1576 if (heap->dbg_request_cb != NULL) {
1577 duk_idx_t nrets;
1578 duk_idx_t nvalues = 0;
1579 duk_idx_t top, idx;
1580
1581 /* Read tvals from the message and push them onto the valstack,
1582 * then call the request callback to process the request.
1583 */
1584 while (duk_debug_peek_byte(thr) != DUK_DBG_IB_EOM) {
1585 duk_tval *tv;
1586 if (!duk_check_stack(ctx, 1)) {
1587 DUK_D(DUK_DPRINT("failed to allocate space for request dvalue(s)"));
1588 goto fail;
1589 }
1590 tv = duk_debug_read_tval(thr); /* push to stack */
1591 if (tv == NULL) {
1592 /* detached */
1593 return;
1594 }
1595 nvalues++;
1596 }
1597 DUK_ASSERT(duk_get_top(ctx) == old_top + nvalues);
1598
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)));
1605 if (nrets >= 0) {
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));
1612 goto fail;
1613 }
1614
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));
1620 }
1621 duk_debug_write_eom(thr);
1622 } else {
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"));
1626 goto fail;
1627 }
1628 duk_debug_write_error_eom(thr, DUK_DBG_ERR_APPLICATION, duk_get_string(ctx, -1));
1629 }
1630
1631 duk_set_top(ctx, old_top); /* restore stack top */
1632 } else {
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");
1635 }
1636
1637 return;
1638
1639 fail:
1640 duk_set_top(ctx, old_top); /* restore stack top */
1641 DUK__SET_CONN_BROKEN(thr, 1);
1642}
1643
1644/*
1645 * DumpHeap command
1646 */
1647
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?
1651 */
1652DUK_LOCAL void duk__debug_dump_heaphdr(duk_hthread *thr, duk_heap *heap, duk_heaphdr *hdr) {
1653 DUK_UNREF(heap);
1654
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));
1660#else
1661 duk_debug_write_int(thr, (duk_int32_t) -1);
1662#endif
1663
1664 switch (DUK_HEAPHDR_GET_TYPE(hdr)) {
1665 case DUK_HTYPE_STRING: {
1666 duk_hstring *h = (duk_hstring *) hdr;
1667
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);
1672 break;
1673 }
1674 case DUK_HTYPE_OBJECT: {
1675 duk_hobject *h = (duk_hobject *) hdr;
1676 duk_hstring *k;
1677 duk_uint_fast32_t i;
1678
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));
1685
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);
1690 if (k == NULL) {
1691 duk_debug_write_int(thr, 0); /* isAccessor */
1692 duk_debug_write_unused(thr);
1693 continue;
1694 }
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);
1699 } else {
1700 duk_debug_write_int(thr, 0); /* isAccessor */
1701
1702 duk__debug_write_tval_heapptr(thr, &DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->v);
1703 }
1704 }
1705
1706 for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) {
1707 /* Note: array dump will include elements beyond
1708 * 'length'.
1709 */
1710 duk__debug_write_tval_heapptr(thr, DUK_HOBJECT_A_GET_VALUE_PTR(heap, h, i));
1711 }
1712 break;
1713 }
1714 case DUK_HTYPE_BUFFER: {
1715 duk_hbuffer *h = (duk_hbuffer *) hdr;
1716
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));
1719 break;
1720 }
1721 default: {
1722 DUK_D(DUK_DPRINT("invalid htype: %d", (int) DUK_HEAPHDR_GET_TYPE(hdr)));
1723 }
1724 }
1725}
1726
1727DUK_LOCAL void duk__debug_dump_heap_allocated(duk_hthread *thr, duk_heap *heap) {
1728 duk_heaphdr *hdr;
1729
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);
1734 }
1735}
1736
1737#if defined(DUK_USE_STRTAB_CHAIN)
1738DUK_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)
1742 duk_uint16_t *lst;
1743#else
1744 duk_hstring **lst;
1745#endif
1746 duk_hstring *h;
1747
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);
1753#else
1754 lst = e->u.strlist;
1755#endif
1756 DUK_ASSERT(lst != NULL);
1757
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]);
1761#else
1762 h = lst[j];
1763#endif
1764 if (h != NULL) {
1765 duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h);
1766 }
1767 }
1768 } else {
1769#if defined(DUK_USE_HEAPPTR16)
1770 h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.str16);
1771#else
1772 h = e->u.str;
1773#endif
1774 if (h != NULL) {
1775 duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h);
1776 }
1777 }
1778 }
1779}
1780#endif /* DUK_USE_STRTAB_CHAIN */
1781
1782#if defined(DUK_USE_STRTAB_PROBE)
1783DUK_LOCAL void duk__debug_dump_strtab_probe(duk_hthread *thr, duk_heap *heap) {
1784 duk_uint32_t i;
1785 duk_hstring *h;
1786
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]);
1790#else
1791 h = heap->strtable[i];
1792#endif
1793 if (h == NULL || h == DUK_STRTAB_DELETED_MARKER(heap)) {
1794 continue;
1795 }
1796
1797 duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h);
1798 }
1799}
1800#endif /* DUK_USE_STRTAB_PROBE */
1801
1802DUK_LOCAL void duk__debug_handle_dump_heap(duk_hthread *thr, duk_heap *heap) {
1803 DUK_D(DUK_DPRINT("debug command DumpHeap"));
1804
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);
1809#endif
1810#if defined(DUK_USE_STRTAB_PROBE)
1811 duk__debug_dump_strtab_probe(thr, heap);
1812#endif
1813 duk_debug_write_eom(thr);
1814}
1815#endif /* DUK_USE_DEBUGGER_DUMPHEAP */
1816
1817DUK_LOCAL void duk__debug_handle_get_bytecode(duk_hthread *thr, duk_heap *heap) {
1818 duk_activation *act;
1819 duk_hcompiledfunction *fun = NULL;
1820 duk_size_t i, n;
1821 duk_tval *tv;
1822 duk_hobject **fn;
1823 duk_int32_t level = -1;
1824 duk_uint8_t ibyte;
1825
1826 DUK_UNREF(heap);
1827
1828 DUK_D(DUK_DPRINT("debug command GetBytecode"));
1829
1830 ibyte = duk_debug_peek_byte(thr);
1831 if (ibyte != DUK_DBG_IB_EOM) {
1832 tv = duk_debug_read_tval(thr);
1833 if (tv == NULL) {
1834 /* detached */
1835 return;
1836 }
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);
1843 } else {
1844 DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!T", tv));
1845 goto fail_args;
1846 }
1847 }
1848
1849 if (fun == NULL) {
1850 if (level >= 0 || -level > (duk_int32_t) thr->callstack_top) {
1851 DUK_D(DUK_DPRINT("invalid callstack level for GetBytecode"));
1852 goto fail_level;
1853 }
1854 act = thr->callstack + thr->callstack_top + level;
1855 fun = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act);
1856 }
1857
1858 if (fun == NULL || !DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) fun)) {
1859 DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!O", fun));
1860 goto fail_args;
1861 }
1862 DUK_ASSERT(fun != NULL && DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) fun));
1863
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);
1870 tv++;
1871 }
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);
1877 fn++;
1878 }
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);
1883 return;
1884
1885 fail_args:
1886 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid argument");
1887 return;
1888
1889 fail_level:
1890 duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid callstack level");
1891 return;
1892}
1893
1894/*
1895 * Object inspection commands: GetHeapObjInfo, GetObjPropDesc,
1896 * GetObjPropDescRange
1897 */
1898
1899#if defined(DUK_USE_DEBUGGER_INSPECT)
1900
1901#if 0 /* pruned */
1902DUK_LOCAL const char * const duk__debug_getinfo_heaphdr_keys[] = {
1903 "reachable",
1904 "temproot",
1905 "finalizable",
1906 "finalized",
1907 "readonly"
1908 /* NULL not needed here */
1909};
1910DUK_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,
1916 0 /* terminator */
1917};
1918#endif
1919DUK_LOCAL const char * const duk__debug_getinfo_hstring_keys[] = {
1920#if 0
1921 "arridx",
1922 "internal",
1923 "reserved_word",
1924 "strict_reserved_word",
1925 "eval_or_arguments",
1926#endif
1927 "extdata"
1928 /* NULL not needed here */
1929};
1930DUK_LOCAL duk_uint_t duk__debug_getinfo_hstring_masks[] = {
1931#if 0
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,
1937#endif
1938 DUK_HSTRING_FLAG_EXTDATA,
1939 0 /* terminator */
1940};
1941DUK_LOCAL const char * const duk__debug_getinfo_hobject_keys[] = {
1942 "extensible",
1943 "constructable",
1944 "bound",
1945 "compiledfunction",
1946 "nativefunction",
1947 "bufferobject",
1948 "thread",
1949 "array_part",
1950 "strict",
1951 "notail",
1952 "newenv",
1953 "namebinding",
1954 "createargs",
1955 "envrecclosed",
1956 "exotic_array",
1957 "exotic_stringobj",
1958 "exotic_arguments",
1959 "exotic_dukfunc",
1960 "exotic_proxyobj"
1961 /* NULL not needed here */
1962};
1963DUK_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,
1983 0 /* terminator */
1984};
1985DUK_LOCAL const char * const duk__debug_getinfo_hbuffer_keys[] = {
1986 "dynamic",
1987 "external"
1988 /* NULL not needed here */
1989};
1990DUK_LOCAL duk_uint_t duk__debug_getinfo_hbuffer_masks[] = {
1991 DUK_HBUFFER_FLAG_DYNAMIC,
1992 DUK_HBUFFER_FLAG_EXTERNAL,
1993 0 /* terminator */
1994};
1995
1996DUK_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);
1999}
2000
2001DUK_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);
2005}
2006
2007DUK_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);
2011}
2012
2013DUK_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);
2017}
2018
2019DUK_LOCAL void duk__debug_getinfo_bitmask(duk_hthread *thr, const char * const * keys, duk_uint_t *masks, duk_uint_t flags) {
2020 const char *key;
2021 duk_uint_t mask;
2022
2023 for (;;) {
2024 mask = *masks++;
2025 if (!mask) {
2026 break;
2027 }
2028 key = *keys++;
2029 DUK_ASSERT(key != NULL);
2030
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);
2033 }
2034}
2035
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'.
2040 */
2041DUK_LOCAL duk_bool_t duk__debug_getprop_index(duk_hthread *thr, duk_heap *heap, duk_hobject *h_obj, duk_uint_t idx) {
2042 duk_uint_t a_size;
2043 duk_tval *tv;
2044 duk_hstring *h_key;
2045 duk_hobject *h_getset;
2046 duk_uint_t flags;
2047
2048 DUK_UNREF(heap);
2049
2050 a_size = DUK_HOBJECT_GET_ASIZE(h_obj);
2051 if (idx < a_size) {
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);
2056 return 1;
2057 }
2058
2059 idx -= a_size;
2060 if (idx >= DUK_HOBJECT_GET_ENEXT(h_obj)) {
2061 return 0;
2062 }
2063
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);
2069 return 1;
2070 }
2071
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;
2075 }
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);
2080 if (h_getset) {
2081 duk_debug_write_hobject(thr, h_getset);
2082 } else {
2083 duk_debug_write_null(thr);
2084 }
2085 h_getset = DUK_HOBJECT_E_GET_VALUE_SETTER(heap, h_obj, idx);
2086 if (h_getset) {
2087 duk_debug_write_hobject(thr, h_getset);
2088 } else {
2089 duk_debug_write_null(thr);
2090 }
2091 } else {
2092 tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, h_obj, idx);
2093 duk_debug_write_tval(thr, tv);
2094 }
2095 return 1;
2096}
2097
2098DUK_LOCAL void duk__debug_handle_get_heap_obj_info(duk_hthread *thr, duk_heap *heap) {
2099 duk_heaphdr *h;
2100
2101 DUK_D(DUK_DPRINT("debug command GetHeapObjInfo"));
2102 DUK_UNREF(heap);
2103
2104 h = duk_debug_read_any_ptr(thr);
2105 if (!h) {
2106 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid target");
2107 return;
2108 }
2109
2110 duk_debug_write_reply(thr);
2111
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.
2115 */
2116
2117 duk__debug_getinfo_flags_key(thr, "heapptr");
2118 duk_debug_write_heapptr(thr, h);
2119
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));
2125#endif
2126#if 0 /* pruned */
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));
2131#endif
2132
2133 switch (DUK_HEAPHDR_GET_TYPE(h)) {
2134 case DUK_HTYPE_STRING: {
2135 duk_hstring *h_str;
2136
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);
2147 break;
2148 }
2149 case DUK_HTYPE_OBJECT: {
2150 duk_hobject *h_obj;
2151 duk_hobject *h_proto;
2152
2153 h_obj = (duk_hobject *) h;
2154 h_proto = DUK_HOBJECT_GET_PROTOTYPE(heap, h_obj);
2155
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);
2167 } else {
2168 duk_debug_write_null(thr);
2169 }
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));
2176
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;
2181
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).
2187 */
2188 duk__debug_getinfo_flags_key(thr, "funcptr");
2189 duk_debug_write_buffer(thr, (const char *) &h_fun->func, sizeof(h_fun->func));
2190 }
2191
2192 if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) {
2193 duk_hcompiledfunction *h_fun;
2194 duk_hbuffer *h_buf;
2195 h_fun = (duk_hcompiledfunction *) h_obj;
2196
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);
2205 }
2206 }
2207
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.
2211 */
2212 duk_hthread *h_thr;
2213 h_thr = (duk_hthread *) h_obj;
2214 DUK_UNREF(h_thr);
2215 }
2216
2217 if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj)) {
2218 duk_hbufferobject *h_bufobj;
2219 h_bufobj = (duk_hbufferobject *) h_obj;
2220
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);
2229 }
2230 }
2231 break;
2232 }
2233 case DUK_HTYPE_BUFFER: {
2234 duk_hbuffer *h_buf;
2235
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 */
2246 break;
2247 }
2248 default: {
2249 /* Since we already started writing the reply, just emit nothing. */
2250 DUK_D(DUK_DPRINT("inspect target pointer has invalid heaphdr type"));
2251 }
2252 }
2253
2254 duk_debug_write_eom(thr);
2255}
2256
2257DUK_LOCAL void duk__debug_handle_get_obj_prop_desc(duk_hthread *thr, duk_heap *heap) {
2258 duk_heaphdr *h;
2259 duk_hobject *h_obj;
2260 duk_hstring *h_key;
2261 duk_propdesc desc;
2262
2263 DUK_D(DUK_DPRINT("debug command GetObjPropDesc"));
2264 DUK_UNREF(heap);
2265
2266 h = duk_debug_read_any_ptr(thr);
2267 if (!h) {
2268 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid target");
2269 return;
2270 }
2271 h_key = duk_debug_read_hstring(thr);
2272 if (h == NULL || DUK_HEAPHDR_GET_TYPE(h) != DUK_HTYPE_OBJECT || h_key == NULL) {
2273 goto fail_args;
2274 }
2275 h_obj = (duk_hobject *) h;
2276
2277 if (duk_hobject_get_own_propdesc(thr, h_obj, h_key, &desc, 0 /*flags*/)) {
2278 duk_int_t virtual_idx;
2279 duk_bool_t rc;
2280
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);
2285
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);
2289 DUK_UNREF(rc);
2290 duk_debug_write_eom(thr);
2291 } else {
2292 duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "not found");
2293 }
2294 return;
2295
2296 fail_args:
2297 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid args");
2298}
2299
2300DUK_LOCAL void duk__debug_handle_get_obj_prop_desc_range(duk_hthread *thr, duk_heap *heap) {
2301 duk_heaphdr *h;
2302 duk_hobject *h_obj;
2303 duk_uint_t idx, idx_start, idx_end;
2304
2305 DUK_D(DUK_DPRINT("debug command GetObjPropDescRange"));
2306 DUK_UNREF(heap);
2307
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) {
2312 goto fail_args;
2313 }
2314 h_obj = (duk_hobject *) h;
2315
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
2320 * array .length is.
2321 */
2322
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)) {
2326 break;
2327 }
2328 }
2329 duk_debug_write_eom(thr);
2330 return;
2331
2332 fail_args:
2333 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid args");
2334}
2335
2336#endif /* DUK_USE_DEBUGGER_INSPECT */
2337
2338/*
2339 * Process incoming debug requests
2340 *
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
2343 * automatically.
2344 */
2345
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.
2349 */
2350DUK_LOCAL void duk__debug_process_message(duk_hthread *thr) {
2351 duk_context *ctx = (duk_context *) thr;
2352 duk_heap *heap;
2353 duk_uint8_t x;
2354 duk_int32_t cmd;
2355 duk_idx_t entry_top;
2356
2357 DUK_ASSERT(thr != NULL);
2358 heap = thr->heap;
2359 DUK_ASSERT(heap != NULL);
2360 DUK_UNREF(ctx);
2361
2362 entry_top = duk_get_top(ctx);
2363
2364 x = duk_debug_read_byte(thr);
2365 switch (x) {
2366 case DUK_DBG_IB_REQUEST: {
2367 cmd = duk_debug_read_int(thr);
2368 switch (cmd) {
2369 case DUK_DBG_CMD_BASICINFO: {
2370 duk__debug_handle_basic_info(thr, heap);
2371 break;
2372 }
2373 case DUK_DBG_CMD_TRIGGERSTATUS: {
2374 duk__debug_handle_trigger_status(thr, heap);
2375 break;
2376 }
2377 case DUK_DBG_CMD_PAUSE: {
2378 duk__debug_handle_pause(thr, heap);
2379 break;
2380 }
2381 case DUK_DBG_CMD_RESUME: {
2382 duk__debug_handle_resume(thr, heap);
2383 break;
2384 }
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);
2389 break;
2390 }
2391 case DUK_DBG_CMD_LISTBREAK: {
2392 duk__debug_handle_list_break(thr, heap);
2393 break;
2394 }
2395 case DUK_DBG_CMD_ADDBREAK: {
2396 duk__debug_handle_add_break(thr, heap);
2397 break;
2398 }
2399 case DUK_DBG_CMD_DELBREAK: {
2400 duk__debug_handle_del_break(thr, heap);
2401 break;
2402 }
2403 case DUK_DBG_CMD_GETVAR: {
2404 duk__debug_handle_get_var(thr, heap);
2405 break;
2406 }
2407 case DUK_DBG_CMD_PUTVAR: {
2408 duk__debug_handle_put_var(thr, heap);
2409 break;
2410 }
2411 case DUK_DBG_CMD_GETCALLSTACK: {
2412 duk__debug_handle_get_call_stack(thr, heap);
2413 break;
2414 }
2415 case DUK_DBG_CMD_GETLOCALS: {
2416 duk__debug_handle_get_locals(thr, heap);
2417 break;
2418 }
2419 case DUK_DBG_CMD_EVAL: {
2420 duk__debug_handle_eval(thr, heap);
2421 break;
2422 }
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).
2427 */
2428 duk__debug_handle_detach(thr, heap);
2429 break;
2430 }
2431#if defined(DUK_USE_DEBUGGER_DUMPHEAP)
2432 case DUK_DBG_CMD_DUMPHEAP: {
2433 duk__debug_handle_dump_heap(thr, heap);
2434 break;
2435 }
2436#endif /* DUK_USE_DEBUGGER_DUMPHEAP */
2437 case DUK_DBG_CMD_GETBYTECODE: {
2438 duk__debug_handle_get_bytecode(thr, heap);
2439 break;
2440 }
2441 case DUK_DBG_CMD_APPREQUEST: {
2442 duk__debug_handle_apprequest(thr, heap);
2443 break;
2444 }
2445#if defined(DUK_USE_DEBUGGER_INSPECT)
2446 case DUK_DBG_CMD_GETHEAPOBJINFO: {
2447 duk__debug_handle_get_heap_obj_info(thr, heap);
2448 break;
2449 }
2450 case DUK_DBG_CMD_GETOBJPROPDESC: {
2451 duk__debug_handle_get_obj_prop_desc(thr, heap);
2452 break;
2453 }
2454 case DUK_DBG_CMD_GETOBJPROPDESCRANGE: {
2455 duk__debug_handle_get_obj_prop_desc_range(thr, heap);
2456 break;
2457 }
2458#endif /* DUK_USE_DEBUGGER_INSPECT */
2459 default: {
2460 DUK_D(DUK_DPRINT("debug command unsupported: %d", (int) cmd));
2461 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "unsupported command");
2462 }
2463 } /* switch cmd */
2464 break;
2465 }
2466 case DUK_DBG_IB_REPLY: {
2467 DUK_D(DUK_DPRINT("debug reply, skipping"));
2468 break;
2469 }
2470 case DUK_DBG_IB_ERROR: {
2471 DUK_D(DUK_DPRINT("debug error, skipping"));
2472 break;
2473 }
2474 case DUK_DBG_IB_NOTIFY: {
2475 DUK_D(DUK_DPRINT("debug notify, skipping"));
2476 break;
2477 }
2478 default: {
2479 DUK_D(DUK_DPRINT("invalid initial byte, drop connection: %d", (int) x));
2480 goto fail;
2481 }
2482 } /* switch initial byte */
2483
2484 DUK_ASSERT(duk_get_top(ctx) >= entry_top);
2485 duk_set_top(ctx, entry_top);
2486 duk__debug_skip_to_eom(thr);
2487 return;
2488
2489 fail:
2490 DUK_ASSERT(duk_get_top(ctx) >= entry_top);
2491 duk_set_top(ctx, entry_top);
2492 DUK__SET_CONN_BROKEN(thr, 1);
2493 return;
2494}
2495
2496DUK_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;
2500 }
2501}
2502
2503DUK_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;
2507#endif
2508 duk_bool_t retval = 0;
2509
2510 DUK_ASSERT(thr != NULL);
2511 DUK_UNREF(ctx);
2512 DUK_ASSERT(thr->heap != NULL);
2513#if defined(DUK_USE_ASSERTIONS)
2514 entry_top = duk_get_top(ctx);
2515#endif
2516
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)));
2521
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.
2524 */
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;
2528
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.
2532 */
2533 duk__check_resend_status(thr);
2534
2535 for (;;) {
2536 /* Process messages until we're no longer paused or we peek
2537 * and see there's nothing to read right now.
2538 */
2539 DUK_DD(DUK_DDPRINT("top at loop top: %ld", (long) duk_get_top(ctx)));
2540 DUK_ASSERT(thr->heap->dbg_processing == 1);
2541
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.
2548 *
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
2556 * fully detached.
2557 *
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.
2561 */
2562
2563 DUK_D(DUK_DPRINT("detach pending (dbg_read_cb == NULL, dbg_detaching != 0), call detach2"));
2564
2565 duk__debug_do_detach2(thr->heap);
2566 thr->heap->dbg_processing = 1; /* may be set to 0 by duk_debugger_attach() inside callback */
2567
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));
2570 }
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 */
2573
2574 if (thr->heap->dbg_read_cb == NULL) {
2575 DUK_D(DUK_DPRINT("debug connection broken (and not detaching), stop processing messages"));
2576 break;
2577 }
2578
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
2583 * loop is correct.
2584 */
2585 DUK_D(DUK_DPRINT("processing debug message, peek indicated no data, stop processing messages"));
2586 break;
2587 }
2588 DUK_D(DUK_DPRINT("processing debug message, peek indicated there is data, handle it"));
2589 } else {
2590 DUK_D(DUK_DPRINT("paused, process debug message, blocking if necessary"));
2591 }
2592
2593 duk__check_resend_status(thr);
2594 duk__debug_process_message(thr);
2595 duk__check_resend_status(thr);
2596
2597 retval = 1; /* processed one or more messages */
2598 }
2599
2600 DUK_ASSERT(thr->heap->dbg_detaching == 0);
2601 DUK_ASSERT(thr->heap->dbg_processing == 1);
2602 thr->heap->dbg_processing = 0;
2603
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).
2606 */
2607 duk_debug_read_flush(thr); /* this cannot initiate a detach */
2608 DUK_ASSERT(thr->heap->dbg_detaching == 0);
2609
2610 DUK_DD(DUK_DDPRINT("top at exit: %ld", (long) duk_get_top(ctx)));
2611
2612#if defined(DUK_USE_ASSERTIONS)
2613 /* Easy to get wrong, so assert for it. */
2614 DUK_ASSERT(entry_top == duk_get_top(ctx));
2615#endif
2616
2617 return retval;
2618}
2619
2620/*
2621 * Halt execution helper
2622 */
2623
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.
2628 */
2629
2630DUK_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;
2634
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);
2639
2640 DUK_HEAP_SET_PAUSED(thr->heap);
2641
2642 act = duk_hthread_get_current_activation(thr);
2643
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.
2646 */
2647
2648 /* Decrement PC if that was requested, this requires a PC sync. */
2649 if (act != NULL) {
2650 duk_hthread_sync_currpc(thr);
2651 old_pc = act->curr_pc;
2652 fun = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act);
2653
2654 /* Short circuit if is safe: if act->curr_pc != NULL, 'fun' is
2655 * guaranteed to be a non-NULL Ecmascript function.
2656 */
2657 DUK_ASSERT(act->curr_pc == NULL ||
2658 (fun != NULL && DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) fun)));
2659 if (use_prev_pc &&
2660 act->curr_pc != NULL &&
2661 act->curr_pc > DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun)) {
2662 act->curr_pc--;
2663 }
2664 }
2665
2666 /* Process debug messages until we are no longer paused. */
2667
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.
2671 */
2672
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*/);
2678 }
2679
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
2685 * with PC values.
2686 */
2687 if (act != NULL) {
2688 act->curr_pc = old_pc; /* restore PC */
2689 }
2690}
2691
2692/*
2693 * Breakpoint management
2694 */
2695
2696DUK_INTERNAL duk_small_int_t duk_debug_add_breakpoint(duk_hthread *thr, duk_hstring *filename, duk_uint32_t line) {
2697 duk_heap *heap;
2698 duk_breakpoint *b;
2699
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.
2703 */
2704
2705 DUK_ASSERT(thr != NULL);
2706 DUK_ASSERT(filename != NULL);
2707 heap = thr->heap;
2708 DUK_ASSERT(heap != NULL);
2709
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));
2713 return -1;
2714 }
2715 heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL;
2716 b = heap->dbg_breakpoints + (heap->dbg_breakpoint_count++);
2717 b->filename = filename;
2718 b->line = line;
2719 DUK_HSTRING_INCREF(thr, filename);
2720
2721 return heap->dbg_breakpoint_count - 1; /* index */
2722}
2723
2724DUK_INTERNAL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_uint_t breakpoint_index) {
2725 duk_heap *heap;
2726 duk_hstring *h;
2727 duk_breakpoint *b;
2728 duk_size_t move_size;
2729
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.
2733 */
2734
2735 DUK_ASSERT(thr != NULL);
2736 heap = thr->heap;
2737 DUK_ASSERT(heap != NULL);
2738 DUK_ASSERT(DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap));
2739 DUK_ASSERT_DISABLE(breakpoint_index >= 0); /* unsigned */
2740
2741 if (breakpoint_index >= heap->dbg_breakpoint_count) {
2742 DUK_D(DUK_DPRINT("invalid breakpoint index: %ld", (long) breakpoint_index));
2743 return 0;
2744 }
2745 b = heap->dbg_breakpoints + breakpoint_index;
2746
2747 h = b->filename;
2748 DUK_ASSERT(h != NULL);
2749
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);
2755 }
2756 heap->dbg_breakpoint_count--;
2757 heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL;
2758
2759 DUK_HSTRING_DECREF(thr, h); /* side effects */
2760 DUK_UNREF(h); /* w/o refcounting */
2761
2762 /* Breakpoint entries above the used area are left as garbage. */
2763
2764 return 1;
2765}
2766
2767#undef DUK__SET_CONN_BROKEN
2768
2769#else /* DUK_USE_DEBUGGER_SUPPORT */
2770
2771/* No debugger support. */
2772
2773#endif /* DUK_USE_DEBUGGER_SUPPORT */