From 4daecc9a4113aa0198ddf30c90e59cc256a748d1 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 14 Dec 2024 10:34:36 -0600 Subject: [PATCH] Prevent await inside janet_call - address #1531 This was partially implemented before, but not in the case where the await or other signal itself was created by a C function. We have to separate code paths for generating signals - one via normal returns in janet_vm_continue, and the other via longjump. This adds handling for the longjump case, as well as improved messaging. --- src/core/capi.c | 7 +++++++ src/core/state.h | 1 + src/core/vm.c | 10 ++++++++++ src/include/janet.h | 1 + test/suite-ev.janet | 9 +++++++++ 5 files changed, 28 insertions(+) diff --git a/src/core/capi.c b/src/core/capi.c index 9d741332..d4169dc0 100644 --- a/src/core/capi.c +++ b/src/core/capi.c @@ -62,6 +62,13 @@ JANET_NO_RETURN static void janet_top_level_signal(const char *msg) { void janet_signalv(JanetSignal sig, Janet message) { if (janet_vm.return_reg != NULL) { + /* Should match logic in janet_call for coercing everything not ok to an error (no awaits, yields, etc.) */ + if (janet_vm.coerce_error && sig != JANET_SIGNAL_OK) { + if (sig != JANET_SIGNAL_ERROR) { + message = janet_wrap_string(janet_formatc("%v coerced from %s to error", message, janet_signal_names[sig])); + } + sig = JANET_SIGNAL_ERROR; + } *janet_vm.return_reg = message; if (NULL != janet_vm.fiber) { janet_vm.fiber->flags |= JANET_FIBER_DID_LONGJUMP; diff --git a/src/core/state.h b/src/core/state.h index 5d9192c4..d121f559 100644 --- a/src/core/state.h +++ b/src/core/state.h @@ -100,6 +100,7 @@ struct JanetVM { * return point for panics. */ jmp_buf *signal_buf; Janet *return_reg; + int coerce_error; /* The global registry for c functions. Used to store meta-data * along with otherwise bare c function pointers. */ diff --git a/src/core/vm.c b/src/core/vm.c index ccbc87e2..0e599aa3 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1373,7 +1373,10 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) { /* Run vm */ janet_vm.fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP; + int old_coerce_error = janet_vm.coerce_error; + janet_vm.coerce_error = 1; JanetSignal signal = run_vm(janet_vm.fiber, janet_wrap_nil()); + janet_vm.coerce_error = old_coerce_error; /* Teardown */ janet_vm.stackn = oldn; @@ -1384,6 +1387,10 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) { } if (signal != JANET_SIGNAL_OK) { + /* Should match logic in janet_signalv */ + if (signal != JANET_SIGNAL_ERROR) { + *janet_vm.return_reg = janet_wrap_string(janet_formatc("%v coerced from %s to error", *janet_vm.return_reg, janet_signal_names[signal])); + } janet_panicv(*janet_vm.return_reg); } @@ -1430,8 +1437,10 @@ void janet_try_init(JanetTryState *state) { state->vm_fiber = janet_vm.fiber; state->vm_jmp_buf = janet_vm.signal_buf; state->vm_return_reg = janet_vm.return_reg; + state->coerce_error = janet_vm.coerce_error; janet_vm.return_reg = &(state->payload); janet_vm.signal_buf = &(state->buf); + janet_vm.coerce_error = 0; } void janet_restore(JanetTryState *state) { @@ -1440,6 +1449,7 @@ void janet_restore(JanetTryState *state) { janet_vm.fiber = state->vm_fiber; janet_vm.signal_buf = state->vm_jmp_buf; janet_vm.return_reg = state->vm_return_reg; + janet_vm.coerce_error = state->coerce_error; } static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *out) { diff --git a/src/include/janet.h b/src/include/janet.h index e9c9429f..c91e5e60 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -1261,6 +1261,7 @@ typedef struct { /* new state */ jmp_buf buf; Janet payload; + int coerce_error; } JanetTryState; /***** END SECTION TYPES *****/ diff --git a/test/suite-ev.janet b/test/suite-ev.janet index f0e859bf..2b9ef2d9 100644 --- a/test/suite-ev.janet +++ b/test/suite-ev.janet @@ -465,4 +465,13 @@ # Close chat server (:close chat-server) +# Issue #1531 +(def c (ev/chan 0)) +(ev/spawn (while (def x (ev/take c)))) +(defn print-to-chan [x] (ev/give c x)) +(assert-error "coerce await inside janet_call to error" + (with-dyns [*out* print-to-chan] + (pp :foo))) +(ev/chan-close c) + (end-suite)