diff --git a/lix/libcmd/command.cc b/lix/libcmd/command.cc index 4dd5fdb67..f02c3bbb5 100644 --- a/lix/libcmd/command.cc +++ b/lix/libcmd/command.cc @@ -118,7 +118,7 @@ ref EvalCommand::getEvalState() evalState->repair = repair; if (startReplOnEvalErrors) { - evalState->debugRepl = &AbstractNixRepl::runSimple; + evalState->debug.repl = &AbstractNixRepl::runSimple; }; } return ref(evalState); diff --git a/lix/libcmd/repl.cc b/lix/libcmd/repl.cc index c31421d1d..d156deaf1 100644 --- a/lix/libcmd/repl.cc +++ b/lix/libcmd/repl.cc @@ -285,7 +285,7 @@ ReplExitStatus NixRepl::mainLoop() { if (isFirstRepl) { std::string_view debuggerNotice = ""; - if (state.debugRepl) { + if (state.debug.repl) { debuggerNotice = " debugger"; } notice("Lix %1%%2%\nType :? for help.", nixVersion, debuggerNotice); @@ -310,7 +310,7 @@ ReplExitStatus NixRepl::mainLoop() // number of chars as the prompt. if (!interacter->getLine(input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) { // Ctrl-D should exit the debugger. - state.debugStop = false; + state.debug.stop = false; logger->cout(""); // TODO: Should Ctrl-D exit just the current debugger session or // the entire program? @@ -366,7 +366,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix) } } - if (state.debugRepl) { + if (state.debug.repl) { for (auto const & colonCmd : this->DEBUG_COMMANDS) { if (colonCmd.starts_with(prefix)) { completions.insert(std::string(colonCmd)); @@ -416,9 +416,9 @@ StringSet NixRepl::completePrefix(const std::string & prefix) } } else { /* Temporarily disable the debugger, to avoid re-entering readline. */ - auto debug_repl = state.debugRepl; - state.debugRepl = nullptr; - Finally restoreDebug([&]() { state.debugRepl = debug_repl; }); + auto debug_repl = state.debug.repl; + state.debug.repl = nullptr; + Finally restoreDebug([&]() { state.debug.repl = debug_repl; }); try { /* This is an expression that should evaluate to an attribute set. Evaluate it to get the names of the @@ -485,7 +485,7 @@ void NixRepl::loadDebugTraceEnv(DebugTrace & dt) { initEnv(); - auto se = state.getStaticEnv(dt.expr); + auto se = state.debug.staticEnvFor(dt.expr); if (se) { auto vm = mapStaticEnvBindings(state.symbols, *se.get(), dt.env); @@ -541,7 +541,7 @@ ProcessLineResult NixRepl::processLine(std::string line) << " errors\n" << " :?, :help Brings up this help menu\n" ; - if (state.debugRepl) { + if (state.debug.repl) { std::cout << "\n" << " Debug mode commands\n" @@ -556,15 +556,15 @@ ProcessLineResult NixRepl::processLine(std::string line) } - else if (state.debugRepl && (command == ":bt" || command == ":backtrace")) { - for (const auto & [idx, i] : enumerate(state.debugTraces)) { + else if (state.debug.repl && (command == ":bt" || command == ":backtrace")) { + for (const auto & [idx, i] : enumerate(state.debug.traces)) { std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": "; showDebugTrace(std::cout, state.positions, i); } } - else if (state.debugRepl && (command == ":env")) { - for (const auto & [idx, i] : enumerate(state.debugTraces)) { + else if (state.debug.repl && (command == ":env")) { + for (const auto & [idx, i] : enumerate(state.debug.traces)) { if (idx == debugTraceIndex) { printEnvBindings(state, i.expr, i.env); break; @@ -572,13 +572,13 @@ ProcessLineResult NixRepl::processLine(std::string line) } } - else if (state.debugRepl && (command == ":st")) { + else if (state.debug.repl && (command == ":st")) { try { // change the DebugTrace index. debugTraceIndex = stoi(arg); } catch (...) { } - for (const auto & [idx, i] : enumerate(state.debugTraces)) { + for (const auto & [idx, i] : enumerate(state.debug.traces)) { if (idx == debugTraceIndex) { std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": "; showDebugTrace(std::cout, state.positions, i); @@ -590,15 +590,15 @@ ProcessLineResult NixRepl::processLine(std::string line) } } - else if (state.debugRepl && (command == ":s" || command == ":step")) { + else if (state.debug.repl && (command == ":s" || command == ":step")) { // set flag to stop at next DebugTrace; exit repl. - state.debugStop = true; + state.debug.stop = true; return ProcessLineResult::Continue; } - else if (state.debugRepl && (command == ":c" || command == ":continue")) { + else if (state.debug.repl && (command == ":c" || command == ":continue")) { // set flag to run to next breakpoint or end of program; exit repl. - state.debugStop = false; + state.debug.stop = false; return ProcessLineResult::Continue; } @@ -771,7 +771,7 @@ ProcessLineResult NixRepl::processLine(std::string line) } else if (command == ":q" || command == ":quit") { - state.debugStop = false; + state.debug.stop = false; return ProcessLineResult::Quit; } diff --git a/lix/libexpr/eval-error.cc b/lix/libexpr/eval-error.cc index c716ea6e6..1fa43e81e 100644 --- a/lix/libexpr/eval-error.cc +++ b/lix/libexpr/eval-error.cc @@ -45,7 +45,7 @@ EvalErrorBuilder & EvalErrorBuilder::withFrame(const Env & env, const Expr // NOTE: This is abusing side-effects. // TODO: check compatibility with nested debugger calls. // TODO: What side-effects?? - error.state.debugTraces.push_front(DebugTrace{ + error.state.debug.traces.push_front(DebugTrace{ .pos = error.state.positions[expr.getPos()], .expr = expr, .env = env, @@ -74,11 +74,11 @@ EvalErrorBuilder::addTrace(PosIdx pos, std::string_view formatString, const A template void EvalErrorBuilder::debugThrow() { - if (error.state.debugRepl && !error.state.debugTraces.empty()) { - const DebugTrace & last = error.state.debugTraces.front(); + if (error.state.debug.repl && !error.state.debug.traces.empty()) { + const DebugTrace & last = error.state.debug.traces.front(); const Env * env = &last.env; const Expr * expr = &last.expr; - error.state.runDebugRepl(&error, *env, *expr); + error.state.debug.runDebugRepl(error.state, &error, *env, *expr); } // `EvalState` is the only class that can construct an `EvalErrorBuilder`, diff --git a/lix/libexpr/eval.cc b/lix/libexpr/eval.cc index aa428f5bc..7f9eefe74 100644 --- a/lix/libexpr/eval.cc +++ b/lix/libexpr/eval.cc @@ -262,9 +262,6 @@ EvalState::EvalState( , derivationInternal(rootPath(CanonPath("/builtin/derivation.nix"))) , store(store) , buildStore(buildStore ? buildStore : store) - , debugRepl(nullptr) - , debugStop(false) - , trylevel(0) , regexCache(makeRegexCache()) #if HAVE_BOEHMGC , valueAllocCache(std::allocate_shared(traceable_allocator(), nullptr)) @@ -590,7 +587,7 @@ void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & void printEnvBindings(const EvalState &es, const Expr & expr, const Env & env) { // just print the names for now - auto se = es.getStaticEnv(expr); + auto se = es.debug.staticEnvFor(expr); if (se) printEnvBindings(es.symbols, *se, env, 0); } @@ -639,18 +636,20 @@ public: } }; -void EvalState::runDebugRepl(const EvalError * error, const Env & env, const Expr & expr) +void DebugState::runDebugRepl( + EvalState & evalState, const EvalError * error, const Env & env, const Expr & expr +) { // Make sure we have a debugger to run and we're not already in a debugger. - if (!debugRepl || inDebugger) + if (!repl || inDebugger) return; auto dts = error && expr.getPos() ? std::make_unique( - *this, + evalState, DebugTrace { - .pos = error->info().pos ? error->info().pos : positions[expr.getPos()], + .pos = error->info().pos ? error->info().pos : evalState.positions[expr.getPos()], .expr = expr, .env = env, .hint = error->info().msg, @@ -666,11 +665,11 @@ void EvalState::runDebugRepl(const EvalError * error, const Env & env, const Exp printError("This exception occurred in a 'tryEval' call. Use " ANSI_GREEN "--ignore-try" ANSI_NORMAL " to skip these.\n"); } - auto se = getStaticEnv(expr); + auto se = staticEnvFor(expr); if (se) { - auto vm = mapStaticEnvBindings(symbols, *se.get(), env); + auto vm = mapStaticEnvBindings(evalState.symbols, *se.get(), env); DebuggerGuard _guard(inDebugger); - auto exitStatus = (debugRepl)(*this, *vm); + auto exitStatus = repl(evalState, *vm); switch (exitStatus) { case ReplExitStatus::QuitAll: if (error) @@ -718,9 +717,9 @@ DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t) : evalState(evalState) , trace(std::move(t)) { - evalState.debugTraces.push_front(trace); - if (evalState.debugStop && evalState.debugRepl) - evalState.runDebugRepl(nullptr, trace.env, trace.expr); + evalState.debug.traces.push_front(trace); + if (evalState.debug.stop && evalState.debug.repl) + evalState.debug.runDebugRepl(evalState, nullptr, trace.env, trace.expr); } inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) @@ -955,7 +954,7 @@ void EvalState::cacheFile( fileParseCache[resolvedPath] = e; try { - auto dts = debugRepl + auto dts = debug.repl ? makeDebugTraceStacker( *this, *e, @@ -1173,7 +1172,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) *i.second.chooseByKind(&env2, &env, inheritEnv)); } - auto dts = state.debugRepl + auto dts = state.debug.repl ? makeDebugTraceStacker( state, *this, @@ -1258,7 +1257,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) } try { - auto dts = state.debugRepl + auto dts = state.debug.repl ? makeDebugTraceStacker( state, *this, @@ -1576,7 +1575,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & /* Evaluate the body. */ try { - auto dts = debugRepl + auto dts = debug.repl ? makeDebugTraceStacker( *this, *lambda.body, env2, positions[lambda.pos], "while calling %s", @@ -1715,7 +1714,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & void ExprCall::eval(EvalState & state, Env & env, Value & v) { - auto dts = state.debugRepl + auto dts = state.debug.repl ? makeDebugTraceStacker( state, *this, @@ -2088,7 +2087,7 @@ void EvalState::forceValueDeep(Value & v) for (auto & i : *v.attrs) try { // If the value is a thunk, we're evaling. Otherwise no trace necessary. - auto dts = debugRepl && i.value->isThunk() + auto dts = debug.repl && i.value->isThunk() ? makeDebugTraceStacker(*this, *i.value->thunk.expr, *i.value->thunk.env, positions[i.pos], "while evaluating the attribute '%1%'", symbols[i.name]) : nullptr; diff --git a/lix/libexpr/eval.hh b/lix/libexpr/eval.hh index 9b6dc6066..841ee56a5 100644 --- a/lix/libexpr/eval.hh +++ b/lix/libexpr/eval.hh @@ -152,6 +152,28 @@ struct DebugTrace { bool isError; }; +struct DebugState +{ + std::function repl; + bool stop = false; + bool inDebugger = false; + std::list traces; + std::map> exprEnvs; + int trylevel = 0; + + void runDebugRepl( + EvalState & evalState, const EvalError * error, const Env & env, const Expr & expr + ); + + const std::shared_ptr staticEnvFor(const Expr & expr) const + { + if (auto i = exprEnvs.find(&expr); i != exprEnvs.end()) { + return i->second; + } + return nullptr; + } +}; + struct StaticSymbols { @@ -202,25 +224,7 @@ public: RootValue vCallFlake = nullptr; RootValue vImportedDrvToDerivation = nullptr; - /** - * Debugger - */ - std::function debugRepl; - bool debugStop; - bool inDebugger = false; - int trylevel; - std::list debugTraces; - std::map> exprEnvs; - const std::shared_ptr getStaticEnv(const Expr & expr) const - { - auto i = exprEnvs.find(&expr); - if (i != exprEnvs.end()) - return i->second; - else - return std::shared_ptr();; - } - - void runDebugRepl(const EvalError * error, const Env & env, const Expr & expr); + DebugState debug; template [[nodiscard, gnu::noinline]] @@ -760,7 +764,7 @@ struct DebugTraceStacker { DebugTraceStacker(EvalState & evalState, DebugTrace t); ~DebugTraceStacker() { - evalState.debugTraces.pop_front(); + evalState.debug.traces.pop_front(); } EvalState & evalState; DebugTrace trace; diff --git a/lix/libexpr/nixexpr.cc b/lix/libexpr/nixexpr.cc index 58f0aa43d..16a87b51a 100644 --- a/lix/libexpr/nixexpr.cc +++ b/lix/libexpr/nixexpr.cc @@ -302,32 +302,32 @@ void Expr::bindVars(EvalState & es, const std::shared_ptr & env void ExprInt::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); } void ExprFloat::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); } void ExprString::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); } void ExprPath::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); } void ExprVar::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); fromWith = nullptr; @@ -364,14 +364,14 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr & void ExprInheritFrom::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); } void ExprSelect::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); e->bindVars(es, env); if (def) def->bindVars(es, env); @@ -382,8 +382,8 @@ void ExprSelect::bindVars(EvalState & es, const std::shared_ptr void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); e->bindVars(es, env); for (auto & i : attrPath) @@ -414,8 +414,8 @@ std::shared_ptr ExprAttrs::bindInheritSources( void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); if (recursive) { auto newEnv = [&] () -> std::shared_ptr { @@ -453,8 +453,8 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr void ExprList::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); for (auto & i : elems) i->bindVars(es, env); @@ -462,8 +462,8 @@ void ExprList::bindVars(EvalState & es, const std::shared_ptr & void ExprLambda::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); auto newEnv = std::make_shared( nullptr, env.get(), @@ -489,8 +489,8 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr void ExprCall::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); fun->bindVars(es, env); for (auto & e : args) @@ -514,16 +514,16 @@ void ExprLet::bindVars(EvalState & es, const std::shared_ptr & for (auto & i : attrs->attrs) i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv)); - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); body->bindVars(es, newEnv); } void ExprWith::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); parentWith = nullptr; for (auto * e = env.get(); e && !parentWith; e = e->up) @@ -548,8 +548,8 @@ void ExprWith::bindVars(EvalState & es, const std::shared_ptr & void ExprIf::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); cond->bindVars(es, env); then->bindVars(es, env); @@ -558,8 +558,8 @@ void ExprIf::bindVars(EvalState & es, const std::shared_ptr & e void ExprAssert::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); cond->bindVars(es, env); body->bindVars(es, env); @@ -567,16 +567,16 @@ void ExprAssert::bindVars(EvalState & es, const std::shared_ptr void ExprOpNot::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); e->bindVars(es, env); } void ExprConcatStrings::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); for (auto & i : this->es) i.second->bindVars(es, env); @@ -584,8 +584,8 @@ void ExprConcatStrings::bindVars(EvalState & es, const std::shared_ptr & env) { - if (es.debugRepl) - es.exprEnvs.insert(std::make_pair(this, env)); + if (es.debug.repl) + es.debug.exprEnvs.insert(std::make_pair(this, env)); } diff --git a/lix/libexpr/primops.cc b/lix/libexpr/primops.cc index 48c836ea5..b6662c638 100644 --- a/lix/libexpr/primops.cc +++ b/lix/libexpr/primops.cc @@ -566,15 +566,15 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a static void prim_break(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - if (state.debugRepl && !state.debugTraces.empty()) { + if (state.debug.repl && !state.debug.traces.empty()) { auto error = EvalError(state, ErrorInfo { .level = lvlInfo, .msg = HintFmt("breakpoint reached"), .pos = state.positions[pos], }); - auto & dt = state.debugTraces.front(); - state.runDebugRepl(&error, dt.env, dt.expr); + auto & dt = state.debug.traces.front(); + state.debug.runDebugRepl(state, &error, dt.env, dt.expr); } // Return the value we were passed. @@ -638,14 +638,14 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va auto attrs = state.buildBindings(2); /* increment state.trylevel, and decrement it when this function returns. */ - MaintainCount trylevel(state.trylevel); + MaintainCount trylevel(state.debug.trylevel); std::function savedDebugRepl; - if (state.debugRepl && evalSettings.ignoreExceptionsDuringTry) + if (state.debug.repl && evalSettings.ignoreExceptionsDuringTry) { /* to prevent starting the repl from exceptions withing a tryEval, null it. */ - savedDebugRepl = state.debugRepl; - state.debugRepl = nullptr; + savedDebugRepl = state.debug.repl; + state.debug.repl = nullptr; } try { @@ -659,7 +659,7 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va // restore the debugRepl pointer if we saved it earlier. if (savedDebugRepl) - state.debugRepl = savedDebugRepl; + state.debug.repl = savedDebugRepl; v.mkAttrs(attrs); } @@ -697,9 +697,9 @@ static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Valu printError("trace: %1%", args[0]->string.s); else printError("trace: %1%", ValuePrinter(state, *args[0])); - if (evalSettings.builtinsTraceDebugger && state.debugRepl && !state.debugTraces.empty()) { - const DebugTrace & last = state.debugTraces.front(); - state.runDebugRepl(nullptr, last.env, last.expr); + if (evalSettings.builtinsTraceDebugger && state.debug.repl && !state.debug.traces.empty()) { + const DebugTrace & last = state.debug.traces.front(); + state.debug.runDebugRepl(state, nullptr, last.env, last.expr); } state.forceValue(*args[1], pos); v = *args[1];