mirror of
https://github.com/NixOS/nix.git
synced 2024-11-25 16:23:02 +00:00
Merge pull request #9916 from 9999years/quit-whole-program-from-debugger
`:quit` in the debugger should quit the whole program
This commit is contained in:
commit
a1b1070dbe
@ -52,6 +52,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.
|
||||
*/
|
||||
Quit,
|
||||
/**
|
||||
* The user exited with `:continue`. The REPL should exit, but the program
|
||||
* should continue running.
|
||||
*/
|
||||
Continue,
|
||||
/**
|
||||
* The user did not exit. The REPL should request another line of input.
|
||||
*/
|
||||
PromptAgain,
|
||||
};
|
||||
|
||||
struct NixRepl
|
||||
: AbstractNixRepl
|
||||
#if HAVE_BOEHMGC
|
||||
@ -75,13 +96,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);
|
||||
@ -246,7 +267,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 = "";
|
||||
@ -287,15 +308,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::Quit:
|
||||
return ReplExitStatus::QuitAll;
|
||||
case ProcessLineResult::Continue:
|
||||
return ReplExitStatus::Continue;
|
||||
case ProcessLineResult::PromptAgain:
|
||||
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
|
||||
@ -483,10 +514,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::PromptAgain;
|
||||
|
||||
_isInterrupted = false;
|
||||
|
||||
@ -581,13 +613,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::Continue;
|
||||
}
|
||||
|
||||
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::Continue;
|
||||
}
|
||||
|
||||
else if (command == ":a" || command == ":add") {
|
||||
@ -730,8 +762,7 @@ bool NixRepl::processLine(std::string line)
|
||||
|
||||
else if (command == ":q" || command == ":quit") {
|
||||
state->debugStop = false;
|
||||
state->debugQuit = true;
|
||||
return false;
|
||||
return ProcessLineResult::Quit;
|
||||
}
|
||||
|
||||
else if (command == ":doc") {
|
||||
@ -792,7 +823,7 @@ bool NixRepl::processLine(std::string line)
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return ProcessLineResult::PromptAgain;
|
||||
}
|
||||
|
||||
void NixRepl::loadFile(const Path & path)
|
||||
@ -923,7 +954,7 @@ std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
|
||||
}
|
||||
|
||||
|
||||
void AbstractNixRepl::runSimple(
|
||||
ReplExitStatus AbstractNixRepl::runSimple(
|
||||
ref<EvalState> evalState,
|
||||
const ValMap & extraEnv)
|
||||
{
|
||||
@ -945,7 +976,7 @@ void AbstractNixRepl::runSimple(
|
||||
for (auto & [name, value] : extraEnv)
|
||||
repl->addVarToScope(repl->state->symbols.create(name), *value);
|
||||
|
||||
repl->mainLoop();
|
||||
return repl->mainLoop();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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"
|
||||
@ -416,7 +417,6 @@ EvalState::EvalState(
|
||||
, buildStore(buildStore ? buildStore : store)
|
||||
, debugRepl(nullptr)
|
||||
, debugStop(false)
|
||||
, debugQuit(false)
|
||||
, trylevel(0)
|
||||
, regexCache(makeRegexCache())
|
||||
#if HAVE_BOEHMGC
|
||||
@ -792,7 +792,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
@ -219,9 +220,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;
|
||||
@ -758,7 +758,6 @@ struct DebugTraceStacker {
|
||||
DebugTraceStacker(EvalState & evalState, DebugTrace t);
|
||||
~DebugTraceStacker()
|
||||
{
|
||||
// assert(evalState.debugTraces.front() == trace);
|
||||
evalState.debugTraces.pop_front();
|
||||
}
|
||||
EvalState & evalState;
|
||||
|
@ -760,15 +760,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.
|
||||
@ -879,7 +870,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. */
|
||||
|
20
src/libexpr/repl-exit-status.hh
Normal file
20
src/libexpr/repl-exit-status.hh
Normal file
@ -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,
|
||||
};
|
||||
|
||||
}
|
@ -408,6 +408,4 @@ PrintFreed::~PrintFreed()
|
||||
showBytes(results.bytesFreed));
|
||||
}
|
||||
|
||||
Exit::~Exit() { }
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "common-args.hh"
|
||||
#include "path.hh"
|
||||
#include "derived-path.hh"
|
||||
#include "exit.hh"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
@ -15,15 +16,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);
|
||||
|
||||
/**
|
||||
|
7
src/libutil/exit.cc
Normal file
7
src/libutil/exit.cc
Normal file
@ -0,0 +1,7 @@
|
||||
#include "exit.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
Exit::~Exit() {}
|
||||
|
||||
}
|
19
src/libutil/exit.hh
Normal file
19
src/libutil/exit.hh
Normal file
@ -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();
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user