aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreldritch horrors <pennae@lix.systems>2024-03-08 09:19:15 +0100
committereldritch horrors <pennae@lix.systems>2024-03-09 10:17:26 -0700
commit992d99592f1022593e4df276e39e8f4f65822f74 (patch)
tree4d688284a84b4dea63c447d7283af0b30e201cf0
parent6b11c2cd7020869b796dc8e6904b358c9e41a23c (diff)
`:quit` in the debugger should quit the whole program
(cherry picked from commit 2a8fe9a93837733e9dd9ed5c078734a35b203e14) Change-Id: I71dadfef6b24d9272b206e9e2c408040559d8a1c
-rw-r--r--src/libcmd/repl.cc63
-rw-r--r--src/libcmd/repl.hh4
-rw-r--r--src/libexpr/eval.cc14
-rw-r--r--src/libexpr/eval.hh5
-rw-r--r--src/libexpr/primops.cc11
-rw-r--r--src/libexpr/repl-exit-status.hh20
-rw-r--r--src/libmain/shared.cc2
-rw-r--r--src/libmain/shared.hh10
-rw-r--r--src/libutil/exit.cc7
-rw-r--r--src/libutil/exit.hh19
10 files changed, 111 insertions, 44 deletions
diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc
index bc5ff9525..ea2f4ebe6 100644
--- a/src/libcmd/repl.cc
+++ b/src/libcmd/repl.cc
@@ -49,6 +49,27 @@ extern "C" {
namespace nix {
+/**
+ * Returned by `NixRepl::processLine`.
+ */
+enum class ProcessLineResult {
+ /**
+ * The user exited with `:quit`. The REPL should exit. The surrounding
+ * program or evaluation (e.g., if the REPL was acting as the debugger)
+ * should also exit.
+ */
+ QuitAll,
+ /**
+ * The user exited with `:continue`. The REPL should exit, but the program
+ * should continue running.
+ */
+ QuitOnce,
+ /**
+ * The user did not exit. The REPL should request another line of input.
+ */
+ Continue,
+};
+
struct NixRepl
: AbstractNixRepl
#if HAVE_BOEHMGC
@@ -72,13 +93,13 @@ struct NixRepl
std::function<AnnotatedValues()> getValues);
virtual ~NixRepl();
- void mainLoop() override;
+ ReplExitStatus mainLoop() override;
void initEnv() override;
StringSet completePrefix(const std::string & prefix);
bool getLine(std::string & input, const std::string & prompt);
StorePath getDerivationPath(Value & v);
- bool processLine(std::string line);
+ ProcessLineResult processLine(std::string line);
void loadFile(const Path & path);
void loadFlake(const std::string & flakeRef);
@@ -243,7 +264,7 @@ static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positi
static bool isFirstRepl = true;
-void NixRepl::mainLoop()
+ReplExitStatus NixRepl::mainLoop()
{
if (isFirstRepl) {
std::string_view debuggerNotice = "";
@@ -284,15 +305,25 @@ void NixRepl::mainLoop()
// When continuing input from previous lines, don't print a prompt, just align to the same
// number of chars as the prompt.
if (!getLine(input, input.empty() ? "nix-repl> " : " ")) {
- // ctrl-D should exit the debugger.
+ // Ctrl-D should exit the debugger.
state->debugStop = false;
- state->debugQuit = true;
logger->cout("");
- break;
+ // TODO: Should Ctrl-D exit just the current debugger session or
+ // the entire program?
+ return ReplExitStatus::QuitAll;
}
logger->resume();
try {
- if (!removeWhitespace(input).empty() && !processLine(input)) return;
+ switch (processLine(input)) {
+ case ProcessLineResult::QuitAll:
+ return ReplExitStatus::QuitAll;
+ case ProcessLineResult::QuitOnce:
+ return ReplExitStatus::Continue;
+ case ProcessLineResult::Continue:
+ break;
+ default:
+ abort();
+ }
} catch (ParseError & e) {
if (e.msg().find("unexpected end of file") != std::string::npos) {
// For parse errors on incomplete input, we continue waiting for the next line of
@@ -479,10 +510,11 @@ void NixRepl::loadDebugTraceEnv(DebugTrace & dt)
}
}
-bool NixRepl::processLine(std::string line)
+ProcessLineResult NixRepl::processLine(std::string line)
{
line = trim(line);
- if (line == "") return true;
+ if (line.empty())
+ return ProcessLineResult::Continue;
_isInterrupted = false;
@@ -577,13 +609,13 @@ bool NixRepl::processLine(std::string line)
else if (state->debugRepl && (command == ":s" || command == ":step")) {
// set flag to stop at next DebugTrace; exit repl.
state->debugStop = true;
- return false;
+ return ProcessLineResult::QuitOnce;
}
else if (state->debugRepl && (command == ":c" || command == ":continue")) {
// set flag to run to next breakpoint or end of program; exit repl.
state->debugStop = false;
- return false;
+ return ProcessLineResult::QuitOnce;
}
else if (command == ":a" || command == ":add") {
@@ -726,8 +758,7 @@ bool NixRepl::processLine(std::string line)
else if (command == ":q" || command == ":quit") {
state->debugStop = false;
- state->debugQuit = true;
- return false;
+ return ProcessLineResult::QuitAll;
}
else if (command == ":doc") {
@@ -788,7 +819,7 @@ bool NixRepl::processLine(std::string line)
}
}
- return true;
+ return ProcessLineResult::Continue;
}
void NixRepl::loadFile(const Path & path)
@@ -919,7 +950,7 @@ std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
}
-void AbstractNixRepl::runSimple(
+ReplExitStatus AbstractNixRepl::runSimple(
ref<EvalState> evalState,
const ValMap & extraEnv)
{
@@ -941,7 +972,7 @@ void AbstractNixRepl::runSimple(
for (auto & [name, value] : extraEnv)
repl->addVarToScope(repl->state->symbols.create(name), *value);
- repl->mainLoop();
+ return repl->mainLoop();
}
}
diff --git a/src/libcmd/repl.hh b/src/libcmd/repl.hh
index 6d88883fe..21aa8bfc7 100644
--- a/src/libcmd/repl.hh
+++ b/src/libcmd/repl.hh
@@ -28,13 +28,13 @@ struct AbstractNixRepl
const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues);
- static void runSimple(
+ static ReplExitStatus runSimple(
ref<EvalState> evalState,
const ValMap & extraEnv);
virtual void initEnv() = 0;
- virtual void mainLoop() = 0;
+ virtual ReplExitStatus mainLoop() = 0;
};
}
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index ba9fe9c65..e15781404 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -3,6 +3,7 @@
#include "hash.hh"
#include "primops.hh"
#include "print-options.hh"
+#include "shared.hh"
#include "types.hh"
#include "util.hh"
#include "store-api.hh"
@@ -391,7 +392,6 @@ EvalState::EvalState(
, buildStore(buildStore ? buildStore : store)
, debugRepl(nullptr)
, debugStop(false)
- , debugQuit(false)
, trylevel(0)
, regexCache(makeRegexCache())
#if HAVE_BOEHMGC
@@ -798,7 +798,17 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr &
auto se = getStaticEnv(expr);
if (se) {
auto vm = mapStaticEnvBindings(symbols, *se.get(), env);
- (debugRepl)(ref<EvalState>(shared_from_this()), *vm);
+ auto exitStatus = (debugRepl)(ref<EvalState>(shared_from_this()), *vm);
+ switch (exitStatus) {
+ case ReplExitStatus::QuitAll:
+ if (error)
+ throw *error;
+ throw Exit(0);
+ case ReplExitStatus::Continue:
+ break;
+ default:
+ abort();
+ }
}
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 135e5fecb..5cdf34a47 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -11,6 +11,7 @@
#include "experimental-features.hh"
#include "input-accessor.hh"
#include "search-path.hh"
+#include "repl-exit-status.hh"
#include <map>
#include <optional>
@@ -207,9 +208,8 @@ public:
/**
* Debugger
*/
- void (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
+ ReplExitStatus (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
bool debugStop;
- bool debugQuit;
int trylevel;
std::list<DebugTrace> debugTraces;
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
@@ -753,7 +753,6 @@ struct DebugTraceStacker {
DebugTraceStacker(EvalState & evalState, DebugTrace t);
~DebugTraceStacker()
{
- // assert(evalState.debugTraces.front() == trace);
evalState.debugTraces.pop_front();
}
EvalState & evalState;
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 86db527f6..30933e191 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -754,15 +754,6 @@ static RegisterPrimOp primop_break({
auto & dt = state.debugTraces.front();
state.runDebugRepl(&error, dt.env, dt.expr);
-
- if (state.debugQuit) {
- // If the user elects to quit the repl, throw an exception.
- throw Error(ErrorInfo{
- .level = lvlInfo,
- .msg = HintFmt("quit the debugger"),
- .pos = nullptr,
- });
- }
}
// Return the value we were passed.
@@ -873,7 +864,7 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va
/* increment state.trylevel, and decrement it when this function returns. */
MaintainCount trylevel(state.trylevel);
- void (* savedDebugRepl)(ref<EvalState> es, const ValMap & extraEnv) = nullptr;
+ ReplExitStatus (* savedDebugRepl)(ref<EvalState> es, const ValMap & extraEnv) = nullptr;
if (state.debugRepl && evalSettings.ignoreExceptionsDuringTry)
{
/* to prevent starting the repl from exceptions withing a tryEval, null it. */
diff --git a/src/libexpr/repl-exit-status.hh b/src/libexpr/repl-exit-status.hh
new file mode 100644
index 000000000..08299ff61
--- /dev/null
+++ b/src/libexpr/repl-exit-status.hh
@@ -0,0 +1,20 @@
+#pragma once
+
+namespace nix {
+
+/**
+ * Exit status returned from the REPL.
+ */
+enum class ReplExitStatus {
+ /**
+ * The user exited with `:quit`. The program (e.g., if the REPL was acting
+ * as the debugger) should exit.
+ */
+ QuitAll,
+ /**
+ * The user exited with `:continue`. The program should continue running.
+ */
+ Continue,
+};
+
+}
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index 1b021d0b0..4ad1faec3 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -407,6 +407,4 @@ PrintFreed::~PrintFreed()
showBytes(results.bytesFreed));
}
-Exit::~Exit() { }
-
}
diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh
index 9415be78a..2b15d3368 100644
--- a/src/libmain/shared.hh
+++ b/src/libmain/shared.hh
@@ -6,6 +6,7 @@
#include "common-args.hh"
#include "path.hh"
#include "derived-path.hh"
+#include "exit.hh"
#include <signal.h>
@@ -14,15 +15,6 @@
namespace nix {
-class Exit : public std::exception
-{
-public:
- int status;
- Exit() : status(0) { }
- Exit(int status) : status(status) { }
- virtual ~Exit();
-};
-
int handleExceptions(const std::string & programName, std::function<void()> fun);
/**
diff --git a/src/libutil/exit.cc b/src/libutil/exit.cc
new file mode 100644
index 000000000..73cd8b04e
--- /dev/null
+++ b/src/libutil/exit.cc
@@ -0,0 +1,7 @@
+#include "exit.hh"
+
+namespace nix {
+
+Exit::~Exit() {}
+
+}
diff --git a/src/libutil/exit.hh b/src/libutil/exit.hh
new file mode 100644
index 000000000..55f33e62f
--- /dev/null
+++ b/src/libutil/exit.hh
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <exception>
+
+namespace nix {
+
+/**
+ * Exit the program with a given exit code.
+ */
+class Exit : public std::exception
+{
+public:
+ int status;
+ Exit() : status(0) { }
+ explicit Exit(int status) : status(status) { }
+ virtual ~Exit();
+};
+
+}