262 lines
8.0 KiB
C
262 lines
8.0 KiB
C
/*
|
|
* Debugging related API calls
|
|
*/
|
|
|
|
#include "third_party/duktape/duk_internal.h"
|
|
|
|
#if defined(DUK_USE_JSON_SUPPORT)
|
|
DUK_EXTERNAL void duk_push_context_dump(duk_hthread *thr) {
|
|
duk_idx_t idx;
|
|
duk_idx_t top;
|
|
|
|
DUK_ASSERT_API_ENTRY(thr);
|
|
|
|
/* We don't duk_require_stack() here now, but rely on the caller having
|
|
* enough space.
|
|
*/
|
|
|
|
top = duk_get_top(thr);
|
|
duk_push_bare_array(thr);
|
|
for (idx = 0; idx < top; idx++) {
|
|
duk_dup(thr, idx);
|
|
duk_put_prop_index(thr, -2, (duk_uarridx_t) idx);
|
|
}
|
|
|
|
/* XXX: conversion errors should not propagate outwards.
|
|
* Perhaps values need to be coerced individually?
|
|
*/
|
|
duk_bi_json_stringify_helper(thr,
|
|
duk_get_top_index(thr), /*idx_value*/
|
|
DUK_INVALID_INDEX, /*idx_replacer*/
|
|
DUK_INVALID_INDEX, /*idx_space*/
|
|
DUK_JSON_FLAG_EXT_CUSTOM |
|
|
DUK_JSON_FLAG_ASCII_ONLY |
|
|
DUK_JSON_FLAG_AVOID_KEY_QUOTES /*flags*/);
|
|
|
|
duk_push_sprintf(thr, "ctx: top=%ld, stack=%s", (long) top, (const char *) duk_safe_to_string(thr, -1));
|
|
duk_replace(thr, -3); /* [ ... arr jsonx(arr) res ] -> [ ... res jsonx(arr) ] */
|
|
duk_pop(thr);
|
|
DUK_ASSERT(duk_is_string(thr, -1));
|
|
}
|
|
#else /* DUK_USE_JSON_SUPPORT */
|
|
DUK_EXTERNAL void duk_push_context_dump(duk_hthread *thr) {
|
|
DUK_ASSERT_API_ENTRY(thr);
|
|
DUK_ERROR_UNSUPPORTED(thr);
|
|
DUK_WO_NORETURN(return;);
|
|
}
|
|
#endif /* DUK_USE_JSON_SUPPORT */
|
|
|
|
#if defined(DUK_USE_DEBUGGER_SUPPORT)
|
|
|
|
DUK_EXTERNAL void duk_debugger_attach(duk_hthread *thr,
|
|
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) {
|
|
duk_heap *heap;
|
|
const char *str;
|
|
duk_size_t len;
|
|
|
|
/* XXX: should there be an error or an automatic detach if
|
|
* already attached?
|
|
*/
|
|
|
|
DUK_D(DUK_DPRINT("application called duk_debugger_attach()"));
|
|
|
|
DUK_ASSERT_API_ENTRY(thr);
|
|
DUK_ASSERT(read_cb != NULL);
|
|
DUK_ASSERT(write_cb != NULL);
|
|
/* Other callbacks are optional. */
|
|
|
|
heap = thr->heap;
|
|
heap->dbg_read_cb = read_cb;
|
|
heap->dbg_write_cb = write_cb;
|
|
heap->dbg_peek_cb = peek_cb;
|
|
heap->dbg_read_flush_cb = read_flush_cb;
|
|
heap->dbg_write_flush_cb = write_flush_cb;
|
|
heap->dbg_request_cb = request_cb;
|
|
heap->dbg_detached_cb = detached_cb;
|
|
heap->dbg_udata = udata;
|
|
heap->dbg_have_next_byte = 0;
|
|
|
|
/* Start in paused state. */
|
|
heap->dbg_processing = 0;
|
|
heap->dbg_state_dirty = 0;
|
|
heap->dbg_force_restart = 0;
|
|
heap->dbg_pause_flags = 0;
|
|
heap->dbg_pause_act = NULL;
|
|
heap->dbg_pause_startline = 0;
|
|
heap->dbg_exec_counter = 0;
|
|
heap->dbg_last_counter = 0;
|
|
heap->dbg_last_time = 0.0;
|
|
duk_debug_set_paused(heap); /* XXX: overlap with fields above */
|
|
|
|
/* Send version identification and flush right afterwards. Note that
|
|
* we must write raw, unframed bytes here.
|
|
*/
|
|
duk_push_sprintf(thr, "%ld %ld %s %s\n",
|
|
(long) DUK_DEBUG_PROTOCOL_VERSION,
|
|
(long) DUK_VERSION,
|
|
(const char *) DUK_GIT_DESCRIBE,
|
|
(const char *) DUK_USE_TARGET_INFO);
|
|
str = duk_get_lstring(thr, -1, &len);
|
|
DUK_ASSERT(str != NULL);
|
|
duk_debug_write_bytes(thr, (const duk_uint8_t *) str, len);
|
|
duk_debug_write_flush(thr);
|
|
duk_pop(thr);
|
|
}
|
|
|
|
DUK_EXTERNAL void duk_debugger_detach(duk_hthread *thr) {
|
|
DUK_D(DUK_DPRINT("application called duk_debugger_detach()"));
|
|
|
|
DUK_ASSERT_API_ENTRY(thr);
|
|
DUK_ASSERT(thr->heap != NULL);
|
|
|
|
/* Can be called multiple times with no harm. */
|
|
duk_debug_do_detach(thr->heap);
|
|
}
|
|
|
|
DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) {
|
|
duk_bool_t processed_messages;
|
|
|
|
DUK_ASSERT_API_ENTRY(thr);
|
|
DUK_ASSERT(thr->heap != NULL);
|
|
|
|
if (!duk_debug_is_attached(thr->heap)) {
|
|
return;
|
|
}
|
|
if (thr->callstack_curr != NULL || thr->heap->dbg_processing) {
|
|
/* Calling duk_debugger_cooperate() while Duktape is being
|
|
* called into is not supported. This is not a 100% check
|
|
* but prevents any damage in most cases.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
processed_messages = duk_debug_process_messages(thr, 1 /*no_block*/);
|
|
DUK_UNREF(processed_messages);
|
|
}
|
|
|
|
DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) {
|
|
duk_idx_t top;
|
|
duk_idx_t idx;
|
|
duk_bool_t ret = 0;
|
|
|
|
DUK_ASSERT_API_ENTRY(thr);
|
|
DUK_ASSERT(thr->heap != NULL);
|
|
|
|
DUK_D(DUK_DPRINT("application called duk_debugger_notify() with nvalues=%ld", (long) nvalues));
|
|
|
|
top = duk_get_top(thr);
|
|
if (top < nvalues) {
|
|
DUK_ERROR_RANGE(thr, "not enough stack values for notify");
|
|
DUK_WO_NORETURN(return 0;);
|
|
}
|
|
if (duk_debug_is_attached(thr->heap)) {
|
|
duk_debug_write_notify(thr, DUK_DBG_CMD_APPNOTIFY);
|
|
for (idx = top - nvalues; idx < top; idx++) {
|
|
duk_tval *tv = DUK_GET_TVAL_POSIDX(thr, idx);
|
|
duk_debug_write_tval(thr, tv);
|
|
}
|
|
duk_debug_write_eom(thr);
|
|
|
|
/* Return non-zero (true) if we have a good reason to believe
|
|
* the notify was delivered; if we're still attached at least
|
|
* a transport error was not indicated by the transport write
|
|
* callback. This is not a 100% guarantee of course.
|
|
*/
|
|
if (duk_debug_is_attached(thr->heap)) {
|
|
ret = 1;
|
|
}
|
|
}
|
|
duk_pop_n(thr, nvalues);
|
|
return ret;
|
|
}
|
|
|
|
DUK_EXTERNAL void duk_debugger_pause(duk_hthread *thr) {
|
|
DUK_ASSERT_API_ENTRY(thr);
|
|
DUK_ASSERT(thr->heap != NULL);
|
|
|
|
DUK_D(DUK_DPRINT("application called duk_debugger_pause()"));
|
|
|
|
/* Treat like a debugger statement: ignore when not attached. */
|
|
if (duk_debug_is_attached(thr->heap)) {
|
|
if (duk_debug_is_paused(thr->heap)) {
|
|
DUK_D(DUK_DPRINT("duk_debugger_pause() called when already paused; ignoring"));
|
|
} else {
|
|
duk_debug_set_paused(thr->heap);
|
|
|
|
/* Pause on the next opcode executed. This is always safe to do even
|
|
* inside the debugger message loop: the interrupt counter will be reset
|
|
* to its proper value when the message loop exits.
|
|
*/
|
|
thr->interrupt_init = 1;
|
|
thr->interrupt_counter = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
#else /* DUK_USE_DEBUGGER_SUPPORT */
|
|
|
|
DUK_EXTERNAL void duk_debugger_attach(duk_hthread *thr,
|
|
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) {
|
|
DUK_ASSERT_API_ENTRY(thr);
|
|
DUK_UNREF(read_cb);
|
|
DUK_UNREF(write_cb);
|
|
DUK_UNREF(peek_cb);
|
|
DUK_UNREF(read_flush_cb);
|
|
DUK_UNREF(write_flush_cb);
|
|
DUK_UNREF(request_cb);
|
|
DUK_UNREF(detached_cb);
|
|
DUK_UNREF(udata);
|
|
DUK_ERROR_TYPE(thr, "no debugger support");
|
|
DUK_WO_NORETURN(return;);
|
|
}
|
|
|
|
DUK_EXTERNAL void duk_debugger_detach(duk_hthread *thr) {
|
|
DUK_ASSERT_API_ENTRY(thr);
|
|
DUK_ERROR_TYPE(thr, "no debugger support");
|
|
DUK_WO_NORETURN(return;);
|
|
}
|
|
|
|
DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) {
|
|
/* nop */
|
|
DUK_ASSERT_API_ENTRY(thr);
|
|
DUK_UNREF(thr);
|
|
}
|
|
|
|
DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) {
|
|
duk_idx_t top;
|
|
|
|
DUK_ASSERT_API_ENTRY(thr);
|
|
|
|
top = duk_get_top(thr);
|
|
if (top < nvalues) {
|
|
DUK_ERROR_RANGE_INVALID_COUNT(thr);
|
|
DUK_WO_NORETURN(return 0;);
|
|
}
|
|
|
|
/* No debugger support, just pop values. */
|
|
duk_pop_n(thr, nvalues);
|
|
return 0;
|
|
}
|
|
|
|
DUK_EXTERNAL void duk_debugger_pause(duk_hthread *thr) {
|
|
/* Treat like debugger statement: nop */
|
|
DUK_ASSERT_API_ENTRY(thr);
|
|
DUK_UNREF(thr);
|
|
}
|
|
|
|
#endif /* DUK_USE_DEBUGGER_SUPPORT */
|