primops: Move functions to primops/control.cc
Moved builtins: abort, break, throw, tryEval
Change-Id: I6198b201325a7029cf011b868f87bef7f4272f8a
This commit is contained in:
parent
0ba37444da
commit
84e80fa97d
|
@ -88,6 +88,7 @@ libexpr_sources = files(
|
|||
'flake/lockfile.cc',
|
||||
'primops/attrset.cc',
|
||||
'primops/context.cc',
|
||||
'primops/control.cc',
|
||||
'primops/fetchClosure.cc',
|
||||
'primops/fetchMercurial.cc',
|
||||
'primops/fetchTree.cc',
|
||||
|
|
|
@ -715,65 +715,6 @@ static RegisterPrimOp primop_genericClosure(PrimOp {
|
|||
});
|
||||
|
||||
|
||||
static RegisterPrimOp 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 = [](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];
|
||||
}
|
||||
});
|
||||
|
||||
static RegisterPrimOp primop_abort({
|
||||
.name = "abort",
|
||||
.args = {"s"},
|
||||
.doc = R"(
|
||||
Abort Nix expression evaluation and print the error message *s*.
|
||||
)",
|
||||
.fun = [](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();
|
||||
}
|
||||
});
|
||||
|
||||
static RegisterPrimOp 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 = [](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();
|
||||
}
|
||||
});
|
||||
|
||||
static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
try {
|
||||
|
@ -836,57 +777,6 @@ static RegisterPrimOp primop_floor({
|
|||
|
||||
/* Try evaluating the argument. Success => {success=true; value=something;},
|
||||
* else => {success=false; value=false;} */
|
||||
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);
|
||||
}
|
||||
|
||||
static RegisterPrimOp 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,
|
||||
});
|
||||
|
||||
/* Return an environment variable. Use with care. */
|
||||
static void prim_getEnv(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
|
|
149
src/libexpr/primops/control.cc
Normal file
149
src/libexpr/primops/control.cc
Normal file
|
@ -0,0 +1,149 @@
|
|||
#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();
|
||||
}
|
||||
|
||||
static RegisterPrimOp 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];
|
||||
}
|
||||
|
||||
static RegisterPrimOp 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();
|
||||
}
|
||||
|
||||
static RegisterPrimOp 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);
|
||||
}
|
||||
|
||||
static RegisterPrimOp 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,
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue