:quit
in the debugger should quit the whole program
(cherry picked from commit 2a8fe9a93837733e9dd9ed5c078734a35b203e14)
Change-Id: I71dadfef6b24d9272b206e9e2c408040559d8a1c
This commit is contained in:
parent
6b11c2cd70
commit
992d99592f
|
@ -49,6 +49,27 @@ extern "C" {
|
||||||
|
|
||||||
namespace nix {
|
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
|
struct NixRepl
|
||||||
: AbstractNixRepl
|
: AbstractNixRepl
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
|
@ -72,13 +93,13 @@ struct NixRepl
|
||||||
std::function<AnnotatedValues()> getValues);
|
std::function<AnnotatedValues()> getValues);
|
||||||
virtual ~NixRepl();
|
virtual ~NixRepl();
|
||||||
|
|
||||||
void mainLoop() override;
|
ReplExitStatus mainLoop() override;
|
||||||
void initEnv() override;
|
void initEnv() override;
|
||||||
|
|
||||||
StringSet completePrefix(const std::string & prefix);
|
StringSet completePrefix(const std::string & prefix);
|
||||||
bool getLine(std::string & input, const std::string & prompt);
|
bool getLine(std::string & input, const std::string & prompt);
|
||||||
StorePath getDerivationPath(Value & v);
|
StorePath getDerivationPath(Value & v);
|
||||||
bool processLine(std::string line);
|
ProcessLineResult processLine(std::string line);
|
||||||
|
|
||||||
void loadFile(const Path & path);
|
void loadFile(const Path & path);
|
||||||
void loadFlake(const std::string & flakeRef);
|
void loadFlake(const std::string & flakeRef);
|
||||||
|
@ -243,7 +264,7 @@ static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positi
|
||||||
|
|
||||||
static bool isFirstRepl = true;
|
static bool isFirstRepl = true;
|
||||||
|
|
||||||
void NixRepl::mainLoop()
|
ReplExitStatus NixRepl::mainLoop()
|
||||||
{
|
{
|
||||||
if (isFirstRepl) {
|
if (isFirstRepl) {
|
||||||
std::string_view debuggerNotice = "";
|
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
|
// When continuing input from previous lines, don't print a prompt, just align to the same
|
||||||
// number of chars as the prompt.
|
// number of chars as the prompt.
|
||||||
if (!getLine(input, input.empty() ? "nix-repl> " : " ")) {
|
if (!getLine(input, input.empty() ? "nix-repl> " : " ")) {
|
||||||
// ctrl-D should exit the debugger.
|
// Ctrl-D should exit the debugger.
|
||||||
state->debugStop = false;
|
state->debugStop = false;
|
||||||
state->debugQuit = true;
|
|
||||||
logger->cout("");
|
logger->cout("");
|
||||||
break;
|
// TODO: Should Ctrl-D exit just the current debugger session or
|
||||||
|
// the entire program?
|
||||||
|
return ReplExitStatus::QuitAll;
|
||||||
}
|
}
|
||||||
logger->resume();
|
logger->resume();
|
||||||
try {
|
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) {
|
} catch (ParseError & e) {
|
||||||
if (e.msg().find("unexpected end of file") != std::string::npos) {
|
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
|
// 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);
|
line = trim(line);
|
||||||
if (line == "") return true;
|
if (line.empty())
|
||||||
|
return ProcessLineResult::Continue;
|
||||||
|
|
||||||
_isInterrupted = false;
|
_isInterrupted = false;
|
||||||
|
|
||||||
|
@ -577,13 +609,13 @@ bool NixRepl::processLine(std::string line)
|
||||||
else if (state->debugRepl && (command == ":s" || command == ":step")) {
|
else if (state->debugRepl && (command == ":s" || command == ":step")) {
|
||||||
// set flag to stop at next DebugTrace; exit repl.
|
// set flag to stop at next DebugTrace; exit repl.
|
||||||
state->debugStop = true;
|
state->debugStop = true;
|
||||||
return false;
|
return ProcessLineResult::QuitOnce;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (state->debugRepl && (command == ":c" || command == ":continue")) {
|
else if (state->debugRepl && (command == ":c" || command == ":continue")) {
|
||||||
// set flag to run to next breakpoint or end of program; exit repl.
|
// set flag to run to next breakpoint or end of program; exit repl.
|
||||||
state->debugStop = false;
|
state->debugStop = false;
|
||||||
return false;
|
return ProcessLineResult::QuitOnce;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (command == ":a" || command == ":add") {
|
else if (command == ":a" || command == ":add") {
|
||||||
|
@ -726,8 +758,7 @@ bool NixRepl::processLine(std::string line)
|
||||||
|
|
||||||
else if (command == ":q" || command == ":quit") {
|
else if (command == ":q" || command == ":quit") {
|
||||||
state->debugStop = false;
|
state->debugStop = false;
|
||||||
state->debugQuit = true;
|
return ProcessLineResult::QuitAll;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (command == ":doc") {
|
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)
|
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,
|
ref<EvalState> evalState,
|
||||||
const ValMap & extraEnv)
|
const ValMap & extraEnv)
|
||||||
{
|
{
|
||||||
|
@ -941,7 +972,7 @@ void AbstractNixRepl::runSimple(
|
||||||
for (auto & [name, value] : extraEnv)
|
for (auto & [name, value] : extraEnv)
|
||||||
repl->addVarToScope(repl->state->symbols.create(name), *value);
|
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,
|
const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||||
std::function<AnnotatedValues()> getValues);
|
std::function<AnnotatedValues()> getValues);
|
||||||
|
|
||||||
static void runSimple(
|
static ReplExitStatus runSimple(
|
||||||
ref<EvalState> evalState,
|
ref<EvalState> evalState,
|
||||||
const ValMap & extraEnv);
|
const ValMap & extraEnv);
|
||||||
|
|
||||||
virtual void initEnv() = 0;
|
virtual void initEnv() = 0;
|
||||||
|
|
||||||
virtual void mainLoop() = 0;
|
virtual ReplExitStatus mainLoop() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "hash.hh"
|
#include "hash.hh"
|
||||||
#include "primops.hh"
|
#include "primops.hh"
|
||||||
#include "print-options.hh"
|
#include "print-options.hh"
|
||||||
|
#include "shared.hh"
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
@ -391,7 +392,6 @@ EvalState::EvalState(
|
||||||
, buildStore(buildStore ? buildStore : store)
|
, buildStore(buildStore ? buildStore : store)
|
||||||
, debugRepl(nullptr)
|
, debugRepl(nullptr)
|
||||||
, debugStop(false)
|
, debugStop(false)
|
||||||
, debugQuit(false)
|
|
||||||
, trylevel(0)
|
, trylevel(0)
|
||||||
, regexCache(makeRegexCache())
|
, regexCache(makeRegexCache())
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
|
@ -798,7 +798,17 @@ 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);
|
||||||
(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 "experimental-features.hh"
|
||||||
#include "input-accessor.hh"
|
#include "input-accessor.hh"
|
||||||
#include "search-path.hh"
|
#include "search-path.hh"
|
||||||
|
#include "repl-exit-status.hh"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
@ -207,9 +208,8 @@ public:
|
||||||
/**
|
/**
|
||||||
* Debugger
|
* Debugger
|
||||||
*/
|
*/
|
||||||
void (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
ReplExitStatus (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
||||||
bool debugStop;
|
bool debugStop;
|
||||||
bool debugQuit;
|
|
||||||
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;
|
||||||
|
@ -753,7 +753,6 @@ struct DebugTraceStacker {
|
||||||
DebugTraceStacker(EvalState & evalState, DebugTrace t);
|
DebugTraceStacker(EvalState & evalState, DebugTrace t);
|
||||||
~DebugTraceStacker()
|
~DebugTraceStacker()
|
||||||
{
|
{
|
||||||
// assert(evalState.debugTraces.front() == trace);
|
|
||||||
evalState.debugTraces.pop_front();
|
evalState.debugTraces.pop_front();
|
||||||
}
|
}
|
||||||
EvalState & evalState;
|
EvalState & evalState;
|
||||||
|
|
|
@ -754,15 +754,6 @@ static RegisterPrimOp primop_break({
|
||||||
|
|
||||||
auto & dt = state.debugTraces.front();
|
auto & dt = state.debugTraces.front();
|
||||||
state.runDebugRepl(&error, dt.env, dt.expr);
|
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.
|
// 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. */
|
/* increment state.trylevel, and decrement it when this function returns. */
|
||||||
MaintainCount trylevel(state.trylevel);
|
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)
|
if (state.debugRepl && evalSettings.ignoreExceptionsDuringTry)
|
||||||
{
|
{
|
||||||
/* to prevent starting the repl from exceptions withing a tryEval, null it. */
|
/* 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -407,6 +407,4 @@ PrintFreed::~PrintFreed()
|
||||||
showBytes(results.bytesFreed));
|
showBytes(results.bytesFreed));
|
||||||
}
|
}
|
||||||
|
|
||||||
Exit::~Exit() { }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "common-args.hh"
|
#include "common-args.hh"
|
||||||
#include "path.hh"
|
#include "path.hh"
|
||||||
#include "derived-path.hh"
|
#include "derived-path.hh"
|
||||||
|
#include "exit.hh"
|
||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
@ -14,15 +15,6 @@
|
||||||
|
|
||||||
namespace nix {
|
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);
|
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 a new issue