]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/src/third_party/duktape-1.8.0/src-separate/duk_debugger.c
buildsys: switch source download to quincy
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.8.0 / src-separate / duk_debugger.c
1 /*
2 * Duktape debugger
3 */
4
5 #include "duk_internal.h"
6
7 #if defined(DUK_USE_DEBUGGER_SUPPORT)
8
9 /*
10 * Helper structs
11 */
12
13 typedef union {
14 void *p;
15 duk_uint_t b[1];
16 /* Use b[] to access the size of the union, which is strictly not
17 * correct. Can't use fixed size unless there's feature detection
18 * for pointer byte size.
19 */
20 } duk__ptr_union;
21
22 /*
23 * Detach handling
24 */
25
26 #define DUK__SET_CONN_BROKEN(thr,reason) do { \
27 /* For now shared handler is fine. */ \
28 duk__debug_do_detach1((thr)->heap, (reason)); \
29 } while (0)
30
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.
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
88 DUK_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
111 DUK_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 */
121 DUK_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
138 DUK_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
157 DUK_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
176 DUK_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. */
200 DUK_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
213 DUK_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. */
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.
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. */
239 DUK_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
285 DUK_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
293 DUK_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
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);
307 }
308
309 DUK_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
319 DUK_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
340 DUK_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
358 DUK_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
386 DUK_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
397 DUK_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
419 DUK_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
429 DUK_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
448 DUK_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
477 DUK_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. */
597 DUK_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
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);
644 }
645
646 DUK_INTERNAL void duk_debug_write_unused(duk_hthread *thr) {
647 duk_debug_write_byte(thr, DUK_DBG_IB_UNUSED);
648 }
649
650 DUK_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)
655 DUK_INTERNAL void duk_debug_write_null(duk_hthread *thr) {
656 duk_debug_write_byte(thr, DUK_DBG_IB_NULL);
657 }
658 #endif
659
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);
662 }
663
664 /* Write signed 32-bit integer. */
665 DUK_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. */
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.
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
706 DUK_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
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);
737 }
738
739 DUK_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
747 DUK_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
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));
759 }
760
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);
763 }
764
765 DUK_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
773 DUK_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
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);
793 }
794
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);
798 }
799 #endif /* DUK_USE_DEBUGGER_DUMPHEAP || DUK_USE_DEBUGGER_INSPECT */
800
801 DUK_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
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;
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 */
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);
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 */
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);
929 }
930 #endif
931
932 DUK_INTERNAL void duk_debug_write_reply(duk_hthread *thr) {
933 duk_debug_write_byte(thr, DUK_DBG_IB_REPLY);
934 }
935
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);
942 }
943
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);
947 }
948
949 DUK_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
964 DUK_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
993 DUK_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 act = thr->callstack + thr->callstack_top - 1;
1017 duk_debug_write_uint(thr, (duk_uint32_t) duk_hthread_get_act_curr_pc(thr, act));
1018 }
1019
1020 duk_debug_write_eom(thr);
1021 }
1022
1023 #if defined(DUK_USE_DEBUGGER_THROW_NOTIFY)
1024 DUK_INTERNAL void duk_debug_send_throw(duk_hthread *thr, duk_bool_t fatal) {
1025 /*
1026 * NFY <int: 5> <int: fatal> <str: msg> <str: filename> <int: linenumber> EOM
1027 */
1028
1029 duk_context *ctx = (duk_context *) thr;
1030 duk_activation *act;
1031 duk_uint32_t pc;
1032
1033 DUK_ASSERT(thr->valstack_top > thr->valstack); /* At least: ... [err] */
1034
1035 duk_debug_write_notify(thr, DUK_DBG_CMD_THROW);
1036 duk_debug_write_int(thr, fatal);
1037
1038 /* Report thrown value to client coerced to string */
1039 duk_dup(ctx, -1);
1040 duk__debug_write_hstring_safe_top(thr);
1041 duk_pop(ctx);
1042
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));
1049 duk_pop_2(ctx);
1050 } else {
1051 /* For anything other than an Error instance, we calculate the
1052 * error location directly from the current activation if one
1053 * exists.
1054 */
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));
1063 duk_pop_2(ctx);
1064 } else {
1065 /* Can happen if duk_throw() is called on an empty
1066 * callstack.
1067 */
1068 duk_debug_write_cstring(thr, "");
1069 duk_debug_write_uint(thr, 0);
1070 }
1071 }
1072
1073 duk_debug_write_eom(thr);
1074 }
1075 #endif /* DUK_USE_DEBUGGER_THROW_NOTIFY */
1076
1077 /*
1078 * Debug message processing
1079 */
1080
1081 /* Skip dvalue. */
1082 DUK_LOCAL duk_bool_t duk__debug_skip_dvalue(duk_hthread *thr) {
1083 duk_uint8_t x;
1084 duk_uint32_t len;
1085
1086 x = duk_debug_read_byte(thr);
1087
1088 if (x >= 0xc0) {
1089 duk_debug_skip_byte(thr);
1090 return 0;
1091 }
1092 if (x >= 0x80) {
1093 return 0;
1094 }
1095 if (x >= 0x60) {
1096 duk_debug_skip_bytes(thr, x - 0x60);
1097 return 0;
1098 }
1099 switch(x) {
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:
1106 break;
1107 case DUK_DBG_IB_INT4:
1108 (void) duk__debug_read_uint32_raw(thr);
1109 break;
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);
1114 break;
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);
1119 break;
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:
1125 break;
1126 case DUK_DBG_IB_NUMBER:
1127 duk_debug_skip_bytes(thr, 8);
1128 break;
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);
1133 break;
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);
1138 break;
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);
1143 break;
1144 default:
1145 goto fail;
1146 }
1147
1148 return 0;
1149
1150 fail:
1151 DUK__SET_CONN_BROKEN(thr, 1);
1152 return 1; /* Pretend like we got EOM */
1153 }
1154
1155 /* Skip dvalues to EOM. */
1156 DUK_LOCAL void duk__debug_skip_to_eom(duk_hthread *thr) {
1157 for (;;) {
1158 if (duk__debug_skip_dvalue(thr)) {
1159 break;
1160 }
1161 }
1162 }
1163
1164 /*
1165 * Simple commands
1166 */
1167
1168 DUK_LOCAL void duk__debug_handle_basic_info(duk_hthread *thr, duk_heap *heap) {
1169 DUK_UNREF(heap);
1170 DUK_D(DUK_DPRINT("debug command Version"));
1171
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);
1182 #else
1183 duk_debug_write_int(thr, 0);
1184 #endif
1185 duk_debug_write_int(thr, (duk_int_t) sizeof(void *));
1186 duk_debug_write_eom(thr);
1187 }
1188
1189 DUK_LOCAL void duk__debug_handle_trigger_status(duk_hthread *thr, duk_heap *heap) {
1190 DUK_UNREF(heap);
1191 DUK_D(DUK_DPRINT("debug command TriggerStatus"));
1192
1193 duk_debug_write_reply(thr);
1194 duk_debug_write_eom(thr);
1195 heap->dbg_state_dirty = 1;
1196 }
1197
1198 DUK_LOCAL void duk__debug_handle_pause(duk_hthread *thr, duk_heap *heap) {
1199 DUK_D(DUK_DPRINT("debug command Pause"));
1200
1201 DUK_HEAP_SET_PAUSED(heap);
1202 duk_debug_write_reply(thr);
1203 duk_debug_write_eom(thr);
1204 }
1205
1206 DUK_LOCAL void duk__debug_handle_resume(duk_hthread *thr, duk_heap *heap) {
1207 DUK_D(DUK_DPRINT("debug command Resume"));
1208
1209 DUK_HEAP_CLEAR_PAUSED(heap);
1210 duk_debug_write_reply(thr);
1211 duk_debug_write_eom(thr);
1212 }
1213
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;
1217
1218 DUK_D(DUK_DPRINT("debug command StepInto/StepOver/StepOut: %d", (int) cmd));
1219
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;
1224 } else {
1225 DUK_ASSERT(cmd == DUK_DBG_CMD_STEPOUT);
1226 step_type = DUK_STEP_TYPE_OUT;
1227 }
1228
1229 line = duk_debug_curr_line(thr);
1230 if (line > 0) {
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;
1237 } else {
1238 DUK_D(DUK_DPRINT("cannot determine current line, stepinto/stepover/stepout ignored"));
1239 }
1240 duk_debug_write_reply(thr);
1241 duk_debug_write_eom(thr);
1242 }
1243
1244 DUK_LOCAL void duk__debug_handle_list_break(duk_hthread *thr, duk_heap *heap) {
1245 duk_small_int_t i;
1246
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);
1252 }
1253 duk_debug_write_eom(thr);
1254 }
1255
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;
1260
1261 DUK_UNREF(heap);
1262
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);
1267 if (idx >= 0) {
1268 duk_debug_write_reply(thr);
1269 duk_debug_write_int(thr, (duk_int32_t) idx);
1270 duk_debug_write_eom(thr);
1271 } else {
1272 duk_debug_write_error_eom(thr, DUK_DBG_ERR_TOOMANY, "no space for breakpoint");
1273 }
1274 }
1275
1276 DUK_LOCAL void duk__debug_handle_del_break(duk_hthread *thr, duk_heap *heap) {
1277 duk_small_uint_t idx;
1278
1279 DUK_UNREF(heap);
1280
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);
1286 } else {
1287 duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid breakpoint index");
1288 }
1289 }
1290
1291 DUK_LOCAL void duk__debug_handle_get_var(duk_hthread *thr, duk_heap *heap) {
1292 duk_context *ctx = (duk_context *) thr;
1293 duk_hstring *str;
1294 duk_bool_t rc;
1295 duk_int32_t level;
1296
1297 DUK_UNREF(heap);
1298 DUK_D(DUK_DPRINT("debug command GetVar"));
1299
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");
1307 return;
1308 }
1309 } else {
1310 level = -1;
1311 }
1312
1313 if (thr->callstack_top > 0) {
1314 rc = duk_js_getvar_activation(thr,
1315 thr->callstack + thr->callstack_top + level,
1316 str,
1317 0);
1318 } else {
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.
1322 */
1323 DUK_D(DUK_DPRINT("callstack empty, no activation -> ignore getvar"));
1324 rc = 0;
1325 }
1326
1327 duk_debug_write_reply(thr);
1328 if (rc) {
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));
1332 } else {
1333 duk_debug_write_int(thr, 0);
1334 duk_debug_write_unused(thr);
1335 }
1336 duk_debug_write_eom(thr);
1337 }
1338
1339 DUK_LOCAL void duk__debug_handle_put_var(duk_hthread *thr, duk_heap *heap) {
1340 duk_hstring *str;
1341 duk_tval *tv;
1342 duk_int32_t level;
1343
1344 DUK_UNREF(heap);
1345 DUK_D(DUK_DPRINT("debug command PutVar"));
1346
1347 str = duk_debug_read_hstring(thr); /* push to stack */
1348 DUK_ASSERT(str != NULL);
1349 tv = duk_debug_read_tval(thr);
1350 if (tv == NULL) {
1351 /* detached */
1352 return;
1353 }
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");
1359 return;
1360 }
1361 } else {
1362 level = -1;
1363 }
1364
1365 if (thr->callstack_top > 0) {
1366 duk_js_putvar_activation(thr,
1367 thr->callstack + thr->callstack_top + level,
1368 str,
1369 tv,
1370 0);
1371 } else {
1372 DUK_D(DUK_DPRINT("callstack empty, no activation -> ignore putvar"));
1373 }
1374
1375 /* XXX: Current putvar implementation doesn't have a success flag,
1376 * add one and send to debug client?
1377 */
1378 duk_debug_write_reply(thr);
1379 duk_debug_write_eom(thr);
1380 }
1381
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;
1388 duk_size_t i;
1389
1390 DUK_ASSERT(thr != NULL);
1391 DUK_UNREF(heap);
1392
1393 duk_debug_write_reply(thr);
1394 while (curr_thr != NULL) {
1395 i = curr_thr->callstack_top;
1396 while (i > 0) {
1397 i--;
1398 curr_act = curr_thr->callstack + i;
1399
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
1408 */
1409
1410 /* XXX: optimize to use direct reads, i.e. avoid
1411 * value stack operations.
1412 */
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) {
1420 pc--;
1421 }
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);
1425 duk_pop_3(ctx);
1426 }
1427 curr_thr = curr_thr->resumer;
1428 }
1429 /* SCANBUILD: warning about 'thr' potentially being NULL here,
1430 * warning is incorrect because thr != NULL always here.
1431 */
1432 duk_debug_write_eom(thr);
1433 }
1434
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;
1438 duk_int32_t level;
1439 duk_hstring *varname;
1440
1441 DUK_UNREF(heap);
1442
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");
1448 return;
1449 }
1450 duk_debug_write_reply(thr);
1451 } else {
1452 duk_debug_write_reply(thr);
1453 if (thr->callstack_top == 0) {
1454 goto callstack_empty;
1455 }
1456 level = -1;
1457 }
1458
1459 curr_act = thr->callstack + thr->callstack_top + level;
1460
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
1465 */
1466
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);
1474
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 ] */
1480 }
1481 } else {
1482 DUK_D(DUK_DPRINT("varmap is not an object in GetLocals, ignore"));
1483 }
1484
1485 callstack_empty:
1486 duk_debug_write_eom(thr);
1487 }
1488
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;
1492 duk_int_t call_ret;
1493 duk_small_int_t eval_err;
1494 duk_int32_t level;
1495
1496 DUK_UNREF(heap);
1497
1498 DUK_D(DUK_DPRINT("debug command Eval"));
1499
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'.
1503 *
1504 * Callstack level for debug commands only affects scope -- the callstack
1505 * as seen by, e.g. Duktape.act() will be the same regardless.
1506 */
1507
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 */
1511
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");
1518 return;
1519 }
1520 }
1521 else {
1522 level = -1;
1523 }
1524 DUK_ASSERT(level < 0 && -level <= (duk_int32_t) thr->callstack_top);
1525 duk_push_int(ctx, level - 1); /* compensate for eval() call */
1526
1527 /* [ ... eval "eval" eval_input level ] */
1528
1529 call_flags = 0;
1530 if (thr->callstack_top >= (duk_size_t) -level) {
1531 duk_activation *act;
1532 duk_hobject *fun;
1533
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.
1541 */
1542 call_flags |= DUK_CALL_FLAG_DIRECT_EVAL;
1543 }
1544 }
1545
1546 call_ret = duk_handle_call_protected(thr, 2 /*num_stack_args*/, call_flags);
1547
1548 if (call_ret == DUK_EXEC_SUCCESS) {
1549 eval_err = 0;
1550 /* Use result value as is. */
1551 } else {
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.
1555 */
1556 eval_err = 1;
1557 duk_safe_to_string(ctx, -1);
1558 }
1559
1560 /* [ ... result ] */
1561
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);
1567 }
1568
1569 DUK_LOCAL void duk__debug_handle_detach(duk_hthread *thr, duk_heap *heap) {
1570 DUK_UNREF(heap);
1571 DUK_D(DUK_DPRINT("debug command Detach"));
1572
1573 duk_debug_write_reply(thr);
1574 duk_debug_write_eom(thr);
1575
1576 DUK_D(DUK_DPRINT("debug connection detached, mark broken"));
1577 DUK__SET_CONN_BROKEN(thr, 0); /* not an error */
1578 }
1579
1580 DUK_LOCAL void duk__debug_handle_apprequest(duk_hthread *thr, duk_heap *heap) {
1581 duk_context *ctx = (duk_context *) thr;
1582 duk_idx_t old_top;
1583
1584 DUK_D(DUK_DPRINT("debug command AppRequest"));
1585
1586 old_top = duk_get_top(ctx); /* save stack top */
1587
1588 if (heap->dbg_request_cb != NULL) {
1589 duk_idx_t nrets;
1590 duk_idx_t nvalues = 0;
1591 duk_idx_t top, idx;
1592
1593 /* Read tvals from the message and push them onto the valstack,
1594 * then call the request callback to process the request.
1595 */
1596 while (duk_debug_peek_byte(thr) != DUK_DBG_IB_EOM) {
1597 duk_tval *tv;
1598 if (!duk_check_stack(ctx, 1)) {
1599 DUK_D(DUK_DPRINT("failed to allocate space for request dvalue(s)"));
1600 goto fail;
1601 }
1602 tv = duk_debug_read_tval(thr); /* push to stack */
1603 if (tv == NULL) {
1604 /* detached */
1605 return;
1606 }
1607 nvalues++;
1608 }
1609 DUK_ASSERT(duk_get_top(ctx) == old_top + nvalues);
1610
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)));
1617 if (nrets >= 0) {
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));
1624 goto fail;
1625 }
1626
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));
1632 }
1633 duk_debug_write_eom(thr);
1634 } else {
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"));
1638 goto fail;
1639 }
1640 duk_debug_write_error_eom(thr, DUK_DBG_ERR_APPLICATION, duk_get_string(ctx, -1));
1641 }
1642
1643 duk_set_top(ctx, old_top); /* restore stack top */
1644 } else {
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");
1647 }
1648
1649 return;
1650
1651 fail:
1652 duk_set_top(ctx, old_top); /* restore stack top */
1653 DUK__SET_CONN_BROKEN(thr, 1);
1654 }
1655
1656 /*
1657 * DumpHeap command
1658 */
1659
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?
1663 */
1664 DUK_LOCAL void duk__debug_dump_heaphdr(duk_hthread *thr, duk_heap *heap, duk_heaphdr *hdr) {
1665 DUK_UNREF(heap);
1666
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));
1672 #else
1673 duk_debug_write_int(thr, (duk_int32_t) -1);
1674 #endif
1675
1676 switch (DUK_HEAPHDR_GET_TYPE(hdr)) {
1677 case DUK_HTYPE_STRING: {
1678 duk_hstring *h = (duk_hstring *) hdr;
1679
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);
1684 break;
1685 }
1686 case DUK_HTYPE_OBJECT: {
1687 duk_hobject *h = (duk_hobject *) hdr;
1688 duk_hstring *k;
1689 duk_uint_fast32_t i;
1690
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));
1697
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);
1702 if (k == NULL) {
1703 duk_debug_write_int(thr, 0); /* isAccessor */
1704 duk_debug_write_unused(thr);
1705 continue;
1706 }
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);
1711 } else {
1712 duk_debug_write_int(thr, 0); /* isAccessor */
1713
1714 duk__debug_write_tval_heapptr(thr, &DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->v);
1715 }
1716 }
1717
1718 for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) {
1719 /* Note: array dump will include elements beyond
1720 * 'length'.
1721 */
1722 duk__debug_write_tval_heapptr(thr, DUK_HOBJECT_A_GET_VALUE_PTR(heap, h, i));
1723 }
1724 break;
1725 }
1726 case DUK_HTYPE_BUFFER: {
1727 duk_hbuffer *h = (duk_hbuffer *) hdr;
1728
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));
1731 break;
1732 }
1733 default: {
1734 DUK_D(DUK_DPRINT("invalid htype: %d", (int) DUK_HEAPHDR_GET_TYPE(hdr)));
1735 }
1736 }
1737 }
1738
1739 DUK_LOCAL void duk__debug_dump_heap_allocated(duk_hthread *thr, duk_heap *heap) {
1740 duk_heaphdr *hdr;
1741
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);
1746 }
1747 }
1748
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)
1754 duk_uint16_t *lst;
1755 #else
1756 duk_hstring **lst;
1757 #endif
1758 duk_hstring *h;
1759
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);
1765 #else
1766 lst = e->u.strlist;
1767 #endif
1768 DUK_ASSERT(lst != NULL);
1769
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]);
1773 #else
1774 h = lst[j];
1775 #endif
1776 if (h != NULL) {
1777 duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h);
1778 }
1779 }
1780 } else {
1781 #if defined(DUK_USE_HEAPPTR16)
1782 h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.str16);
1783 #else
1784 h = e->u.str;
1785 #endif
1786 if (h != NULL) {
1787 duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h);
1788 }
1789 }
1790 }
1791 }
1792 #endif /* DUK_USE_STRTAB_CHAIN */
1793
1794 #if defined(DUK_USE_STRTAB_PROBE)
1795 DUK_LOCAL void duk__debug_dump_strtab_probe(duk_hthread *thr, duk_heap *heap) {
1796 duk_uint32_t i;
1797 duk_hstring *h;
1798
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]);
1802 #else
1803 h = heap->strtable[i];
1804 #endif
1805 if (h == NULL || h == DUK_STRTAB_DELETED_MARKER(heap)) {
1806 continue;
1807 }
1808
1809 duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h);
1810 }
1811 }
1812 #endif /* DUK_USE_STRTAB_PROBE */
1813
1814 DUK_LOCAL void duk__debug_handle_dump_heap(duk_hthread *thr, duk_heap *heap) {
1815 DUK_D(DUK_DPRINT("debug command DumpHeap"));
1816
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);
1821 #endif
1822 #if defined(DUK_USE_STRTAB_PROBE)
1823 duk__debug_dump_strtab_probe(thr, heap);
1824 #endif
1825 duk_debug_write_eom(thr);
1826 }
1827 #endif /* DUK_USE_DEBUGGER_DUMPHEAP */
1828
1829 DUK_LOCAL void duk__debug_handle_get_bytecode(duk_hthread *thr, duk_heap *heap) {
1830 duk_activation *act;
1831 duk_hcompiledfunction *fun = NULL;
1832 duk_size_t i, n;
1833 duk_tval *tv;
1834 duk_hobject **fn;
1835 duk_int32_t level = -1;
1836 duk_uint8_t ibyte;
1837
1838 DUK_UNREF(heap);
1839
1840 DUK_D(DUK_DPRINT("debug command GetBytecode"));
1841
1842 ibyte = duk_debug_peek_byte(thr);
1843 if (ibyte != DUK_DBG_IB_EOM) {
1844 tv = duk_debug_read_tval(thr);
1845 if (tv == NULL) {
1846 /* detached */
1847 return;
1848 }
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);
1855 } else {
1856 DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!T", tv));
1857 goto fail_args;
1858 }
1859 }
1860
1861 if (fun == NULL) {
1862 if (level >= 0 || -level > (duk_int32_t) thr->callstack_top) {
1863 DUK_D(DUK_DPRINT("invalid callstack level for GetBytecode"));
1864 goto fail_level;
1865 }
1866 act = thr->callstack + thr->callstack_top + level;
1867 fun = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act);
1868 }
1869
1870 if (fun == NULL || !DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) fun)) {
1871 DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!O", fun));
1872 goto fail_args;
1873 }
1874 DUK_ASSERT(fun != NULL && DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) fun));
1875
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);
1882 tv++;
1883 }
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);
1889 fn++;
1890 }
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);
1895 return;
1896
1897 fail_args:
1898 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid argument");
1899 return;
1900
1901 fail_level:
1902 duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid callstack level");
1903 return;
1904 }
1905
1906 /*
1907 * Object inspection commands: GetHeapObjInfo, GetObjPropDesc,
1908 * GetObjPropDescRange
1909 */
1910
1911 #if defined(DUK_USE_DEBUGGER_INSPECT)
1912
1913 #if 0 /* pruned */
1914 DUK_LOCAL const char * const duk__debug_getinfo_heaphdr_keys[] = {
1915 "reachable",
1916 "temproot",
1917 "finalizable",
1918 "finalized",
1919 "readonly"
1920 /* NULL not needed here */
1921 };
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,
1928 0 /* terminator */
1929 };
1930 #endif
1931 DUK_LOCAL const char * const duk__debug_getinfo_hstring_keys[] = {
1932 #if 0
1933 "arridx",
1934 "internal",
1935 "reserved_word",
1936 "strict_reserved_word",
1937 "eval_or_arguments",
1938 #endif
1939 "extdata"
1940 /* NULL not needed here */
1941 };
1942 DUK_LOCAL duk_uint_t duk__debug_getinfo_hstring_masks[] = {
1943 #if 0
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,
1949 #endif
1950 DUK_HSTRING_FLAG_EXTDATA,
1951 0 /* terminator */
1952 };
1953 DUK_LOCAL const char * const duk__debug_getinfo_hobject_keys[] = {
1954 "extensible",
1955 "constructable",
1956 "bound",
1957 "compiledfunction",
1958 "nativefunction",
1959 "bufferobject",
1960 "thread",
1961 "array_part",
1962 "strict",
1963 "notail",
1964 "newenv",
1965 "namebinding",
1966 "createargs",
1967 "envrecclosed",
1968 "exotic_array",
1969 "exotic_stringobj",
1970 "exotic_arguments",
1971 "exotic_dukfunc",
1972 "exotic_proxyobj"
1973 /* NULL not needed here */
1974 };
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,
1995 0 /* terminator */
1996 };
1997 DUK_LOCAL const char * const duk__debug_getinfo_hbuffer_keys[] = {
1998 "dynamic",
1999 "external"
2000 /* NULL not needed here */
2001 };
2002 DUK_LOCAL duk_uint_t duk__debug_getinfo_hbuffer_masks[] = {
2003 DUK_HBUFFER_FLAG_DYNAMIC,
2004 DUK_HBUFFER_FLAG_EXTERNAL,
2005 0 /* terminator */
2006 };
2007
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);
2011 }
2012
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);
2017 }
2018
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);
2023 }
2024
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);
2029 }
2030
2031 DUK_LOCAL void duk__debug_getinfo_bitmask(duk_hthread *thr, const char * const * keys, duk_uint_t *masks, duk_uint_t flags) {
2032 const char *key;
2033 duk_uint_t mask;
2034
2035 for (;;) {
2036 mask = *masks++;
2037 if (!mask) {
2038 break;
2039 }
2040 key = *keys++;
2041 DUK_ASSERT(key != NULL);
2042
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);
2045 }
2046 }
2047
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'.
2052 */
2053 DUK_LOCAL duk_bool_t duk__debug_getprop_index(duk_hthread *thr, duk_heap *heap, duk_hobject *h_obj, duk_uint_t idx) {
2054 duk_uint_t a_size;
2055 duk_tval *tv;
2056 duk_hstring *h_key;
2057 duk_hobject *h_getset;
2058 duk_uint_t flags;
2059
2060 DUK_UNREF(heap);
2061
2062 a_size = DUK_HOBJECT_GET_ASIZE(h_obj);
2063 if (idx < a_size) {
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);
2068 return 1;
2069 }
2070
2071 idx -= a_size;
2072 if (idx >= DUK_HOBJECT_GET_ENEXT(h_obj)) {
2073 return 0;
2074 }
2075
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);
2081 return 1;
2082 }
2083
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;
2087 }
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);
2092 if (h_getset) {
2093 duk_debug_write_hobject(thr, h_getset);
2094 } else {
2095 duk_debug_write_null(thr);
2096 }
2097 h_getset = DUK_HOBJECT_E_GET_VALUE_SETTER(heap, h_obj, idx);
2098 if (h_getset) {
2099 duk_debug_write_hobject(thr, h_getset);
2100 } else {
2101 duk_debug_write_null(thr);
2102 }
2103 } else {
2104 tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, h_obj, idx);
2105 duk_debug_write_tval(thr, tv);
2106 }
2107 return 1;
2108 }
2109
2110 DUK_LOCAL void duk__debug_handle_get_heap_obj_info(duk_hthread *thr, duk_heap *heap) {
2111 duk_heaphdr *h;
2112
2113 DUK_D(DUK_DPRINT("debug command GetHeapObjInfo"));
2114 DUK_UNREF(heap);
2115
2116 h = duk_debug_read_any_ptr(thr);
2117 if (!h) {
2118 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid target");
2119 return;
2120 }
2121
2122 duk_debug_write_reply(thr);
2123
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.
2127 */
2128
2129 duk__debug_getinfo_flags_key(thr, "heapptr");
2130 duk_debug_write_heapptr(thr, h);
2131
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));
2137 #endif
2138 #if 0 /* pruned */
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));
2143 #endif
2144
2145 switch (DUK_HEAPHDR_GET_TYPE(h)) {
2146 case DUK_HTYPE_STRING: {
2147 duk_hstring *h_str;
2148
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);
2159 break;
2160 }
2161 case DUK_HTYPE_OBJECT: {
2162 duk_hobject *h_obj;
2163 duk_hobject *h_proto;
2164
2165 h_obj = (duk_hobject *) h;
2166 h_proto = DUK_HOBJECT_GET_PROTOTYPE(heap, h_obj);
2167
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);
2179 } else {
2180 duk_debug_write_null(thr);
2181 }
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));
2188
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;
2193
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).
2199 */
2200 duk__debug_getinfo_flags_key(thr, "funcptr");
2201 duk_debug_write_buffer(thr, (const char *) &h_fun->func, sizeof(h_fun->func));
2202 }
2203
2204 if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) {
2205 duk_hcompiledfunction *h_fun;
2206 duk_hbuffer *h_buf;
2207 h_fun = (duk_hcompiledfunction *) h_obj;
2208
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);
2217 }
2218 }
2219
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.
2223 */
2224 duk_hthread *h_thr;
2225 h_thr = (duk_hthread *) h_obj;
2226 DUK_UNREF(h_thr);
2227 }
2228
2229 if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj)) {
2230 duk_hbufferobject *h_bufobj;
2231 h_bufobj = (duk_hbufferobject *) h_obj;
2232
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);
2241 }
2242 }
2243 break;
2244 }
2245 case DUK_HTYPE_BUFFER: {
2246 duk_hbuffer *h_buf;
2247
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 */
2258 break;
2259 }
2260 default: {
2261 /* Since we already started writing the reply, just emit nothing. */
2262 DUK_D(DUK_DPRINT("inspect target pointer has invalid heaphdr type"));
2263 }
2264 }
2265
2266 duk_debug_write_eom(thr);
2267 }
2268
2269 DUK_LOCAL void duk__debug_handle_get_obj_prop_desc(duk_hthread *thr, duk_heap *heap) {
2270 duk_heaphdr *h;
2271 duk_hobject *h_obj;
2272 duk_hstring *h_key;
2273 duk_propdesc desc;
2274
2275 DUK_D(DUK_DPRINT("debug command GetObjPropDesc"));
2276 DUK_UNREF(heap);
2277
2278 h = duk_debug_read_any_ptr(thr);
2279 if (!h) {
2280 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid target");
2281 return;
2282 }
2283 h_key = duk_debug_read_hstring(thr);
2284 if (h == NULL || DUK_HEAPHDR_GET_TYPE(h) != DUK_HTYPE_OBJECT || h_key == NULL) {
2285 goto fail_args;
2286 }
2287 h_obj = (duk_hobject *) h;
2288
2289 if (duk_hobject_get_own_propdesc(thr, h_obj, h_key, &desc, 0 /*flags*/)) {
2290 duk_int_t virtual_idx;
2291 duk_bool_t rc;
2292
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);
2297
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);
2301 DUK_UNREF(rc);
2302 duk_debug_write_eom(thr);
2303 } else {
2304 duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "not found");
2305 }
2306 return;
2307
2308 fail_args:
2309 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid args");
2310 }
2311
2312 DUK_LOCAL void duk__debug_handle_get_obj_prop_desc_range(duk_hthread *thr, duk_heap *heap) {
2313 duk_heaphdr *h;
2314 duk_hobject *h_obj;
2315 duk_uint_t idx, idx_start, idx_end;
2316
2317 DUK_D(DUK_DPRINT("debug command GetObjPropDescRange"));
2318 DUK_UNREF(heap);
2319
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) {
2324 goto fail_args;
2325 }
2326 h_obj = (duk_hobject *) h;
2327
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
2332 * array .length is.
2333 */
2334
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)) {
2338 break;
2339 }
2340 }
2341 duk_debug_write_eom(thr);
2342 return;
2343
2344 fail_args:
2345 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid args");
2346 }
2347
2348 #endif /* DUK_USE_DEBUGGER_INSPECT */
2349
2350 /*
2351 * Process incoming debug requests
2352 *
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
2355 * automatically.
2356 */
2357
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.
2361 */
2362 DUK_LOCAL void duk__debug_process_message(duk_hthread *thr) {
2363 duk_context *ctx = (duk_context *) thr;
2364 duk_heap *heap;
2365 duk_uint8_t x;
2366 duk_int32_t cmd;
2367 duk_idx_t entry_top;
2368
2369 DUK_ASSERT(thr != NULL);
2370 heap = thr->heap;
2371 DUK_ASSERT(heap != NULL);
2372 DUK_UNREF(ctx);
2373
2374 entry_top = duk_get_top(ctx);
2375
2376 x = duk_debug_read_byte(thr);
2377 switch (x) {
2378 case DUK_DBG_IB_REQUEST: {
2379 cmd = duk_debug_read_int(thr);
2380 switch (cmd) {
2381 case DUK_DBG_CMD_BASICINFO: {
2382 duk__debug_handle_basic_info(thr, heap);
2383 break;
2384 }
2385 case DUK_DBG_CMD_TRIGGERSTATUS: {
2386 duk__debug_handle_trigger_status(thr, heap);
2387 break;
2388 }
2389 case DUK_DBG_CMD_PAUSE: {
2390 duk__debug_handle_pause(thr, heap);
2391 break;
2392 }
2393 case DUK_DBG_CMD_RESUME: {
2394 duk__debug_handle_resume(thr, heap);
2395 break;
2396 }
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);
2401 break;
2402 }
2403 case DUK_DBG_CMD_LISTBREAK: {
2404 duk__debug_handle_list_break(thr, heap);
2405 break;
2406 }
2407 case DUK_DBG_CMD_ADDBREAK: {
2408 duk__debug_handle_add_break(thr, heap);
2409 break;
2410 }
2411 case DUK_DBG_CMD_DELBREAK: {
2412 duk__debug_handle_del_break(thr, heap);
2413 break;
2414 }
2415 case DUK_DBG_CMD_GETVAR: {
2416 duk__debug_handle_get_var(thr, heap);
2417 break;
2418 }
2419 case DUK_DBG_CMD_PUTVAR: {
2420 duk__debug_handle_put_var(thr, heap);
2421 break;
2422 }
2423 case DUK_DBG_CMD_GETCALLSTACK: {
2424 duk__debug_handle_get_call_stack(thr, heap);
2425 break;
2426 }
2427 case DUK_DBG_CMD_GETLOCALS: {
2428 duk__debug_handle_get_locals(thr, heap);
2429 break;
2430 }
2431 case DUK_DBG_CMD_EVAL: {
2432 duk__debug_handle_eval(thr, heap);
2433 break;
2434 }
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).
2439 */
2440 duk__debug_handle_detach(thr, heap);
2441 break;
2442 }
2443 #if defined(DUK_USE_DEBUGGER_DUMPHEAP)
2444 case DUK_DBG_CMD_DUMPHEAP: {
2445 duk__debug_handle_dump_heap(thr, heap);
2446 break;
2447 }
2448 #endif /* DUK_USE_DEBUGGER_DUMPHEAP */
2449 case DUK_DBG_CMD_GETBYTECODE: {
2450 duk__debug_handle_get_bytecode(thr, heap);
2451 break;
2452 }
2453 case DUK_DBG_CMD_APPREQUEST: {
2454 duk__debug_handle_apprequest(thr, heap);
2455 break;
2456 }
2457 #if defined(DUK_USE_DEBUGGER_INSPECT)
2458 case DUK_DBG_CMD_GETHEAPOBJINFO: {
2459 duk__debug_handle_get_heap_obj_info(thr, heap);
2460 break;
2461 }
2462 case DUK_DBG_CMD_GETOBJPROPDESC: {
2463 duk__debug_handle_get_obj_prop_desc(thr, heap);
2464 break;
2465 }
2466 case DUK_DBG_CMD_GETOBJPROPDESCRANGE: {
2467 duk__debug_handle_get_obj_prop_desc_range(thr, heap);
2468 break;
2469 }
2470 #endif /* DUK_USE_DEBUGGER_INSPECT */
2471 default: {
2472 DUK_D(DUK_DPRINT("debug command unsupported: %d", (int) cmd));
2473 duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "unsupported command");
2474 }
2475 } /* switch cmd */
2476 break;
2477 }
2478 case DUK_DBG_IB_REPLY: {
2479 DUK_D(DUK_DPRINT("debug reply, skipping"));
2480 break;
2481 }
2482 case DUK_DBG_IB_ERROR: {
2483 DUK_D(DUK_DPRINT("debug error, skipping"));
2484 break;
2485 }
2486 case DUK_DBG_IB_NOTIFY: {
2487 DUK_D(DUK_DPRINT("debug notify, skipping"));
2488 break;
2489 }
2490 default: {
2491 DUK_D(DUK_DPRINT("invalid initial byte, drop connection: %d", (int) x));
2492 goto fail;
2493 }
2494 } /* switch initial byte */
2495
2496 DUK_ASSERT(duk_get_top(ctx) >= entry_top);
2497 duk_set_top(ctx, entry_top);
2498 duk__debug_skip_to_eom(thr);
2499 return;
2500
2501 fail:
2502 DUK_ASSERT(duk_get_top(ctx) >= entry_top);
2503 duk_set_top(ctx, entry_top);
2504 DUK__SET_CONN_BROKEN(thr, 1);
2505 return;
2506 }
2507
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;
2512 }
2513 }
2514
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;
2519 #endif
2520 duk_bool_t retval = 0;
2521
2522 DUK_ASSERT(thr != NULL);
2523 DUK_UNREF(ctx);
2524 DUK_ASSERT(thr->heap != NULL);
2525 #if defined(DUK_USE_ASSERTIONS)
2526 entry_top = duk_get_top(ctx);
2527 #endif
2528
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)));
2533
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.
2536 */
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;
2540
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.
2544 */
2545 duk__check_resend_status(thr);
2546
2547 for (;;) {
2548 /* Process messages until we're no longer paused or we peek
2549 * and see there's nothing to read right now.
2550 */
2551 DUK_DD(DUK_DDPRINT("top at loop top: %ld", (long) duk_get_top(ctx)));
2552 DUK_ASSERT(thr->heap->dbg_processing == 1);
2553
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.
2560 *
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
2568 * fully detached.
2569 *
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.
2573 */
2574
2575 DUK_D(DUK_DPRINT("detach pending (dbg_read_cb == NULL, dbg_detaching != 0), call detach2"));
2576
2577 duk__debug_do_detach2(thr->heap);
2578 thr->heap->dbg_processing = 1; /* may be set to 0 by duk_debugger_attach() inside callback */
2579
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));
2582 }
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 */
2585
2586 if (thr->heap->dbg_read_cb == NULL) {
2587 DUK_D(DUK_DPRINT("debug connection broken (and not detaching), stop processing messages"));
2588 break;
2589 }
2590
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
2595 * loop is correct.
2596 */
2597 DUK_D(DUK_DPRINT("processing debug message, peek indicated no data, stop processing messages"));
2598 break;
2599 }
2600 DUK_D(DUK_DPRINT("processing debug message, peek indicated there is data, handle it"));
2601 } else {
2602 DUK_D(DUK_DPRINT("paused, process debug message, blocking if necessary"));
2603 }
2604
2605 duk__check_resend_status(thr);
2606 duk__debug_process_message(thr);
2607 duk__check_resend_status(thr);
2608
2609 retval = 1; /* processed one or more messages */
2610 }
2611
2612 DUK_ASSERT(thr->heap->dbg_detaching == 0);
2613 DUK_ASSERT(thr->heap->dbg_processing == 1);
2614 thr->heap->dbg_processing = 0;
2615
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).
2618 */
2619 duk_debug_read_flush(thr); /* this cannot initiate a detach */
2620 DUK_ASSERT(thr->heap->dbg_detaching == 0);
2621
2622 DUK_DD(DUK_DDPRINT("top at exit: %ld", (long) duk_get_top(ctx)));
2623
2624 #if defined(DUK_USE_ASSERTIONS)
2625 /* Easy to get wrong, so assert for it. */
2626 DUK_ASSERT(entry_top == duk_get_top(ctx));
2627 #endif
2628
2629 return retval;
2630 }
2631
2632 /*
2633 * Halt execution helper
2634 */
2635
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.
2640 */
2641
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;
2646
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);
2651
2652 DUK_HEAP_SET_PAUSED(thr->heap);
2653
2654 act = duk_hthread_get_current_activation(thr);
2655
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.
2658 */
2659
2660 /* Decrement PC if that was requested, this requires a PC sync. */
2661 if (act != NULL) {
2662 duk_hthread_sync_currpc(thr);
2663 old_pc = act->curr_pc;
2664 fun = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act);
2665
2666 /* Short circuit if is safe: if act->curr_pc != NULL, 'fun' is
2667 * guaranteed to be a non-NULL Ecmascript function.
2668 */
2669 DUK_ASSERT(act->curr_pc == NULL ||
2670 (fun != NULL && DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) fun)));
2671 if (use_prev_pc &&
2672 act->curr_pc != NULL &&
2673 act->curr_pc > DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun)) {
2674 act->curr_pc--;
2675 }
2676 }
2677
2678 /* Process debug messages until we are no longer paused. */
2679
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.
2683 */
2684
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*/);
2690 }
2691
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
2697 * with PC values.
2698 */
2699 if (act != NULL) {
2700 act->curr_pc = old_pc; /* restore PC */
2701 }
2702 }
2703
2704 /*
2705 * Breakpoint management
2706 */
2707
2708 DUK_INTERNAL duk_small_int_t duk_debug_add_breakpoint(duk_hthread *thr, duk_hstring *filename, duk_uint32_t line) {
2709 duk_heap *heap;
2710 duk_breakpoint *b;
2711
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.
2715 */
2716
2717 DUK_ASSERT(thr != NULL);
2718 DUK_ASSERT(filename != NULL);
2719 heap = thr->heap;
2720 DUK_ASSERT(heap != NULL);
2721
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));
2725 return -1;
2726 }
2727 heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL;
2728 b = heap->dbg_breakpoints + (heap->dbg_breakpoint_count++);
2729 b->filename = filename;
2730 b->line = line;
2731 DUK_HSTRING_INCREF(thr, filename);
2732
2733 return heap->dbg_breakpoint_count - 1; /* index */
2734 }
2735
2736 DUK_INTERNAL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_uint_t breakpoint_index) {
2737 duk_heap *heap;
2738 duk_hstring *h;
2739 duk_breakpoint *b;
2740 duk_size_t move_size;
2741
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.
2745 */
2746
2747 DUK_ASSERT(thr != NULL);
2748 heap = thr->heap;
2749 DUK_ASSERT(heap != NULL);
2750 DUK_ASSERT(DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap));
2751 DUK_ASSERT_DISABLE(breakpoint_index >= 0); /* unsigned */
2752
2753 if (breakpoint_index >= heap->dbg_breakpoint_count) {
2754 DUK_D(DUK_DPRINT("invalid breakpoint index: %ld", (long) breakpoint_index));
2755 return 0;
2756 }
2757 b = heap->dbg_breakpoints + breakpoint_index;
2758
2759 h = b->filename;
2760 DUK_ASSERT(h != NULL);
2761
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);
2767 }
2768 heap->dbg_breakpoint_count--;
2769 heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL;
2770
2771 DUK_HSTRING_DECREF(thr, h); /* side effects */
2772 DUK_UNREF(h); /* w/o refcounting */
2773
2774 /* Breakpoint entries above the used area are left as garbage. */
2775
2776 return 1;
2777 }
2778
2779 #undef DUK__SET_CONN_BROKEN
2780
2781 #else /* DUK_USE_DEBUGGER_SUPPORT */
2782
2783 /* No debugger support. */
2784
2785 #endif /* DUK_USE_DEBUGGER_SUPPORT */