libexpr: fix --debugger --ignore-try

a65e9e5828 did not inform `tryEval` that
(as far as it's concerned) `state.debug` moved to `state.errors.debug`
and changed types. this resulted in the REPL erroneously coming up, that
REPL having a non-debug state, and segfaulting after that REPL exited.

it's probably good that `state.debug` isn't mutated by `--ignore-try`
anymore.

Change-Id: I1918e93edacd626452aa423fc2eb825080738835
Fixes: a65e9e5828 ("libexpr: extract eval error creation into new type")
Signed-off-by: Dusk Banks <me@bb010g.com>
This commit is contained in:
Dusk Banks
2025-01-30 18:46:14 -08:00
parent 050cf17307
commit 6a583136b7
4 changed files with 42 additions and 20 deletions
@@ -0,0 +1,9 @@
---
synopsis: "Fix `--debugger --ignore-try`"
issues: []
cls: [2440]
category: "Fixes"
credits: ["bb010g"]
---
When in debug mode (e.g. from using the `--debugger` flag), enabling [`ignore-try`](@docroot@/command-ref/conf-file.md#conf-ignore-try) once again properly disables debug REPLs within [`builtins.tryEval`](@docroot@/language/builtins.md#builtins-tryEval) calls. Previously, a debug REPL would be started as if `ignore-try` was disabled, but that REPL wouldn't actually be in debug mode, and upon exiting the REPL the evaluating process would segfault.
+24 -20
View File
@@ -643,29 +643,33 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va
{
auto attrs = state.ctx.buildBindings(2);
std::optional<MaintainCount<int>> trylevel;
std::unique_ptr<DebugState> savedDebug;
if (state.ctx.debug) {
trylevel.emplace(state.ctx.debug->trylevel);
if (evalSettings.ignoreExceptionsDuringTry) {
/* to prevent starting the repl from exceptions withing a tryEval, null it. */
savedDebug = std::move(state.ctx.debug);
{
std::optional<MaintainCount<int>> trylevel;
DebugState * savedDebug = nullptr;
KJ_DEFER({
if (savedDebug) {
state.ctx.errors.debug = savedDebug;
}
});
if (state.ctx.errors.debug != nullptr) {
trylevel.emplace(state.ctx.errors.debug->trylevel);
if (evalSettings.ignoreExceptionsDuringTry) {
/* to prevent starting the repl from exceptions within a tryEval, null it. */
savedDebug = state.ctx.errors.debug;
state.ctx.errors.debug = nullptr;
}
}
try {
state.forceValue(*args[0], pos);
attrs.insert(state.ctx.s.value, args[0]);
attrs.alloc("success").mkBool(true);
} catch (AssertionError & e) {
attrs.alloc(state.ctx.s.value).mkBool(false);
attrs.alloc("success").mkBool(false);
}
}
try {
state.forceValue(*args[0], pos);
attrs.insert(state.ctx.s.value, args[0]);
attrs.alloc("success").mkBool(true);
} catch (AssertionError & e) {
attrs.alloc(state.ctx.s.value).mkBool(false);
attrs.alloc("success").mkBool(false);
}
// restore the debugRepl pointer if we saved it earlier.
if (savedDebug)
state.ctx.debug = std::move(savedDebug);
v.mkAttrs(attrs);
}
@@ -0,0 +1,8 @@
@args --debugger --ignore-try
we don't enter a debug repl through tryEval
nix-repl> (builtins.tryEval ((x: throw "foo") 1)).success
false
no segfault either
nix-repl> :quit
@@ -187,5 +187,6 @@ REPL_TEST(stack_vars);
REPL_TEST(errors);
REPL_TEST(idempotent);
REPL_TEST(debug_frames);
REPL_TEST(debug_ignore_try);
}; // namespace nix