1 /*
2  *  Debugging related API calls
3  */
4 
5 #include "duk_internal.h"
6 
duk_push_context_dump(duk_context * ctx)7 DUK_EXTERNAL void duk_push_context_dump(duk_context *ctx) {
8 	duk_idx_t idx;
9 	duk_idx_t top;
10 
11 	DUK_ASSERT_CTX_VALID(ctx);
12 
13 	/* We don't duk_require_stack() here now, but rely on the caller having
14 	 * enough space.
15 	 */
16 
17 	top = duk_get_top(ctx);
18 	duk_push_array(ctx);
19 	for (idx = 0; idx < top; idx++) {
20 		duk_dup(ctx, idx);
21 		duk_put_prop_index(ctx, -2, idx);
22 	}
23 
24 	/* XXX: conversion errors should not propagate outwards.
25 	 * Perhaps values need to be coerced individually?
26 	 */
27 	duk_bi_json_stringify_helper(ctx,
28 	                             duk_get_top_index(ctx),  /*idx_value*/
29 	                             DUK_INVALID_INDEX,  /*idx_replacer*/
30 	                             DUK_INVALID_INDEX,  /*idx_space*/
31 	                             DUK_JSON_FLAG_EXT_CUSTOM |
32 	                             DUK_JSON_FLAG_ASCII_ONLY |
33 	                             DUK_JSON_FLAG_AVOID_KEY_QUOTES /*flags*/);
34 
35 	duk_push_sprintf(ctx, "ctx: top=%ld, stack=%s", (long) top, (const char *) duk_safe_to_string(ctx, -1));
36 	duk_replace(ctx, -3);  /* [ ... arr jsonx(arr) res ] -> [ ... res jsonx(arr) ] */
37 	duk_pop(ctx);
38 	DUK_ASSERT(duk_is_string(ctx, -1));
39 }
40 
41 #if defined(DUK_USE_DEBUGGER_SUPPORT)
42 
duk_debugger_attach_custom(duk_context * ctx,duk_debug_read_function read_cb,duk_debug_write_function write_cb,duk_debug_peek_function peek_cb,duk_debug_read_flush_function read_flush_cb,duk_debug_write_flush_function write_flush_cb,duk_debug_request_function request_cb,duk_debug_detached_function detached_cb,void * udata)43 DUK_EXTERNAL void duk_debugger_attach_custom(duk_context *ctx,
44                                              duk_debug_read_function read_cb,
45                                              duk_debug_write_function write_cb,
46                                              duk_debug_peek_function peek_cb,
47                                              duk_debug_read_flush_function read_flush_cb,
48                                              duk_debug_write_flush_function write_flush_cb,
49                                              duk_debug_request_function request_cb,
50                                              duk_debug_detached_function detached_cb,
51                                              void *udata) {
52 	duk_hthread *thr = (duk_hthread *) ctx;
53 	duk_heap *heap;
54 	const char *str;
55 	duk_size_t len;
56 
57 	/* XXX: should there be an error or an automatic detach if
58 	 * already attached?
59 	 */
60 
61 	DUK_D(DUK_DPRINT("application called duk_debugger_attach()"));
62 
63 	DUK_ASSERT_CTX_VALID(ctx);
64 	DUK_ASSERT(read_cb != NULL);
65 	DUK_ASSERT(write_cb != NULL);
66 	/* Other callbacks are optional. */
67 
68 	heap = thr->heap;
69 	heap->dbg_read_cb = read_cb;
70 	heap->dbg_write_cb = write_cb;
71 	heap->dbg_peek_cb = peek_cb;
72 	heap->dbg_read_flush_cb = read_flush_cb;
73 	heap->dbg_write_flush_cb = write_flush_cb;
74 	heap->dbg_request_cb = request_cb;
75 	heap->dbg_detached_cb = detached_cb;
76 	heap->dbg_udata = udata;
77 	heap->dbg_have_next_byte = 0;
78 
79 	/* Start in paused state. */
80 	heap->dbg_processing = 0;
81 	heap->dbg_paused = 1;
82 	heap->dbg_state_dirty = 1;
83 	heap->dbg_force_restart = 0;
84 	heap->dbg_step_type = 0;
85 	heap->dbg_step_thread = NULL;
86 	heap->dbg_step_csindex = 0;
87 	heap->dbg_step_startline = 0;
88 	heap->dbg_exec_counter = 0;
89 	heap->dbg_last_counter = 0;
90 	heap->dbg_last_time = 0.0;
91 
92 	/* Send version identification and flush right afterwards.  Note that
93 	 * we must write raw, unframed bytes here.
94 	 */
95 	duk_push_sprintf(ctx, "%ld %ld %s %s\n",
96 	                 (long) DUK_DEBUG_PROTOCOL_VERSION,
97 	                 (long) DUK_VERSION,
98 	                 (const char *) DUK_GIT_DESCRIBE,
99 	                 (const char *) DUK_USE_TARGET_INFO);
100 	str = duk_get_lstring(ctx, -1, &len);
101 	DUK_ASSERT(str != NULL);
102 	duk_debug_write_bytes(thr, (const duk_uint8_t *) str, len);
103 	duk_debug_write_flush(thr);
104 	duk_pop(ctx);
105 }
106 
duk_debugger_detach(duk_context * ctx)107 DUK_EXTERNAL void duk_debugger_detach(duk_context *ctx) {
108 	duk_hthread *thr;
109 
110 	DUK_D(DUK_DPRINT("application called duk_debugger_detach()"));
111 
112 	DUK_ASSERT_CTX_VALID(ctx);
113 	thr = (duk_hthread *) ctx;
114 	DUK_ASSERT(thr != NULL);
115 	DUK_ASSERT(thr->heap != NULL);
116 
117 	/* Can be called multiple times with no harm. */
118 	duk_debug_do_detach(thr->heap);
119 }
120 
duk_debugger_cooperate(duk_context * ctx)121 DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) {
122 	duk_hthread *thr;
123 	duk_bool_t processed_messages;
124 
125 	DUK_ASSERT_CTX_VALID(ctx);
126 	thr = (duk_hthread *) ctx;
127 	DUK_ASSERT(thr != NULL);
128 	DUK_ASSERT(thr->heap != NULL);
129 
130 	if (!DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
131 		return;
132 	}
133 	if (thr->callstack_top > 0 || thr->heap->dbg_processing) {
134 		/* Calling duk_debugger_cooperate() while Duktape is being
135 		 * called into is not supported.  This is not a 100% check
136 		 * but prevents any damage in most cases.
137 		 */
138 		return;
139 	}
140 
141 	processed_messages = duk_debug_process_messages(thr, 1 /*no_block*/);
142 	DUK_UNREF(processed_messages);
143 }
144 
duk_debugger_notify(duk_context * ctx,duk_idx_t nvalues)145 DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) {
146 	duk_hthread *thr;
147 	duk_idx_t top;
148 	duk_idx_t idx;
149 	duk_bool_t ret = 0;
150 
151 	DUK_ASSERT_CTX_VALID(ctx);
152 	thr = (duk_hthread *) ctx;
153 	DUK_ASSERT(thr != NULL);
154 	DUK_ASSERT(thr->heap != NULL);
155 
156 	DUK_D(DUK_DPRINT("application called duk_debugger_notify() with nvalues=%ld", (long) nvalues));
157 
158 	top = duk_get_top(ctx);
159 	if (top < nvalues) {
160 		DUK_ERROR_API(thr, "not enough stack values for notify");
161 		return ret;  /* unreachable */
162 	}
163 	if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
164 		duk_debug_write_notify(thr, DUK_DBG_CMD_APPNOTIFY);
165 		for (idx = top - nvalues; idx < top; idx++) {
166 			duk_tval *tv = DUK_GET_TVAL_POSIDX(ctx, idx);
167 			duk_debug_write_tval(thr, tv);
168 		}
169 		duk_debug_write_eom(thr);
170 
171 		/* Return non-zero (true) if we have a good reason to believe
172 		 * the notify was delivered; if we're still attached at least
173 		 * a transport error was not indicated by the transport write
174 		 * callback.  This is not a 100% guarantee of course.
175 		 */
176 		if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
177 			ret = 1;
178 		}
179 	}
180 	duk_pop_n(ctx, nvalues);
181 	return ret;
182 }
183 
duk_debugger_pause(duk_context * ctx)184 DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) {
185 	duk_hthread *thr;
186 
187 	DUK_ASSERT_CTX_VALID(ctx);
188 	thr = (duk_hthread *) ctx;
189 	DUK_ASSERT(thr != NULL);
190 	DUK_ASSERT(thr->heap != NULL);
191 
192 	DUK_D(DUK_DPRINT("application called duk_debugger_pause()"));
193 
194 	/* Treat like a debugger statement: ignore when not attached. */
195 	if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
196 		DUK_HEAP_SET_PAUSED(thr->heap);
197 
198 		/* Pause on the next opcode executed.  This is always safe to do even
199 		 * inside the debugger message loop: the interrupt counter will be reset
200 		 * to its proper value when the message loop exits.
201 		 */
202 		thr->interrupt_init = 1;
203 		thr->interrupt_counter = 0;
204 	}
205 }
206 
207 #else  /* DUK_USE_DEBUGGER_SUPPORT */
208 
duk_debugger_attach_custom(duk_context * ctx,duk_debug_read_function read_cb,duk_debug_write_function write_cb,duk_debug_peek_function peek_cb,duk_debug_read_flush_function read_flush_cb,duk_debug_write_flush_function write_flush_cb,duk_debug_request_function request_cb,duk_debug_detached_function detached_cb,void * udata)209 DUK_EXTERNAL void duk_debugger_attach_custom(duk_context *ctx,
210                                              duk_debug_read_function read_cb,
211                                              duk_debug_write_function write_cb,
212                                              duk_debug_peek_function peek_cb,
213                                              duk_debug_read_flush_function read_flush_cb,
214                                              duk_debug_write_flush_function write_flush_cb,
215                                              duk_debug_request_function request_cb,
216                                              duk_debug_detached_function detached_cb,
217                                              void *udata) {
218 	DUK_ASSERT_CTX_VALID(ctx);
219 	DUK_UNREF(read_cb);
220 	DUK_UNREF(write_cb);
221 	DUK_UNREF(peek_cb);
222 	DUK_UNREF(read_flush_cb);
223 	DUK_UNREF(write_flush_cb);
224 	DUK_UNREF(request_cb);
225 	DUK_UNREF(detached_cb);
226 	DUK_UNREF(udata);
227 	DUK_ERROR_API((duk_hthread *) ctx, "no debugger support");
228 }
229 
duk_debugger_detach(duk_context * ctx)230 DUK_EXTERNAL void duk_debugger_detach(duk_context *ctx) {
231 	DUK_ASSERT_CTX_VALID(ctx);
232 	DUK_ERROR_API((duk_hthread *) ctx, "no debugger support");
233 }
234 
duk_debugger_cooperate(duk_context * ctx)235 DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) {
236 	/* nop */
237 	DUK_ASSERT_CTX_VALID(ctx);
238 	DUK_UNREF(ctx);
239 }
240 
duk_debugger_notify(duk_context * ctx,duk_idx_t nvalues)241 DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) {
242 	duk_idx_t top;
243 
244 	DUK_ASSERT_CTX_VALID(ctx);
245 
246 	top = duk_get_top(ctx);
247 	if (top < nvalues) {
248 		DUK_ERROR_API((duk_hthread *) ctx, "not enough stack values for notify");
249 		return 0;  /* unreachable */
250 	}
251 
252 	/* No debugger support, just pop values. */
253 	duk_pop_n(ctx, nvalues);
254 	return 0;
255 }
256 
duk_debugger_pause(duk_context * ctx)257 DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) {
258 	/* Treat like debugger statement: nop */
259 	DUK_ASSERT_CTX_VALID(ctx);
260 	DUK_UNREF(ctx);
261 }
262 
263 #endif  /* DUK_USE_DEBUGGER_SUPPORT */
264