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