forked from lix-project/lix
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',
|
'flake/lockfile.cc',
|
||||||
'primops/attrset.cc',
|
'primops/attrset.cc',
|
||||||
'primops/context.cc',
|
'primops/context.cc',
|
||||||
|
'primops/control.cc',
|
||||||
'primops/fetchClosure.cc',
|
'primops/fetchClosure.cc',
|
||||||
'primops/fetchMercurial.cc',
|
'primops/fetchMercurial.cc',
|
||||||
'primops/fetchTree.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)
|
static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@ -836,57 +777,6 @@ static RegisterPrimOp primop_floor({
|
||||||
|
|
||||||
/* Try evaluating the argument. Success => {success=true; value=something;},
|
/* Try evaluating the argument. Success => {success=true; value=something;},
|
||||||
* else => {success=false; value=false;} */
|
* 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. */
|
/* Return an environment variable. Use with care. */
|
||||||
static void prim_getEnv(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
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