150 lines
4.2 KiB
C++
150 lines
4.2 KiB
C++
#include "eval-settings.hh"
|
|
#include "primops.hh"
|
|
|
|
namespace nix {
|
|
|
|
/**
|
|
* builtins.abort
|
|
*/
|
|
|
|
static void prim_abort(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
|
{
|
|
NixStringContext context;
|
|
auto s = state
|
|
.coerceToString(
|
|
pos,
|
|
*args[0],
|
|
context,
|
|
"while evaluating the error message passed to builtins.abort"
|
|
)
|
|
.toOwned();
|
|
state.error<Abort>("evaluation aborted with the following error message: '%1%'", s)
|
|
.debugThrow();
|
|
}
|
|
|
|
PrimOp primop_abort({
|
|
.name = "abort",
|
|
.args = {"s"},
|
|
.doc = R"(
|
|
Abort Nix expression evaluation and print the error message *s*.
|
|
)",
|
|
.fun = prim_abort,
|
|
});
|
|
|
|
/**
|
|
* builtins.break
|
|
*/
|
|
|
|
static void prim_break(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
|
{
|
|
if (state.debugRepl && !state.debugTraces.empty()) {
|
|
auto error = Error(ErrorInfo{
|
|
.level = lvlInfo,
|
|
.msg = HintFmt("breakpoint reached"),
|
|
.pos = state.positions[pos],
|
|
});
|
|
|
|
auto & dt = state.debugTraces.front();
|
|
state.runDebugRepl(&error, dt.env, dt.expr);
|
|
}
|
|
|
|
// Return the value we were passed.
|
|
v = *args[0];
|
|
}
|
|
|
|
PrimOp primop_break({
|
|
.name = "break",
|
|
.args = {"v"},
|
|
.doc = R"(
|
|
In debug mode (enabled using `--debugger`), pause Nix expression evaluation and enter the REPL.
|
|
Otherwise, return the argument `v`.
|
|
)",
|
|
.fun = prim_break,
|
|
});
|
|
|
|
/**
|
|
* builtins.throw
|
|
*/
|
|
|
|
static void prim_throw(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
|
{
|
|
NixStringContext context;
|
|
auto s =
|
|
state
|
|
.coerceToString(
|
|
pos, *args[0], context, "while evaluating the error message passed to builtin.throw"
|
|
)
|
|
.toOwned();
|
|
state.error<ThrownError>(s).debugThrow();
|
|
}
|
|
|
|
PrimOp primop_throw({
|
|
.name = "throw",
|
|
.args = {"s"},
|
|
.doc = R"(
|
|
Throw an error message *s*. This usually aborts Nix expression
|
|
evaluation, but in `nix-env -qa` and other commands that try to
|
|
evaluate a set of derivations to get information about those
|
|
derivations, a derivation that throws an error is silently skipped
|
|
(which is not the case for `abort`).
|
|
)",
|
|
.fun = prim_throw,
|
|
});
|
|
|
|
/**
|
|
* builtins.tryEval
|
|
*/
|
|
|
|
static void prim_tryEval(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
|
{
|
|
auto attrs = state.buildBindings(2);
|
|
|
|
/* increment state.trylevel, and decrement it when this function returns. */
|
|
MaintainCount trylevel(state.trylevel);
|
|
|
|
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. */
|
|
savedDebugRepl = state.debugRepl;
|
|
state.debugRepl = nullptr;
|
|
}
|
|
|
|
try {
|
|
state.forceValue(*args[0], pos);
|
|
attrs.insert(state.sValue, args[0]);
|
|
attrs.alloc("success").mkBool(true);
|
|
} catch (AssertionError & e) {
|
|
attrs.alloc(state.sValue).mkBool(false);
|
|
attrs.alloc("success").mkBool(false);
|
|
}
|
|
|
|
// restore the debugRepl pointer if we saved it earlier.
|
|
if (savedDebugRepl) {
|
|
state.debugRepl = savedDebugRepl;
|
|
}
|
|
|
|
v.mkAttrs(attrs);
|
|
}
|
|
|
|
PrimOp primop_tryEval({
|
|
.name = "__tryEval",
|
|
.args = {"e"},
|
|
.doc = R"(
|
|
Try to shallowly evaluate *e*. Return a set containing the
|
|
attributes `success` (`true` if *e* evaluated successfully,
|
|
`false` if an error was thrown) and `value`, equalling *e* if
|
|
successful and `false` otherwise. `tryEval` will only prevent
|
|
errors created by `throw` or `assert` from being thrown.
|
|
Errors `tryEval` will not catch are for example those created
|
|
by `abort` and type errors generated by builtins. Also note that
|
|
this doesn't evaluate *e* deeply, so `let e = { x = throw ""; };
|
|
in (builtins.tryEval e).success` will be `true`. Using
|
|
`builtins.deepSeq` one can get the expected result:
|
|
`let e = { x = throw ""; }; in
|
|
(builtins.tryEval (builtins.deepSeq e e)).success` will be
|
|
`false`.
|
|
)",
|
|
.fun = prim_tryEval,
|
|
});
|
|
}
|