]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* |
2 | * Debugging related API calls | |
3 | */ | |
4 | ||
5 | #include "duk_internal.h" | |
6 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
235 | DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) { | |
236 | /* nop */ | |
237 | DUK_ASSERT_CTX_VALID(ctx); | |
238 | DUK_UNREF(ctx); | |
239 | } | |
240 | ||
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 | ||
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 */ |