Merge pull request #9920 from 9999years/forbid-nested-debuggers
Forbid nested debuggers
(cherry picked from commit e164b39ee90fd655dbb7f479fdd4fbe38cc883bd)
Change-Id: Iff62f40fd251116516a63e2d3f9fb5b21480b16d
This commit is contained in:
parent
45623f077f
commit
6c29016a09
32
doc/manual/rl-next/forbid-nested-debuggers.md
Normal file
32
doc/manual/rl-next/forbid-nested-debuggers.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
synopsis: Nested debuggers are no longer supported
|
||||||
|
prs: 9920
|
||||||
|
---
|
||||||
|
|
||||||
|
Previously, evaluating an expression that throws an error in the debugger would
|
||||||
|
enter a second, nested debugger:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix-repl> builtins.throw "what"
|
||||||
|
error: what
|
||||||
|
|
||||||
|
|
||||||
|
Starting REPL to allow you to inspect the current state of the evaluator.
|
||||||
|
|
||||||
|
Welcome to Nix 2.18.1. Type :? for help.
|
||||||
|
|
||||||
|
nix-repl>
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, it just prints the error message like `nix repl`:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix-repl> builtins.throw "what"
|
||||||
|
error:
|
||||||
|
… while calling the 'throw' builtin
|
||||||
|
at «string»:1:1:
|
||||||
|
1| builtins.throw "what"
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: what
|
||||||
|
```
|
|
@ -233,12 +233,6 @@ ReplExitStatus NixRepl::mainLoop()
|
||||||
printMsg(lvlError, e.msg());
|
printMsg(lvlError, e.msg());
|
||||||
}
|
}
|
||||||
} catch (EvalError & e) {
|
} catch (EvalError & e) {
|
||||||
// in debugger mode, an EvalError should trigger another repl session.
|
|
||||||
// when that session returns the exception will land here. No need to show it again;
|
|
||||||
// show the error for this repl session instead.
|
|
||||||
if (state->debugRepl && !state->debugTraces.empty())
|
|
||||||
showDebugTrace(std::cout, state->positions, state->debugTraces.front());
|
|
||||||
else
|
|
||||||
printMsg(lvlError, e.msg());
|
printMsg(lvlError, e.msg());
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
printMsg(lvlError, e.msg());
|
printMsg(lvlError, e.msg());
|
||||||
|
|
|
@ -778,10 +778,24 @@ std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const Stati
|
||||||
return vm;
|
return vm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets `inDebugger` to true on construction and false on destruction.
|
||||||
|
*/
|
||||||
|
class DebuggerGuard {
|
||||||
|
bool & inDebugger;
|
||||||
|
public:
|
||||||
|
DebuggerGuard(bool & inDebugger) : inDebugger(inDebugger) {
|
||||||
|
inDebugger = true;
|
||||||
|
}
|
||||||
|
~DebuggerGuard() {
|
||||||
|
inDebugger = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & expr)
|
void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & expr)
|
||||||
{
|
{
|
||||||
// double check we've got the debugRepl function pointer.
|
// Make sure we have a debugger to run and we're not already in a debugger.
|
||||||
if (!debugRepl)
|
if (!debugRepl || inDebugger)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto dts =
|
auto dts =
|
||||||
|
@ -808,6 +822,7 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr &
|
||||||
auto se = getStaticEnv(expr);
|
auto se = getStaticEnv(expr);
|
||||||
if (se) {
|
if (se) {
|
||||||
auto vm = mapStaticEnvBindings(symbols, *se.get(), env);
|
auto vm = mapStaticEnvBindings(symbols, *se.get(), env);
|
||||||
|
DebuggerGuard _guard(inDebugger);
|
||||||
auto exitStatus = (debugRepl)(ref<EvalState>(shared_from_this()), *vm);
|
auto exitStatus = (debugRepl)(ref<EvalState>(shared_from_this()), *vm);
|
||||||
switch (exitStatus) {
|
switch (exitStatus) {
|
||||||
case ReplExitStatus::QuitAll:
|
case ReplExitStatus::QuitAll:
|
||||||
|
|
|
@ -152,6 +152,7 @@ struct DebugTrace {
|
||||||
bool isError;
|
bool isError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class EvalState : public std::enable_shared_from_this<EvalState>
|
class EvalState : public std::enable_shared_from_this<EvalState>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -210,6 +211,7 @@ public:
|
||||||
*/
|
*/
|
||||||
ReplExitStatus (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
ReplExitStatus (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
||||||
bool debugStop;
|
bool debugStop;
|
||||||
|
bool inDebugger = false;
|
||||||
int trylevel;
|
int trylevel;
|
||||||
std::list<DebugTrace> debugTraces;
|
std::list<DebugTrace> debugTraces;
|
||||||
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
builtins.break {}
|
|
@ -0,0 +1,39 @@
|
||||||
|
we enter a debugger via builtins.break in the input file.
|
||||||
|
|
||||||
|
info: breakpoint reached
|
||||||
|
|
||||||
|
causing another debugger even should not nest, but simply
|
||||||
|
print the error, skip the breakpoint, etc as appropriate.
|
||||||
|
|
||||||
|
nix-repl> "values show"
|
||||||
|
"values show"
|
||||||
|
|
||||||
|
nix-repl> builtins.break 2
|
||||||
|
2
|
||||||
|
|
||||||
|
nix-repl> builtins.throw "foo"
|
||||||
|
error:
|
||||||
|
… while calling the 'throw' builtin
|
||||||
|
at «string»:1:1:
|
||||||
|
1| builtins.throw "foo"
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: foo
|
||||||
|
|
||||||
|
nix-repl> assert false; 2
|
||||||
|
error: assertion 'false' failed
|
||||||
|
at «string»:1:1:
|
||||||
|
1| assert false; 2
|
||||||
|
| ^
|
||||||
|
|
||||||
|
exiting the debug frame should allow another to open.
|
||||||
|
|
||||||
|
nix-repl> :c
|
||||||
|
|
||||||
|
nix-repl> builtins.throw "bar"
|
||||||
|
error: bar
|
||||||
|
|
||||||
|
and once again, more breakpoints are ignored.
|
||||||
|
|
||||||
|
nix-repl> builtins.break 3
|
||||||
|
3
|
|
@ -126,5 +126,6 @@ DEBUGGER_TEST(regression_9918);
|
||||||
DEBUGGER_TEST(regression_9917);
|
DEBUGGER_TEST(regression_9917);
|
||||||
DEBUGGER_TEST(regression_l145);
|
DEBUGGER_TEST(regression_l145);
|
||||||
DEBUGGER_TEST(stack_vars);
|
DEBUGGER_TEST(stack_vars);
|
||||||
|
DEBUGGER_TEST(no_nested_debuggers);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue