Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6f0e87fa8 | ||
|
|
17879c9a83 | ||
|
|
9ba7a7eee7 | ||
|
|
5f070b2297 | ||
|
|
3b093988fb | ||
|
|
fe6dfa5ace | ||
|
|
acd85805ab | ||
|
|
af86b74467 | ||
|
|
85ed12485e | ||
|
|
30b971f6e0 | ||
|
|
32440b5fae | ||
|
|
aa39e14fcf | ||
|
|
b0d11f9da1 | ||
|
|
002dfbb2e3 | ||
|
|
6e242e8b9b | ||
|
|
b1ffae3ccd | ||
|
|
5def7559a6 | ||
|
|
ce70234904 | ||
|
|
cbc378b277 | ||
|
|
5d6bb8c350 | ||
|
|
93bda92508 | ||
|
|
dbee9d15c5 | ||
|
|
7642b7227a | ||
|
|
aa96b00182 | ||
|
|
d37a5d4000 | ||
|
|
b36a19e50c | ||
|
|
f08b8532be | ||
|
|
f3fcd7c02f | ||
|
|
1f0b25a92c | ||
|
|
82b771951c |
+61
-8
@@ -9,6 +9,7 @@ import tempfile
|
||||
import platform
|
||||
import shlex
|
||||
import textwrap
|
||||
import dataclasses
|
||||
|
||||
flake_args = ["--extra-experimental-features", "nix-command flakes"]
|
||||
cases = {
|
||||
@@ -64,8 +65,9 @@ arg_parser.add_argument(
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
'--mode',
|
||||
choices=[ "walltime" ] + [ "icount" ] if platform.system() == 'Linux' else [], # perf doesn't run on Darwin
|
||||
default="walltime",
|
||||
nargs='+',
|
||||
choices=[ "walltime", "memory" ] + [ "icount" ] if platform.system() == 'Linux' else [], # perf doesn't run on Darwin
|
||||
default=[ "walltime" ],
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
'--daemon',
|
||||
@@ -73,8 +75,8 @@ arg_parser.add_argument(
|
||||
help='Run a temporary daemon for the benchmark instead of using a local store directly',
|
||||
)
|
||||
args = arg_parser.parse_args()
|
||||
if len(args.builds) < 2:
|
||||
raise ValueError("need at least two build directories to compare")
|
||||
if len(args.builds) < 1:
|
||||
raise ValueError("need at least one build directory to benchmark")
|
||||
|
||||
benchmarks: list[str] = []
|
||||
if args.cases is None:
|
||||
@@ -162,6 +164,54 @@ def bench_icount(env):
|
||||
print(" relative instructions:", int(instr)/perf_results_for[case][0][1])
|
||||
print("\n")
|
||||
|
||||
@dataclasses.dataclass
|
||||
class MemoryStatistics:
|
||||
envBytes: int
|
||||
listBytes: int
|
||||
setBytes: int
|
||||
valueBytes: int
|
||||
heapBytes: int
|
||||
heapSize: int
|
||||
|
||||
def bench_memory(env):
|
||||
path = "bench/bench-memory.json"
|
||||
env = env | {
|
||||
'NIX_SHOW_STATS': '1',
|
||||
'NIX_SHOW_STATS_PATH': path,
|
||||
}
|
||||
results: dict[str, list[tuple[str, MemoryStatistics]]] = {}
|
||||
for case in benchmarks:
|
||||
for build in args.builds:
|
||||
case_command = make_full_command(build, case)
|
||||
commandline = [ "sh", "-c", case_command ]
|
||||
print("running", case_command)
|
||||
subprocess.run(commandline, env=env, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
with open(path) as fd:
|
||||
stats = json.load(fd)
|
||||
results.setdefault(case, []).append((case_command, MemoryStatistics(
|
||||
envBytes=stats['envs']['bytes'],
|
||||
listBytes=stats['list']['bytes'],
|
||||
setBytes=stats['sets']['bytes'],
|
||||
valueBytes=stats['values']['bytes'],
|
||||
heapSize=stats['gc']['heapSize'],
|
||||
heapBytes=stats['gc']['totalBytes'],
|
||||
)))
|
||||
|
||||
print("Benchmarks summary\n---\n")
|
||||
for (case, entries) in results.items():
|
||||
for cmd, stats in entries:
|
||||
print(cmd)
|
||||
print("-" * min(80, len(cmd)))
|
||||
print(f" env bytes: {stats.envBytes :15d} | {(stats.envBytes / entries[0][1].envBytes) :.3f}x")
|
||||
print(f" list bytes: {stats.listBytes :15d} | {(stats.listBytes / entries[0][1].listBytes) :.3f}x")
|
||||
print(f" set bytes: {stats.setBytes :15d} | {(stats.setBytes / entries[0][1].setBytes) :.3f}x")
|
||||
if not entries[0][1].valueBytes:
|
||||
print(f" value bytes: {0:15d}")
|
||||
else:
|
||||
print(f" value bytes: {stats.valueBytes:15d} | {(stats.valueBytes / entries[0][1].valueBytes):.3f}x")
|
||||
print(f" heap alloc'd: {stats.heapBytes :15d} | {(stats.heapBytes / entries[0][1].heapBytes) :.3f}x")
|
||||
print(f" heap size: {stats.heapSize :15d} | {(stats.heapSize / entries[0][1].heapSize) :.3f}x")
|
||||
print("\n")
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
subprocess.run([
|
||||
@@ -178,7 +228,10 @@ with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
])
|
||||
subenv["NIX_DAEMON_SOCKET_PATH"] = f"{tmp_dir}/daemon"
|
||||
|
||||
if args.mode == "walltime":
|
||||
bench_walltime(subenv)
|
||||
else:
|
||||
bench_icount(subenv)
|
||||
for mode in args.mode:
|
||||
if mode == "walltime":
|
||||
bench_walltime(subenv)
|
||||
elif mode == "memory":
|
||||
bench_memory(subenv)
|
||||
else:
|
||||
bench_icount(subenv)
|
||||
|
||||
@@ -272,7 +272,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
|
||||
}
|
||||
bool add = false;
|
||||
if (v.type() == nFunction) {
|
||||
if (auto pattern = dynamic_cast<AttrsPattern *>(v.lambda.fun->pattern.get())) {
|
||||
if (auto pattern = dynamic_cast<AttrsPattern *>(v.lambda().fun->pattern.get())) {
|
||||
for (auto & i : pattern->formals) {
|
||||
if (evaluator->symbols[i.name] == "inNixShell") {
|
||||
add = true;
|
||||
@@ -285,12 +285,12 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
|
||||
};
|
||||
|
||||
for (auto & i : attrPaths) {
|
||||
Value & v(*findAlongAttrPath(
|
||||
*state,
|
||||
i,
|
||||
takesNixShellAttr(vRoot) ? *autoArgsWithInNixShell : *autoArgs,
|
||||
vRoot
|
||||
).first);
|
||||
Value v(
|
||||
findAlongAttrPath(
|
||||
*state, i, takesNixShellAttr(vRoot) ? *autoArgsWithInNixShell : *autoArgs, vRoot
|
||||
)
|
||||
.first
|
||||
);
|
||||
state->forceValue(v, noPos);
|
||||
getDerivations(
|
||||
*state,
|
||||
|
||||
+24
-18
@@ -1,6 +1,7 @@
|
||||
#include "lix/libcmd/cmd-profiles.hh"
|
||||
#include "lix/libexpr/attr-path.hh"
|
||||
#include "lix/libcmd/common-eval-args.hh"
|
||||
#include "lix/libexpr/value.hh"
|
||||
#include "lix/libstore/derivations.hh"
|
||||
#include "lix/libutil/terminal.hh"
|
||||
#include "lix/libexpr/eval.hh"
|
||||
@@ -150,11 +151,12 @@ static void getAllExprs(Evaluator & state,
|
||||
continue;
|
||||
}
|
||||
/* Load the expression on demand. */
|
||||
auto vArg = state.mem.allocValue();
|
||||
vArg->mkString(path2.canonical().abs());
|
||||
Value vArg;
|
||||
vArg.mkString(path2.canonical().abs());
|
||||
if (seen.size() == maxAttrs)
|
||||
throw Error("too many Nix expressions in directory '%1%'", path);
|
||||
attrs.alloc(attrName).mkApp(&state.builtins.get("import"), vArg);
|
||||
attrs.alloc(attrName
|
||||
) = {NewValueAs::app, state.mem, state.builtins.get("import"), vArg};
|
||||
}
|
||||
else if (st.type == InputAccessor::tDirectory)
|
||||
/* `path2' is a directory (with no default.nix in it);
|
||||
@@ -181,7 +183,7 @@ static void loadSourceExpr(EvalState & state, const SourcePath & path_, Value &
|
||||
directory). */
|
||||
else if (st.type == InputAccessor::tDirectory) {
|
||||
auto attrs = state.ctx.buildBindings(maxAttrs);
|
||||
attrs.alloc("_combineChannels").mkList(0);
|
||||
attrs.alloc("_combineChannels") = Value::EMPTY_LIST;
|
||||
StringSet seen;
|
||||
getAllExprs(state.ctx, path, seen, attrs);
|
||||
v.mkAttrs(attrs);
|
||||
@@ -198,7 +200,7 @@ static void loadDerivations(EvalState & state, const SourcePath & nixExprPath,
|
||||
Value vRoot;
|
||||
loadSourceExpr(state, nixExprPath, vRoot);
|
||||
|
||||
Value & v(*findAlongAttrPath(state, pathPrefix, autoArgs, vRoot).first);
|
||||
Value v(findAlongAttrPath(state, pathPrefix, autoArgs, vRoot).first);
|
||||
|
||||
getDerivations(state, v, pathPrefix, autoArgs, elems, true);
|
||||
|
||||
@@ -425,7 +427,7 @@ static void queryInstSources(EvalState & state,
|
||||
Expr & eFun = state.ctx.parseExprFromString(i, CanonPath::fromCwd());
|
||||
Value vFun, vTmp;
|
||||
state.eval(eFun, vFun);
|
||||
vTmp.mkApp(&vFun, &vArg);
|
||||
vTmp = {NewValueAs::app, state.ctx.mem, vFun, vArg};
|
||||
getDerivations(state, vTmp, "", *instSource.autoArgs, elems, true);
|
||||
}
|
||||
|
||||
@@ -480,7 +482,7 @@ static void queryInstSources(EvalState & state,
|
||||
Value vRoot;
|
||||
loadSourceExpr(state, *instSource.nixExprPath, vRoot);
|
||||
for (auto & i : args) {
|
||||
Value & v(*findAlongAttrPath(state, i, *instSource.autoArgs, vRoot).first);
|
||||
Value v(findAlongAttrPath(state, i, *instSource.autoArgs, vRoot).first);
|
||||
getDerivations(state, v, "", *instSource.autoArgs, elems, true);
|
||||
}
|
||||
break;
|
||||
@@ -515,8 +517,8 @@ static bool keep(EvalState & state, DrvInfo & drv)
|
||||
static void setMetaFlag(EvalState & state, DrvInfo & drv,
|
||||
const std::string & name, const std::string & value)
|
||||
{
|
||||
auto v = state.ctx.mem.allocValue();
|
||||
v->mkString(value);
|
||||
Value v;
|
||||
v.mkString(value);
|
||||
drv.setMeta(state, name, v);
|
||||
}
|
||||
|
||||
@@ -1275,35 +1277,39 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type() == nInt) {
|
||||
attrs2["type"] = "int";
|
||||
attrs2["value"] = fmt("%1%", v->integer);
|
||||
attrs2["value"] = fmt("%1%", v->integer());
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type() == nFloat) {
|
||||
attrs2["type"] = "float";
|
||||
attrs2["value"] = fmt("%1%", v->fpoint);
|
||||
attrs2["value"] = fmt("%1%", v->fpoint());
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type() == nBool) {
|
||||
attrs2["type"] = "bool";
|
||||
attrs2["value"] = v->boolean ? "true" : "false";
|
||||
attrs2["value"] = v->boolean() ? "true" : "false";
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type() == nList) {
|
||||
attrs2["type"] = "strings";
|
||||
XMLOpenElement m(xml, "meta", attrs2);
|
||||
for (auto elem : v->listItems()) {
|
||||
if (elem->type() != nString) continue;
|
||||
for (auto & elem : v->listItems()) {
|
||||
if (elem.type() != nString) {
|
||||
continue;
|
||||
}
|
||||
XMLAttrs attrs3;
|
||||
attrs3["value"] = elem->str();
|
||||
attrs3["value"] = elem.str();
|
||||
xml.writeEmptyElement("string", attrs3);
|
||||
}
|
||||
} else if (v->type() == nAttrs) {
|
||||
attrs2["type"] = "strings";
|
||||
XMLOpenElement m(xml, "meta", attrs2);
|
||||
Bindings & attrs = *v->attrs;
|
||||
Bindings & attrs = *v->attrs();
|
||||
for (auto &i : attrs) {
|
||||
const Attr & a(*attrs.get(i.name));
|
||||
if(a.value->type() != nString) continue;
|
||||
if (a.value.type() != nString) {
|
||||
continue;
|
||||
}
|
||||
XMLAttrs attrs3;
|
||||
attrs3["type"] = globals.state->symbols[i.name];
|
||||
attrs3["value"] = a.value->str();
|
||||
attrs3["value"] = a.value.str();
|
||||
xml.writeEmptyElement("string", attrs3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
||||
state.eval(e, vRoot);
|
||||
|
||||
for (auto & i : attrPaths) {
|
||||
Value & v(*findAlongAttrPath(state, i, autoArgs, vRoot).first);
|
||||
Value v(findAlongAttrPath(state, i, autoArgs, vRoot).first);
|
||||
state.forceValue(v, noPos);
|
||||
|
||||
NixStringContext context;
|
||||
|
||||
+15
-13
@@ -1,4 +1,5 @@
|
||||
#include "user-env.hh"
|
||||
#include "lix/libexpr/value.hh"
|
||||
#include "lix/libstore/derivations.hh"
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libstore/path-with-outputs.hh"
|
||||
@@ -32,7 +33,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
|
||||
/* Construct the whole top level derivation. */
|
||||
StorePathSet references;
|
||||
Value manifest = state.ctx.mem.newList(elems.size());
|
||||
auto manifest = state.ctx.mem.newList(elems.size());
|
||||
Value vManifest{NewValueAs::list, manifest};
|
||||
size_t n = 0;
|
||||
for (auto & i : elems) {
|
||||
/* Create a pseudo-derivation containing the name, system,
|
||||
@@ -55,9 +57,10 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
|
||||
// Copy each output meant for installation.
|
||||
auto & vOutputs = attrs.alloc(state.ctx.s.outputs);
|
||||
vOutputs = state.ctx.mem.newList(outputs.size());
|
||||
auto outputsList = state.ctx.mem.newList(outputs.size());
|
||||
vOutputs = {NewValueAs::list, outputsList};
|
||||
for (const auto & [m, j] : enumerate(outputs)) {
|
||||
(vOutputs.listElems()[m] = state.ctx.mem.allocValue())->mkString(j.first);
|
||||
outputsList->elems[m].mkString(j.first);
|
||||
auto outputAttrs = state.ctx.buildBindings(2);
|
||||
outputAttrs.alloc(state.ctx.s.outPath).mkString(state.ctx.store->printStorePath(*j.second));
|
||||
attrs.alloc(j.first).mkAttrs(outputAttrs);
|
||||
@@ -75,12 +78,12 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
for (auto & j : metaNames) {
|
||||
Value * v = i.queryMeta(state, j);
|
||||
if (!v) continue;
|
||||
meta.insert(state.ctx.symbols.create(j), v);
|
||||
meta.insert(state.ctx.symbols.create(j), *v);
|
||||
}
|
||||
|
||||
attrs.alloc(state.ctx.s.meta).mkAttrs(meta);
|
||||
|
||||
(manifest.listElems()[n++] = state.ctx.mem.allocValue())->mkAttrs(attrs);
|
||||
manifest->elems[n++].mkAttrs(attrs);
|
||||
|
||||
if (drvPath) references.insert(*drvPath);
|
||||
}
|
||||
@@ -89,7 +92,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
the store; we need it for future modifications of the
|
||||
environment. */
|
||||
std::ostringstream str;
|
||||
printAmbiguous(manifest, state.ctx.symbols, str, nullptr, std::numeric_limits<int>::max());
|
||||
printAmbiguous(vManifest, state.ctx.symbols, str, nullptr, std::numeric_limits<int>::max());
|
||||
auto manifestFile = state.aio.blockOn(state.ctx.store->addTextToStore("env-manifest.nix",
|
||||
str.str(), references));
|
||||
|
||||
@@ -103,21 +106,20 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
builder with the manifest as argument. */
|
||||
auto attrs = state.ctx.buildBindings(3);
|
||||
state.ctx.paths.mkStorePathString(manifestFile, attrs.alloc("manifest"));
|
||||
attrs.insert(state.ctx.symbols.create("derivations"), &manifest);
|
||||
attrs.insert(state.ctx.symbols.create("derivations"), vManifest);
|
||||
Value args;
|
||||
args.mkAttrs(attrs);
|
||||
|
||||
Value topLevel;
|
||||
topLevel.mkApp(&envBuilder, &args);
|
||||
Value topLevel{NewValueAs::app, state.ctx.mem, envBuilder, args};
|
||||
|
||||
/* Evaluate it. */
|
||||
debug("evaluating user environment builder");
|
||||
state.forceValue(topLevel, noPos);
|
||||
NixStringContext context;
|
||||
const Attr & aDrvPath(*topLevel.attrs->get(state.ctx.s.drvPath));
|
||||
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, "");
|
||||
const Attr & aOutPath(*topLevel.attrs->get(state.ctx.s.outPath));
|
||||
auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context, "");
|
||||
const Attr & aDrvPath(*topLevel.attrs()->get(state.ctx.s.drvPath));
|
||||
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, aDrvPath.value, context, "");
|
||||
const Attr & aOutPath(*topLevel.attrs()->get(state.ctx.s.outPath));
|
||||
auto topLevelOut = state.coerceToStorePath(aOutPath.pos, aOutPath.value, context, "");
|
||||
|
||||
/* Realise the resulting store expression. */
|
||||
debug("building user environment");
|
||||
|
||||
@@ -183,13 +183,13 @@ Bindings * MixEvalArgs::getAutoArgs(Evaluator & state)
|
||||
{
|
||||
auto res = state.buildBindings(autoArgs.size());
|
||||
for (auto & i : autoArgs) {
|
||||
auto v = state.mem.allocValue();
|
||||
Value v;
|
||||
if (i.second[0] == 'E')
|
||||
state.evalLazily(
|
||||
state.parseExprFromString(i.second.substr(1), CanonPath::fromCwd()), *v
|
||||
state.parseExprFromString(i.second.substr(1), CanonPath::fromCwd()), v
|
||||
);
|
||||
else
|
||||
v->mkString(((std::string_view) i.second).substr(1));
|
||||
v.mkString(((std::string_view) i.second).substr(1));
|
||||
res.insert(state.symbols.create(i.first), v);
|
||||
}
|
||||
return res.finish();
|
||||
|
||||
@@ -12,9 +12,10 @@ namespace nix {
|
||||
InstallableAttrPath::InstallableAttrPath(
|
||||
ref<eval_cache::CachingEvaluator> state,
|
||||
SourceExprCommand & cmd,
|
||||
Value * v,
|
||||
Value & v,
|
||||
const std::string & attrPath,
|
||||
ExtendedOutputsSpec extendedOutputsSpec)
|
||||
ExtendedOutputsSpec extendedOutputsSpec
|
||||
)
|
||||
: InstallableValue(state)
|
||||
, cmd(cmd)
|
||||
, v(allocRootValue(v))
|
||||
@@ -22,10 +23,10 @@ InstallableAttrPath::InstallableAttrPath(
|
||||
, extendedOutputsSpec(std::move(extendedOutputsSpec))
|
||||
{ }
|
||||
|
||||
std::pair<Value *, PosIdx> InstallableAttrPath::toValue(EvalState & state)
|
||||
std::pair<Value, PosIdx> InstallableAttrPath::toValue(EvalState & state)
|
||||
{
|
||||
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(*evaluator), **v);
|
||||
state.forceValue(*vRes, pos);
|
||||
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(*evaluator), *v);
|
||||
state.forceValue(vRes, pos);
|
||||
return {vRes, pos};
|
||||
}
|
||||
|
||||
@@ -34,7 +35,7 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths(EvalState & state)
|
||||
auto [v, pos] = toValue(state);
|
||||
|
||||
if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths(
|
||||
state, *v, pos, fmt("while evaluating the attribute '%s'", attrPath)
|
||||
state, v, pos, fmt("while evaluating the attribute '%s'", attrPath)
|
||||
))
|
||||
{
|
||||
return { *derivedPathWithInfo };
|
||||
@@ -43,7 +44,7 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths(EvalState & state)
|
||||
Bindings & autoArgs = *cmd.getAutoArgs(*evaluator);
|
||||
|
||||
DrvInfos drvInfos;
|
||||
getDerivations(state, *v, "", autoArgs, drvInfos, false);
|
||||
getDerivations(state, v, "", autoArgs, drvInfos, false);
|
||||
|
||||
// Backward compatibility hack: group results by drvPath. This
|
||||
// helps keep .all output together.
|
||||
@@ -92,9 +93,10 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths(EvalState & state)
|
||||
InstallableAttrPath InstallableAttrPath::parse(
|
||||
ref<eval_cache::CachingEvaluator> state,
|
||||
SourceExprCommand & cmd,
|
||||
Value * v,
|
||||
Value & v,
|
||||
std::string_view prefix,
|
||||
ExtendedOutputsSpec extendedOutputsSpec)
|
||||
ExtendedOutputsSpec extendedOutputsSpec
|
||||
)
|
||||
{
|
||||
return {
|
||||
state, cmd, v,
|
||||
|
||||
@@ -20,13 +20,14 @@ class InstallableAttrPath : public InstallableValue
|
||||
InstallableAttrPath(
|
||||
ref<eval_cache::CachingEvaluator> state,
|
||||
SourceExprCommand & cmd,
|
||||
Value * v,
|
||||
Value & v,
|
||||
const std::string & attrPath,
|
||||
ExtendedOutputsSpec extendedOutputsSpec);
|
||||
ExtendedOutputsSpec extendedOutputsSpec
|
||||
);
|
||||
|
||||
std::string what() const override { return attrPath; };
|
||||
|
||||
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||
std::pair<Value, PosIdx> toValue(EvalState & state) override;
|
||||
|
||||
DerivedPathsWithInfo toDerivedPaths(EvalState & state) override;
|
||||
|
||||
@@ -35,9 +36,10 @@ public:
|
||||
static InstallableAttrPath parse(
|
||||
ref<eval_cache::CachingEvaluator> state,
|
||||
SourceExprCommand & cmd,
|
||||
Value * v,
|
||||
Value & v,
|
||||
std::string_view prefix,
|
||||
ExtendedOutputsSpec extendedOutputsSpec);
|
||||
ExtendedOutputsSpec extendedOutputsSpec
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -136,9 +136,9 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths(EvalState & state)
|
||||
}};
|
||||
}
|
||||
|
||||
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
|
||||
std::pair<Value, PosIdx> InstallableFlake::toValue(EvalState & state)
|
||||
{
|
||||
return {&getCursor(state)->forceValue(state), noPos};
|
||||
return {getCursor(state)->forceValue(state), noPos};
|
||||
}
|
||||
|
||||
std::vector<ref<eval_cache::AttrCursor>>
|
||||
|
||||
@@ -55,7 +55,7 @@ struct InstallableFlake : InstallableValue
|
||||
|
||||
DerivedPathsWithInfo toDerivedPaths(EvalState & state) override;
|
||||
|
||||
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||
std::pair<Value, PosIdx> toValue(EvalState & state) override;
|
||||
|
||||
/**
|
||||
* Get a cursor to every attrpath in getActualAttrPaths() that
|
||||
|
||||
@@ -9,8 +9,9 @@ std::vector<ref<eval_cache::AttrCursor>>
|
||||
InstallableValue::getCursors(EvalState & state)
|
||||
{
|
||||
auto evalCache =
|
||||
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt,
|
||||
[&](EvalState & state) { return toValue(state).first; });
|
||||
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, [&](EvalState & state) {
|
||||
return toValue(state).first;
|
||||
});
|
||||
return {evalCache->getRoot()};
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ struct InstallableValue : Installable
|
||||
|
||||
virtual ~InstallableValue() { }
|
||||
|
||||
virtual std::pair<Value *, PosIdx> toValue(EvalState & state) = 0;
|
||||
virtual std::pair<Value, PosIdx> toValue(EvalState & state) = 0;
|
||||
|
||||
/**
|
||||
* Get a cursor to each value this Installable could refer to.
|
||||
|
||||
+13
-15
@@ -235,14 +235,13 @@ void SourceExprCommand::completeInstallable(EvalState & state, AddCompletions &
|
||||
prefix_ = "";
|
||||
}
|
||||
|
||||
auto [v, pos] = findAlongAttrPath(state, prefix_, *autoArgs, root);
|
||||
Value &v1(*v);
|
||||
auto [v1, pos] = findAlongAttrPath(state, prefix_, *autoArgs, root);
|
||||
state.forceValue(v1, pos);
|
||||
Value v2;
|
||||
state.autoCallFunction(*autoArgs, v1, v2, pos);
|
||||
|
||||
if (v2.type() == nAttrs) {
|
||||
for (auto & i : *v2.attrs) {
|
||||
for (auto & i : *v2.attrs()) {
|
||||
std::string name{evaluator->symbols[i.name]};
|
||||
if (name.find(searchWord) == 0) {
|
||||
if (prefix_ == "")
|
||||
@@ -412,12 +411,12 @@ ref<eval_cache::EvalCache> openEvalCache(
|
||||
if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0")
|
||||
throw Error("not everything is cached, but evaluation is not allowed");
|
||||
|
||||
auto vFlake = state.ctx.mem.allocValue();
|
||||
flake::callFlake(state, *lockedFlake, *vFlake);
|
||||
Value vFlake;
|
||||
flake::callFlake(state, *lockedFlake, vFlake);
|
||||
|
||||
state.forceAttrs(*vFlake, noPos, "while parsing cached flake data");
|
||||
state.forceAttrs(vFlake, noPos, "while parsing cached flake data");
|
||||
|
||||
auto aOutputs = vFlake->attrs->get(state.ctx.symbols.create("outputs"));
|
||||
auto aOutputs = vFlake.attrs()->get(state.ctx.symbols.create("outputs"));
|
||||
assert(aOutputs);
|
||||
|
||||
return aOutputs->value;
|
||||
@@ -450,25 +449,24 @@ Installables SourceExprCommand::parseInstallables(
|
||||
throw UsageError("'--file' and '--expr' are exclusive");
|
||||
|
||||
auto evaluator = getEvaluator();
|
||||
auto vFile = evaluator->mem.allocValue();
|
||||
Value vFile;
|
||||
|
||||
if (file == "-") {
|
||||
auto & e = evaluator->parseStdin();
|
||||
state.eval(e, *vFile);
|
||||
state.eval(e, vFile);
|
||||
}
|
||||
else if (file)
|
||||
state.evalFile(state.aio.blockOn(lookupFileArg(*evaluator, *file)).unwrap(), *vFile);
|
||||
state.evalFile(state.aio.blockOn(lookupFileArg(*evaluator, *file)).unwrap(), vFile);
|
||||
else {
|
||||
auto & e = evaluator->parseExprFromString(*expr, CanonPath::fromCwd());
|
||||
state.eval(e, *vFile);
|
||||
state.eval(e, vFile);
|
||||
}
|
||||
|
||||
for (auto & s : ss) {
|
||||
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
|
||||
result.push_back(
|
||||
make_ref<InstallableAttrPath>(
|
||||
InstallableAttrPath::parse(
|
||||
evaluator, *this, vFile, std::move(prefix), std::move(extendedOutputsSpec))));
|
||||
result.push_back(make_ref<InstallableAttrPath>(InstallableAttrPath::parse(
|
||||
evaluator, *this, vFile, std::move(prefix), std::move(extendedOutputsSpec)
|
||||
)));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
+65
-57
@@ -5,6 +5,7 @@
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
|
||||
#include "lix/libexpr/value.hh"
|
||||
#include "lix/libutil/box_ptr.hh"
|
||||
#include "lix/libcmd/repl-interacter.hh"
|
||||
#include "lix/libcmd/repl.hh"
|
||||
@@ -173,35 +174,37 @@ struct NixRepl
|
||||
/**
|
||||
* Get a list of each of the `repl-overlays` (parsed and evaluated).
|
||||
*/
|
||||
Value * replOverlays();
|
||||
Value replOverlays();
|
||||
|
||||
/**
|
||||
* Get the Nix function that composes the `repl-overlays` together.
|
||||
*/
|
||||
Value * getReplOverlaysEvalFunction();
|
||||
Value getReplOverlaysEvalFunction();
|
||||
|
||||
/**
|
||||
* Cached return value of `getReplOverlaysEvalFunction`.
|
||||
*
|
||||
* Note: This is `shared_ptr` to avoid garbage collection.
|
||||
*/
|
||||
std::shared_ptr<Value *> replOverlaysEvalFunction =
|
||||
std::allocate_shared<Value *>(TraceableAllocator<Value *>(), nullptr);
|
||||
std::shared_ptr<std::optional<Value>> replOverlaysEvalFunction =
|
||||
std::allocate_shared<std::optional<Value>>(
|
||||
TraceableAllocator<std::optional<Value>>(), std::nullopt
|
||||
);
|
||||
|
||||
/**
|
||||
* Get the `info` AttrSet that's passed as the first argument to each
|
||||
* of the `repl-overlays`.
|
||||
*/
|
||||
Value * replInitInfo();
|
||||
Value replInitInfo();
|
||||
|
||||
/**
|
||||
* Get the current top-level bindings as an AttrSet.
|
||||
*/
|
||||
Value * bindingsToAttrs();
|
||||
Value bindingsToAttrs();
|
||||
/**
|
||||
* Parse a file, evaluate its result, and force the resulting value.
|
||||
*/
|
||||
Value * evalFile(SourcePath & path);
|
||||
Value evalFile(SourcePath & path);
|
||||
|
||||
void printValue(std::ostream & str,
|
||||
Value & v,
|
||||
@@ -450,7 +453,7 @@ StringSet NixRepl::completePrefix(const std::string &prefix)
|
||||
e.eval(state, *env, v);
|
||||
state.forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)");
|
||||
|
||||
for (auto & i : *v.attrs) {
|
||||
for (auto & i : *v.attrs()) {
|
||||
std::ostringstream output;
|
||||
printAttributeName(output, evaluator.symbols[i.name]);
|
||||
std::string name = output.str();
|
||||
@@ -653,7 +656,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
auto path = state.coerceToPath(noPos, v, context, "while evaluating the filename to edit");
|
||||
return {path, 0};
|
||||
} else if (v.isLambda()) {
|
||||
auto pos = evaluator.positions[v.lambda.fun->pos];
|
||||
auto pos = evaluator.positions[v.lambda().fun->pos];
|
||||
if (auto path = std::get_if<CheckedSourcePath>(&pos.origin))
|
||||
return {*path, pos.line};
|
||||
else
|
||||
@@ -822,7 +825,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
|
||||
logger->cout(trim(renderMarkdownToTerminal(markdown)));
|
||||
} else if (v.isLambda()) {
|
||||
auto pos = evaluator.positions[v.lambda.fun->pos];
|
||||
auto pos = evaluator.positions[v.lambda().fun->pos];
|
||||
if (auto path = std::get_if<CheckedSourcePath>(&pos.origin)) {
|
||||
// Path and position have now been obtained, feed to nix-doc library to get data.
|
||||
auto docComment = lambdaDocsForPos(*path, pos);
|
||||
@@ -862,10 +865,10 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
std::visit(overloaded {
|
||||
[&](ExprReplBindings & b) {
|
||||
for (auto & [name, e] : b.symbols) {
|
||||
Value * v = state.ctx.mem.allocValue();
|
||||
e->eval(state, *env, *v);
|
||||
Value v;
|
||||
e->eval(state, *env, v);
|
||||
(void) e.release(); // NOLINT(bugprone-unused-return-value): leak because of thunk references
|
||||
addVarToScope(name, *v);
|
||||
addVarToScope(name, v);
|
||||
}
|
||||
},
|
||||
[&](std::unique_ptr<Expr> & e) {
|
||||
@@ -948,7 +951,7 @@ void NixRepl::loadFiles()
|
||||
|
||||
for (auto & [i, what] : getValues()) {
|
||||
notice("Loading installable '%1%'...", Magenta(what));
|
||||
addAttrsToScope(*i);
|
||||
addAttrsToScope(i);
|
||||
}
|
||||
|
||||
loadReplOverlays();
|
||||
@@ -963,9 +966,9 @@ void NixRepl::loadReplOverlays()
|
||||
notice("Loading '%1%'...", "repl-overlays");
|
||||
auto replInitFilesFunction = getReplOverlaysEvalFunction();
|
||||
|
||||
Value &newAttrs(*evaluator.mem.allocValue());
|
||||
SmallValueVector<3> args = {replInitInfo(), bindingsToAttrs(), replOverlays()};
|
||||
state.callFunction(*replInitFilesFunction, args.size(), args.data(), newAttrs, noPos);
|
||||
Value newAttrs;
|
||||
Value args[] = {replInitInfo(), bindingsToAttrs(), replOverlays()};
|
||||
state.callFunction(replInitFilesFunction, args, newAttrs, noPos);
|
||||
|
||||
// n.b. this does in fact load the stuff into the environment twice (once
|
||||
// from the superset of the environment returned by repl-overlays and once
|
||||
@@ -975,14 +978,14 @@ void NixRepl::loadReplOverlays()
|
||||
addAttrsToScope(newAttrs);
|
||||
}
|
||||
|
||||
Value * NixRepl::getReplOverlaysEvalFunction()
|
||||
Value NixRepl::getReplOverlaysEvalFunction()
|
||||
{
|
||||
if (replOverlaysEvalFunction && *replOverlaysEvalFunction) {
|
||||
return *replOverlaysEvalFunction;
|
||||
return **replOverlaysEvalFunction;
|
||||
}
|
||||
|
||||
auto evalReplInitFilesPath = CanonPath::root + "repl-overlays.nix";
|
||||
*replOverlaysEvalFunction = evaluator.mem.allocValue();
|
||||
*replOverlaysEvalFunction = Value{};
|
||||
auto code =
|
||||
#include "repl-overlays.nix.gen.hh"
|
||||
;
|
||||
@@ -994,14 +997,14 @@ Value * NixRepl::getReplOverlaysEvalFunction()
|
||||
|
||||
state.eval(expr, **replOverlaysEvalFunction);
|
||||
|
||||
return *replOverlaysEvalFunction;
|
||||
return **replOverlaysEvalFunction;
|
||||
}
|
||||
|
||||
Value * NixRepl::replOverlays()
|
||||
Value NixRepl::replOverlays()
|
||||
{
|
||||
Value * replInits(evaluator.mem.allocValue());
|
||||
*replInits = evaluator.mem.newList(evalSettings.replOverlays.get().size());
|
||||
Value ** replInitElems = replInits->listElems();
|
||||
Value replInits;
|
||||
auto replInitElems = evaluator.mem.newList(evalSettings.replOverlays.get().size());
|
||||
replInits = {NewValueAs::list, replInitElems};
|
||||
|
||||
size_t i = 0;
|
||||
for (auto path : evalSettings.replOverlays.get()) {
|
||||
@@ -1017,27 +1020,32 @@ Value * NixRepl::replOverlays()
|
||||
auto replInit = evalFile(sourcePath);
|
||||
evalSettings.pureEval.setDefault(prevPureEval);
|
||||
|
||||
if (!replInit->isLambda()) {
|
||||
evaluator.errors.make<TypeError>(
|
||||
"Expected `repl-overlays` entry %s to be a lambda but found %s: %s",
|
||||
path,
|
||||
showType(*replInit),
|
||||
ValuePrinter(state, *replInit, errorPrintOptions)
|
||||
)
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
if (auto attrs = dynamic_cast<AttrsPattern *>(replInit->lambda.fun->pattern.get()); attrs && !attrs->ellipsis) {
|
||||
evaluator.errors.make<TypeError>(
|
||||
"Expected first argument of %1% to have %2% to allow future versions of Lix to add additional attributes to the argument",
|
||||
"repl-overlays",
|
||||
"..."
|
||||
)
|
||||
.atPos(replInit->lambda.fun->pos)
|
||||
if (!replInit.isLambda()) {
|
||||
evaluator.errors
|
||||
.make<TypeError>(
|
||||
"Expected `repl-overlays` entry %s to be a lambda but found %s: %s",
|
||||
path,
|
||||
showType(replInit),
|
||||
ValuePrinter(state, replInit, errorPrintOptions)
|
||||
)
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
replInitElems[i] = replInit;
|
||||
if (auto attrs = dynamic_cast<AttrsPattern *>(replInit.lambda().fun->pattern.get());
|
||||
attrs && !attrs->ellipsis)
|
||||
{
|
||||
evaluator.errors
|
||||
.make<TypeError>(
|
||||
"Expected first argument of %1% to have %2% to allow future versions of Lix to "
|
||||
"add additional attributes to the argument",
|
||||
"repl-overlays",
|
||||
"..."
|
||||
)
|
||||
.atPos(replInit.lambda().fun->pos)
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
replInitElems->elems[i] = replInit;
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -1045,16 +1053,16 @@ Value * NixRepl::replOverlays()
|
||||
return replInits;
|
||||
}
|
||||
|
||||
Value * NixRepl::replInitInfo()
|
||||
Value NixRepl::replInitInfo()
|
||||
{
|
||||
auto builder = evaluator.buildBindings(2);
|
||||
|
||||
Value * currentSystem(evaluator.mem.allocValue());
|
||||
currentSystem->mkString(evalSettings.getCurrentSystem());
|
||||
Value currentSystem;
|
||||
currentSystem.mkString(evalSettings.getCurrentSystem());
|
||||
builder.insert(evaluator.symbols.create("currentSystem"), currentSystem);
|
||||
|
||||
Value * info(evaluator.mem.allocValue());
|
||||
info->mkAttrs(builder.finish());
|
||||
Value info;
|
||||
info.mkAttrs(builder.finish());
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -1090,7 +1098,7 @@ void NixRepl::addAttrsToScope(Value & attrs)
|
||||
{
|
||||
state.forceAttrs(attrs, noPos, "while evaluating an attribute set to be merged in the global scope");
|
||||
addToScope(
|
||||
*attrs.attrs, [](const Attr & a) { return a.name; }, [](const Attr & a) { return a.value; }
|
||||
*attrs.attrs(), [](const Attr & a) { return a.name; }, [](const Attr & a) { return a.value; }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1112,19 +1120,19 @@ void NixRepl::addVarToScope(const Symbol name, Value & v)
|
||||
} else {
|
||||
notice("Added %s.", evaluator.symbols[name]);
|
||||
}
|
||||
env->values[displ++] = &v;
|
||||
env->values[displ++] = v;
|
||||
varNames.emplace(evaluator.symbols[name]);
|
||||
}
|
||||
|
||||
Value * NixRepl::bindingsToAttrs()
|
||||
Value NixRepl::bindingsToAttrs()
|
||||
{
|
||||
auto builder = evaluator.buildBindings(staticEnv->vars.size());
|
||||
for (auto & [symbol, displacement] : staticEnv->vars) {
|
||||
builder.insert(symbol, env->values[displacement]);
|
||||
}
|
||||
|
||||
Value * attrs(evaluator.mem.allocValue());
|
||||
attrs->mkAttrs(builder.finish());
|
||||
Value attrs;
|
||||
attrs.mkAttrs(builder.finish());
|
||||
return attrs;
|
||||
}
|
||||
|
||||
@@ -1147,12 +1155,12 @@ void NixRepl::evalString(std::string s, Value & v)
|
||||
state.forceValue(v, noPos);
|
||||
}
|
||||
|
||||
Value * NixRepl::evalFile(SourcePath & path)
|
||||
Value NixRepl::evalFile(SourcePath & path)
|
||||
{
|
||||
auto & expr = evaluator.parseExprFromFile(evaluator.paths.checkSourcePath(path), staticEnv);
|
||||
Value * result(evaluator.mem.allocValue());
|
||||
expr.eval(state, *env, *result);
|
||||
state.forceValue(*result, noPos);
|
||||
Value result;
|
||||
expr.eval(state, *env, result);
|
||||
state.forceValue(result, noPos);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ namespace nix {
|
||||
|
||||
struct AbstractNixRepl : NeverAsync
|
||||
{
|
||||
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
|
||||
typedef std::vector<std::pair<Value, std::string>> AnnotatedValues;
|
||||
|
||||
static ReplExitStatus
|
||||
run(const SearchPath & searchPath,
|
||||
|
||||
+24
-22
@@ -69,13 +69,12 @@ std::string unparseAttrPath(std::vector<std::string> const & attrPath)
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
|
||||
std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::string & attrPath,
|
||||
Bindings & autoArgs, Value & vIn)
|
||||
std::pair<Value, PosIdx>
|
||||
findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & autoArgs, Value & vIn)
|
||||
{
|
||||
auto tokens = parseAttrPath(attrPath);
|
||||
|
||||
Value * v = &vIn;
|
||||
Value v = vIn;
|
||||
PosIdx pos = noPos;
|
||||
|
||||
for (auto [attrPathIdx, attr] : enumerate(tokens)) {
|
||||
@@ -84,10 +83,10 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
||||
auto attrIndex = string2Int<unsigned int>(attr);
|
||||
|
||||
/* Evaluate the expression. */
|
||||
Value * vNew = state.ctx.mem.allocValue();
|
||||
state.autoCallFunction(autoArgs, *v, *vNew, pos);
|
||||
Value vNew;
|
||||
state.autoCallFunction(autoArgs, v, vNew, pos);
|
||||
v = vNew;
|
||||
state.forceValue(*v, noPos);
|
||||
state.forceValue(v, noPos);
|
||||
|
||||
/* It should evaluate to either a set or an expression,
|
||||
according to what is specified in the attrPath. */
|
||||
@@ -96,7 +95,7 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
||||
if (attr.empty())
|
||||
throw Error("empty attribute name in selection path '%1%'", attrPath);
|
||||
|
||||
if (v->type() != nAttrs) {
|
||||
if (v.type() != nAttrs) {
|
||||
auto pathPart =
|
||||
std::vector<std::string>(tokens.begin(), tokens.begin() + attrPathIdx);
|
||||
state.ctx.errors
|
||||
@@ -105,17 +104,18 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
||||
"set but is %3%: %4%",
|
||||
attrPath,
|
||||
unparseAttrPath(pathPart),
|
||||
showType(*v),
|
||||
ValuePrinter(state, *v, errorPrintOptions)
|
||||
showType(v),
|
||||
ValuePrinter(state, v, errorPrintOptions)
|
||||
)
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
auto a = v->attrs->get(state.ctx.symbols.create(attr));
|
||||
auto a = v.attrs()->get(state.ctx.symbols.create(attr));
|
||||
if (!a) {
|
||||
std::set<std::string> attrNames;
|
||||
for (auto & attr : *v->attrs)
|
||||
for (auto & attr : *v.attrs()) {
|
||||
attrNames.emplace(state.ctx.symbols[attr.name]);
|
||||
}
|
||||
|
||||
auto suggestions = Suggestions::bestMatches(attrNames, attr);
|
||||
auto pathPart =
|
||||
@@ -127,33 +127,33 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
||||
attr,
|
||||
attrPath,
|
||||
unparseAttrPath(pathPart),
|
||||
ValuePrinter(state, *v, errorPrintOptions)
|
||||
ValuePrinter(state, v, errorPrintOptions)
|
||||
);
|
||||
}
|
||||
v = &*a->value;
|
||||
v = a->value;
|
||||
pos = a->pos;
|
||||
} else {
|
||||
if (!v->isList()) {
|
||||
if (!v.isList()) {
|
||||
state.ctx.errors
|
||||
.make<TypeError>(
|
||||
"the expression selected by the selection path '%1%' should be a list but "
|
||||
"is %2%: %3%",
|
||||
attrPath,
|
||||
showType(*v),
|
||||
ValuePrinter(state, *v, errorPrintOptions)
|
||||
showType(v),
|
||||
ValuePrinter(state, v, errorPrintOptions)
|
||||
)
|
||||
.debugThrow();
|
||||
}
|
||||
if (*attrIndex >= v->listSize()) {
|
||||
if (*attrIndex >= v.listSize()) {
|
||||
throw AttrPathNotFound(
|
||||
"list index %1% in selection path '%2%' is out of range for list %3%",
|
||||
*attrIndex,
|
||||
attrPath,
|
||||
ValuePrinter(state, *v, errorPrintOptions)
|
||||
ValuePrinter(state, v, errorPrintOptions)
|
||||
);
|
||||
}
|
||||
|
||||
v = v->listElems()[*attrIndex];
|
||||
v = v.listElems()[*attrIndex];
|
||||
pos = noPos;
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
||||
|
||||
std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what)
|
||||
{
|
||||
Value * v2;
|
||||
Value v2;
|
||||
try {
|
||||
auto dummyArgs = state.ctx.mem.allocBindings(0);
|
||||
v2 = findAlongAttrPath(state, "meta.position", *dummyArgs, v).first;
|
||||
@@ -176,7 +176,9 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
|
||||
// FIXME: is it possible to extract the Pos object instead of doing this
|
||||
// toString + parsing?
|
||||
NixStringContext context;
|
||||
auto path = state.coerceToPath(noPos, *v2, context, "while evaluating the 'meta.position' attribute of a derivation");
|
||||
auto path = state.coerceToPath(
|
||||
noPos, v2, context, "while evaluating the 'meta.position' attribute of a derivation"
|
||||
);
|
||||
|
||||
auto fn = path.canonical().abs();
|
||||
|
||||
|
||||
@@ -10,11 +10,9 @@ namespace nix {
|
||||
MakeError(AttrPathNotFound, Error);
|
||||
MakeError(NoPositionInfo, Error);
|
||||
|
||||
std::pair<Value *, PosIdx> findAlongAttrPath(
|
||||
EvalState & state,
|
||||
const std::string & attrPath,
|
||||
Bindings & autoArgs,
|
||||
Value & vIn);
|
||||
std::pair<Value, PosIdx> findAlongAttrPath(
|
||||
EvalState & state, const std::string & attrPath, Bindings & autoArgs, Value & vIn
|
||||
);
|
||||
|
||||
/**
|
||||
* Heuristic to find the filename and lineno or a nix value.
|
||||
|
||||
@@ -20,15 +20,14 @@ Bindings * EvalMemory::allocBindings(size_t capacity)
|
||||
throw Error("attribute set of size %d is too big", capacity);
|
||||
stats.nrAttrsets++;
|
||||
stats.nrAttrsInAttrsets += capacity;
|
||||
return new (gcAllocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings();
|
||||
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings();
|
||||
}
|
||||
|
||||
|
||||
Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
|
||||
{
|
||||
auto value = mem.allocValue();
|
||||
bindings->push_back(Attr(name, value, pos));
|
||||
return *value;
|
||||
bindings->push_back(Attr(name, {}, pos));
|
||||
return (bindings->end() - 1)->value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,9 +23,8 @@ struct Attr
|
||||
way we keep Attr size at two words with no wasted space. */
|
||||
Symbol name;
|
||||
PosIdx pos;
|
||||
Value * value;
|
||||
Attr(Symbol name, Value * value, PosIdx pos = noPos)
|
||||
: name(name), pos(pos), value(value) { };
|
||||
mutable Value value;
|
||||
Attr(Symbol name, Value value, PosIdx pos = noPos) : name(name), pos(pos), value(value) {}
|
||||
Attr() { };
|
||||
bool operator < (const Attr & a) const
|
||||
{
|
||||
@@ -73,7 +72,7 @@ public:
|
||||
|
||||
const Attr * get(Symbol name)
|
||||
{
|
||||
Attr key(name, 0);
|
||||
Attr key(name, {});
|
||||
iterator i = std::lower_bound(begin(), end(), key);
|
||||
if (i != end() && i->name == name) return &*i;
|
||||
return nullptr;
|
||||
@@ -136,7 +135,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void insert(Symbol name, Value * value, PosIdx pos = noPos)
|
||||
void insert(Symbol name, Value value, PosIdx pos = noPos)
|
||||
{
|
||||
insert(Attr(name, value, pos));
|
||||
}
|
||||
|
||||
+22
-16
@@ -341,7 +341,7 @@ EvalCache::EvalCache(
|
||||
{
|
||||
}
|
||||
|
||||
Value * EvalCache::getRootValue(EvalState & state)
|
||||
Value & EvalCache::getRootValue(EvalState & state)
|
||||
{
|
||||
if (!value) {
|
||||
debug("getting root value");
|
||||
@@ -362,8 +362,9 @@ AttrCursor::AttrCursor(
|
||||
std::optional<std::pair<AttrId, AttrValue>> && cachedValue)
|
||||
: root(root), parent(parent), cachedValue(std::move(cachedValue))
|
||||
{
|
||||
if (value)
|
||||
_value = allocRootValue(value);
|
||||
if (value) {
|
||||
_value = allocRootValue(*value);
|
||||
}
|
||||
}
|
||||
|
||||
AttrKey AttrCursor::getKey()
|
||||
@@ -383,14 +384,14 @@ Value & AttrCursor::getValue(EvalState & state)
|
||||
if (parent) {
|
||||
auto & vParent = parent->first->getValue(state);
|
||||
state.forceAttrs(vParent, noPos, "while searching for an attribute");
|
||||
auto attr = vParent.attrs->get(state.ctx.symbols.create(parent->second));
|
||||
auto attr = vParent.attrs()->get(state.ctx.symbols.create(parent->second));
|
||||
if (!attr)
|
||||
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr(state));
|
||||
_value = allocRootValue(attr->value);
|
||||
} else
|
||||
_value = allocRootValue(root->getRootValue(state));
|
||||
}
|
||||
return **_value;
|
||||
return *_value;
|
||||
}
|
||||
|
||||
std::vector<std::string> AttrCursor::getAttrPath(EvalState & state) const
|
||||
@@ -438,16 +439,16 @@ Value & AttrCursor::forceValue(EvalState & state)
|
||||
if (root->db && (!cachedValue || std::get_if<placeholder_t>(&cachedValue->second))) {
|
||||
if (v.type() == nString)
|
||||
cachedValue = {
|
||||
root->db->setString(getKey(), v.str(), v.string.context), string_t{v.str(), {}}
|
||||
root->db->setString(getKey(), v.str(), v.string().context), string_t{v.str(), {}}
|
||||
};
|
||||
else if (v.type() == nPath) {
|
||||
auto path = v.path().canonical().abs();
|
||||
cachedValue = {root->db->setString(getKey(), path), string_t{path, {}}};
|
||||
}
|
||||
else if (v.type() == nBool)
|
||||
cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean};
|
||||
cachedValue = {root->db->setBool(getKey(), v.boolean()), v.boolean()};
|
||||
else if (v.type() == nInt)
|
||||
cachedValue = {root->db->setInt(getKey(), v.integer.value), int_t{v.integer}};
|
||||
cachedValue = {root->db->setInt(getKey(), v.integer().value), int_t{v.integer()}};
|
||||
else if (v.type() == nAttrs)
|
||||
; // FIXME: do something?
|
||||
else
|
||||
@@ -500,7 +501,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(EvalState & state, const st
|
||||
return nullptr;
|
||||
//errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
|
||||
auto attr = v.attrs->get(state.ctx.symbols.create(name));
|
||||
auto attr = v.attrs()->get(state.ctx.symbols.create(name));
|
||||
|
||||
if (!attr) {
|
||||
if (root->db) {
|
||||
@@ -519,7 +520,8 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(EvalState & state, const st
|
||||
}
|
||||
|
||||
return make_ref<AttrCursor>(
|
||||
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
|
||||
root, std::make_pair(shared_from_this(), name), &attr->value, std::move(cachedValue2)
|
||||
);
|
||||
}
|
||||
|
||||
ref<AttrCursor> AttrCursor::getAttr(EvalState & state, const std::string & name)
|
||||
@@ -633,7 +635,7 @@ bool AttrCursor::getBool(EvalState & state)
|
||||
if (v.type() != nBool)
|
||||
state.ctx.errors.make<TypeError>("'%s' is not a Boolean", getAttrPathStr(state)).debugThrow();
|
||||
|
||||
return v.boolean;
|
||||
return v.boolean();
|
||||
}
|
||||
|
||||
NixInt AttrCursor::getInt(EvalState & state)
|
||||
@@ -655,7 +657,7 @@ NixInt AttrCursor::getInt(EvalState & state)
|
||||
if (v.type() != nInt)
|
||||
state.ctx.errors.make<TypeError>("'%s' is not an integer", getAttrPathStr(state)).debugThrow();
|
||||
|
||||
return v.integer;
|
||||
return v.integer();
|
||||
}
|
||||
|
||||
std::vector<std::string> AttrCursor::getListOfStrings(EvalState & state)
|
||||
@@ -682,11 +684,15 @@ std::vector<std::string> AttrCursor::getListOfStrings(EvalState & state)
|
||||
|
||||
std::vector<std::string> res;
|
||||
|
||||
for (auto & elem : v.listItems())
|
||||
res.push_back(std::string(state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching")));
|
||||
for (auto & elem : v.listItems()) {
|
||||
res.push_back(std::string(
|
||||
state.forceStringNoCtx(elem, noPos, "while evaluating an attribute for caching")
|
||||
));
|
||||
}
|
||||
|
||||
if (root->db)
|
||||
if (root->db) {
|
||||
cachedValue = {root->db->setListOfStrings(getKey(), res), res};
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -711,7 +717,7 @@ std::vector<std::string> AttrCursor::getAttrs(EvalState & state)
|
||||
state.ctx.errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr(state)).debugThrow();
|
||||
|
||||
fullattr_t attrs;
|
||||
for (auto & attr : *getValue(state).attrs)
|
||||
for (auto & attr : *getValue(state).attrs())
|
||||
attrs.p.emplace_back(state.ctx.symbols[attr.name]);
|
||||
std::sort(attrs.p.begin(), attrs.p.end());
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace nix::eval_cache {
|
||||
struct AttrDb;
|
||||
class AttrCursor;
|
||||
|
||||
typedef std::function<Value *(EvalState &)> RootLoader;
|
||||
typedef std::function<Value(EvalState &)> RootLoader;
|
||||
|
||||
/**
|
||||
* EvalState with caching support. Historically this was part of EvalState,
|
||||
@@ -42,7 +42,7 @@ class EvalCache : public std::enable_shared_from_this<EvalCache>
|
||||
RootLoader rootLoader;
|
||||
RootValue value;
|
||||
|
||||
Value * getRootValue(EvalState & state);
|
||||
Value & getRootValue(EvalState & state);
|
||||
|
||||
public:
|
||||
|
||||
|
||||
+94
-43
@@ -5,59 +5,94 @@
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libexpr/eval-error.hh"
|
||||
#include "lix/libexpr/gc-alloc.hh"
|
||||
#include "value.hh"
|
||||
#include <cstdint>
|
||||
|
||||
namespace nix {
|
||||
|
||||
inline Value::Value(app_t, EvalMemory & mem, Value & lhs, Value & rhs)
|
||||
{
|
||||
auto app = static_cast<Value::App *>(mem.allocBytes(sizeof(Value::App) + sizeof(Value *)));
|
||||
app->_left = lhs;
|
||||
app->_n = 1;
|
||||
app->_args[0] = rhs;
|
||||
raw = tag(tApp, app);
|
||||
}
|
||||
|
||||
inline Value::Value(app_t, EvalMemory & mem, Value & lhs, std::span<Value> args)
|
||||
{
|
||||
auto app = static_cast<Value::App *>(mem.allocBytes(sizeof(Value::App) + args.size_bytes()));
|
||||
app->_left = lhs;
|
||||
app->_n = args.size();
|
||||
std::copy(args.begin(), args.end(), app->_args);
|
||||
raw = tag(tApp, app);
|
||||
}
|
||||
|
||||
inline Value::Value(thunk_t, EvalMemory & mem, Env & env, Expr & expr)
|
||||
{
|
||||
auto thunk = mem.allocType<Thunk>();
|
||||
*thunk = {._env = reinterpret_cast<uintptr_t>(&env), .expr = &expr};
|
||||
raw = tag(tThunk, thunk);
|
||||
}
|
||||
|
||||
inline Value::Value(lambda_t, EvalMemory & mem, Env & env, ExprLambda & lambda)
|
||||
{
|
||||
auto lp = mem.allocType<Lambda>();
|
||||
new (lp) Lambda{env, lambda};
|
||||
raw = tag(tAuxiliary, lp);
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
Value * EvalMemory::allocValue()
|
||||
void * EvalMemory::allocBytes(size_t size)
|
||||
{
|
||||
#if HAVE_BOEHMGC
|
||||
/* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
|
||||
GC_malloc_many returns a linked list of objects of the given size, where the first word
|
||||
of each object is also the pointer to the next object in the list. This also means that we
|
||||
have to explicitly clear the first word of every object we take. */
|
||||
if (!*valueAllocCache) {
|
||||
*valueAllocCache = GC_malloc_many(sizeof(Value));
|
||||
if (!*valueAllocCache) throw std::bad_alloc();
|
||||
}
|
||||
// NOTE: we purposely do not allocate 0 byte blocks on caches; we never allocate
|
||||
// zero bytes anyway, and it makes cache index calculation a little bit simpler.
|
||||
const auto cacheIdx = (size - 1) / CACHE_INCREMENT;
|
||||
if (cacheIdx < CACHES) {
|
||||
const auto roundedSize = (cacheIdx + 1) * CACHE_INCREMENT;
|
||||
auto & cache = gcCache[cacheIdx];
|
||||
if (!cache) {
|
||||
cache = GC_malloc_many(roundedSize);
|
||||
if (!cache) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
/* GC_NEXT is a convenience macro for accessing the first word of an object.
|
||||
Take the first list item, advance the list to the next item, and clear the next pointer. */
|
||||
void * p = *valueAllocCache;
|
||||
*valueAllocCache = GC_NEXT(p);
|
||||
GC_NEXT(p) = nullptr;
|
||||
#else
|
||||
void * p = gcAllocBytes(sizeof(Value));
|
||||
/* GC_NEXT is a convenience macro for accessing the first word of an object.
|
||||
Take the first list item, advance the list to the next item, and clear the next pointer.
|
||||
*/
|
||||
void * p = cache;
|
||||
cache = GC_NEXT(p);
|
||||
GC_NEXT(p) = nullptr;
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
|
||||
stats.nrValues++;
|
||||
return static_cast<Value *>(p);
|
||||
return gcAllocBytes(size);
|
||||
}
|
||||
|
||||
/// `gcAllocType`, but using allocation caches to amortize allocation overhead.
|
||||
template<typename T>
|
||||
[[gnu::always_inline]]
|
||||
T * EvalMemory::allocType(size_t n)
|
||||
{
|
||||
return static_cast<T *>(allocBytes(checkedArrayAllocSize(sizeof(T), n)));
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
Env & EvalMemory::allocEnv(size_t size)
|
||||
{
|
||||
static_assert(CACHES * CACHE_INCREMENT >= sizeof(Env) + sizeof(Value *));
|
||||
|
||||
stats.nrEnvs++;
|
||||
stats.nrValuesInEnvs += size;
|
||||
|
||||
Env * env;
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
if (size == 1) {
|
||||
/* see allocValue for explanations. */
|
||||
if (!*env1AllocCache) {
|
||||
*env1AllocCache = GC_malloc_many(sizeof(Env) + sizeof(Value *));
|
||||
if (!*env1AllocCache) throw std::bad_alloc();
|
||||
}
|
||||
|
||||
void * p = *env1AllocCache;
|
||||
*env1AllocCache = GC_NEXT(p);
|
||||
GC_NEXT(p) = nullptr;
|
||||
env = static_cast<Env *>(p);
|
||||
} else
|
||||
#endif
|
||||
env = static_cast<Env *>(gcAllocBytes(sizeof(Env) + size * sizeof(Value *)));
|
||||
Env * env = static_cast<Env *>(allocBytes(sizeof(Env) + size * sizeof(Value *)));
|
||||
|
||||
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
|
||||
|
||||
@@ -69,22 +104,38 @@ Env & EvalMemory::allocEnv(size_t size)
|
||||
void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||
{
|
||||
if (v.isThunk()) {
|
||||
Env * env = v.thunk.env;
|
||||
Expr & expr = *v.thunk.expr;
|
||||
try {
|
||||
v.mkBlackhole();
|
||||
expr.eval(*this, *env, v);
|
||||
} catch (...) {
|
||||
v.mkThunk(env, expr);
|
||||
tryFixupBlackHolePos(v, pos);
|
||||
throw;
|
||||
auto & thunk = v.thunk();
|
||||
if (thunk.resolved()) {
|
||||
v = thunk.result();
|
||||
} else {
|
||||
const auto backup = thunk;
|
||||
Env * env = thunk.env();
|
||||
Expr & expr = *thunk.expr;
|
||||
thunk = Value::blackHole;
|
||||
try {
|
||||
expr.eval(*this, *env, v);
|
||||
thunk.resolve(v);
|
||||
} catch (...) {
|
||||
thunk = backup;
|
||||
tryFixupBlackHolePos(v, pos);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
} else if (v.isApp()) {
|
||||
auto & app = v.app();
|
||||
if (app.resolved()) {
|
||||
v = app.result();
|
||||
} else {
|
||||
auto target = app.target();
|
||||
if (!target.isPrimOp() || target.primOp()->arity <= app.totalArgs()) {
|
||||
auto tmp = v.app().left();
|
||||
callFunction(tmp, v.app().args(), v, pos);
|
||||
app.resolve(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (v.isApp())
|
||||
callFunction(*v.app.left, *v.app.right, v, pos);
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
|
||||
+343
-283
File diff suppressed because it is too large
Load Diff
+20
-67
@@ -36,52 +36,7 @@ namespace eval_cache {
|
||||
class EvalCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that implements a primop.
|
||||
*/
|
||||
using PrimOpImpl = void(EvalState & state, Value ** args, Value & v);
|
||||
|
||||
/**
|
||||
* Info about a primitive operation, and its implementation
|
||||
*/
|
||||
struct PrimOp
|
||||
{
|
||||
/**
|
||||
* Name of the primop. `__` prefix is treated specially.
|
||||
*/
|
||||
std::string name;
|
||||
|
||||
/**
|
||||
* Names of the parameters of a primop, for primops that take a
|
||||
* fixed number of arguments to be substituted for these parameters.
|
||||
*/
|
||||
std::vector<std::string> args;
|
||||
|
||||
/**
|
||||
* Aritiy of the primop.
|
||||
*
|
||||
* If `args` is not empty, this field will be computed from that
|
||||
* field instead, so it doesn't need to be manually set.
|
||||
*/
|
||||
size_t arity = 0;
|
||||
|
||||
/**
|
||||
* Optional free-form documentation about the primop.
|
||||
*/
|
||||
const char * doc = nullptr;
|
||||
|
||||
/**
|
||||
* Implementation of the primop.
|
||||
*/
|
||||
std::function<PrimOpImpl> fun;
|
||||
|
||||
/**
|
||||
* Optional experimental for this to be gated on.
|
||||
*/
|
||||
std::optional<ExperimentalFeature> experimentalFeature;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & output, PrimOp & primOp);
|
||||
std::ostream & operator<<(std::ostream & output, const PrimOp & primOp);
|
||||
|
||||
/**
|
||||
* Info about a constant
|
||||
@@ -106,12 +61,12 @@ struct Constant
|
||||
bool impureOnly = false;
|
||||
};
|
||||
|
||||
using ValMap = GcMap<std::string, Value *>;
|
||||
using ValMap = GcMap<std::string, Value>;
|
||||
|
||||
struct Env
|
||||
struct alignas(Value::Acb::TAG_ALIGN) Env
|
||||
{
|
||||
Env * up;
|
||||
Value * values[0];
|
||||
Value values[0];
|
||||
};
|
||||
|
||||
void printEnvBindings(const EvalState &es, const Expr & expr, const Env & env);
|
||||
@@ -224,39 +179,40 @@ struct StaticSymbols
|
||||
|
||||
class EvalMemory
|
||||
{
|
||||
/**
|
||||
* Allocation cache for GC'd Value objects.
|
||||
*/
|
||||
std::shared_ptr<void *> valueAllocCache;
|
||||
static constexpr size_t CACHES = 8;
|
||||
static constexpr size_t CACHE_INCREMENT = sizeof(void *);
|
||||
|
||||
/**
|
||||
* Allocation cache for size-1 Env objects.
|
||||
* Allocation caches for small values.
|
||||
*/
|
||||
std::shared_ptr<void *> env1AllocCache;
|
||||
void * gcCache[CACHES] = {};
|
||||
|
||||
public:
|
||||
struct Statistics
|
||||
{
|
||||
unsigned long nrEnvs = 0;
|
||||
unsigned long nrValuesInEnvs = 0;
|
||||
unsigned long nrValues = 0;
|
||||
unsigned long nrAttrsets = 0;
|
||||
unsigned long nrAttrsInAttrsets = 0;
|
||||
unsigned long nrListElems = 0;
|
||||
};
|
||||
|
||||
EvalMemory();
|
||||
~EvalMemory();
|
||||
|
||||
EvalMemory(const EvalMemory &) = delete;
|
||||
EvalMemory(EvalMemory &&) = delete;
|
||||
EvalMemory & operator=(const EvalMemory &) = delete;
|
||||
EvalMemory & operator=(EvalMemory &&) = delete;
|
||||
|
||||
inline Value * allocValue();
|
||||
inline void * allocBytes(size_t size);
|
||||
template<typename T>
|
||||
inline T * allocType(size_t n = 1);
|
||||
|
||||
inline Env & allocEnv(size_t size);
|
||||
|
||||
Bindings * allocBindings(size_t capacity);
|
||||
Value newList(size_t length);
|
||||
Value::List * newList(size_t length);
|
||||
|
||||
BindingsBuilder buildBindings(SymbolTable & symbols, size_t capacity)
|
||||
{
|
||||
@@ -307,11 +263,9 @@ private:
|
||||
|
||||
void createBaseEnv(const SearchPath & searchPath, const Path & storeDir);
|
||||
|
||||
Value * addConstant(const std::string & name, const Value & v, Constant info);
|
||||
void addConstant(const std::string & name, const Value & v, Constant info);
|
||||
|
||||
void addConstant(const std::string & name, Value * v, Constant info);
|
||||
|
||||
Value * addPrimOp(PrimOp && primOp);
|
||||
void addPrimOp(PrimOpDetails && primOp);
|
||||
|
||||
Value prepareNixPath(const SearchPath & searchPath);
|
||||
|
||||
@@ -839,13 +793,11 @@ public:
|
||||
|
||||
bool isFunctor(Value & fun);
|
||||
|
||||
// FIXME: use std::span
|
||||
void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos);
|
||||
void callFunction(Value & fun, std::span<Value> args, Value & vRes, const PosIdx pos);
|
||||
|
||||
void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos)
|
||||
{
|
||||
Value * args[] = {&arg};
|
||||
callFunction(fun, 1, args, vRes, pos);
|
||||
callFunction(fun, {&arg, 1}, vRes, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -885,7 +837,8 @@ public:
|
||||
const SingleDerivedPath & p,
|
||||
Value & v);
|
||||
|
||||
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
|
||||
void
|
||||
concatLists(Value & v, std::span<Value> lists, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
+121
-81
@@ -96,7 +96,7 @@ static void expectType(EvalState & state, ValueType type,
|
||||
|
||||
static std::pair<std::map<FlakeId, FlakeInput>, std::optional<fetchers::Attrs>> parseFlakeInputs(
|
||||
EvalState & state,
|
||||
Value * value,
|
||||
Value & value,
|
||||
const PosIdx pos,
|
||||
const std::optional<Path> & baseDir,
|
||||
InputPath lockRootPath,
|
||||
@@ -109,15 +109,15 @@ static void parseFlakeInputAttr(EvalState & state, const Attr & attr, fetchers::
|
||||
// Allow selecting a subset of enum values
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
switch (attr.value->type()) {
|
||||
switch (attr.value.type()) {
|
||||
case nString:
|
||||
attrs.emplace(state.ctx.symbols[attr.name], std::string(attr.value->str()));
|
||||
attrs.emplace(state.ctx.symbols[attr.name], std::string(attr.value.str()));
|
||||
break;
|
||||
case nBool:
|
||||
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool>{attr.value->boolean});
|
||||
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool>{attr.value.boolean()});
|
||||
break;
|
||||
case nInt: {
|
||||
auto intValue = attr.value->integer.value;
|
||||
auto intValue = attr.value.integer().value;
|
||||
|
||||
if (intValue < 0) {
|
||||
state.ctx.errors
|
||||
@@ -137,18 +137,24 @@ static void parseFlakeInputAttr(EvalState & state, const Attr & attr, fetchers::
|
||||
.make<TypeError>(
|
||||
"flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
||||
state.ctx.symbols[attr.name],
|
||||
showType(*attr.value)
|
||||
showType(attr.value)
|
||||
)
|
||||
.debugThrow();
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
static FlakeInput parseFlakeInput(EvalState & state,
|
||||
const std::string & inputName, Value * value, const PosIdx pos,
|
||||
const std::optional<Path> & baseDir, InputPath lockRootPath, unsigned depth)
|
||||
static FlakeInput parseFlakeInput(
|
||||
EvalState & state,
|
||||
const std::string & inputName,
|
||||
Value & value,
|
||||
const PosIdx pos,
|
||||
const std::optional<Path> & baseDir,
|
||||
InputPath lockRootPath,
|
||||
unsigned depth
|
||||
)
|
||||
{
|
||||
expectType(state, nAttrs, *value, pos);
|
||||
expectType(state, nAttrs, value, pos);
|
||||
|
||||
FlakeInput input;
|
||||
|
||||
@@ -160,15 +166,15 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
fetchers::Attrs attrs;
|
||||
std::optional<std::string> url;
|
||||
|
||||
for (nix::Attr attr : *(value->attrs)) {
|
||||
for (nix::Attr attr : *(value.attrs())) {
|
||||
try {
|
||||
if (attr.name == sUrl) {
|
||||
expectType(state, nString, *attr.value, attr.pos);
|
||||
url = attr.value->str();
|
||||
expectType(state, nString, attr.value, attr.pos);
|
||||
url = attr.value.str();
|
||||
attrs.emplace("url", *url);
|
||||
} else if (attr.name == sFlake) {
|
||||
expectType(state, nBool, *attr.value, attr.pos);
|
||||
input.isFlake = attr.value->boolean;
|
||||
expectType(state, nBool, attr.value, attr.pos);
|
||||
input.isFlake = attr.value.boolean();
|
||||
} else if (attr.name == sInputs) {
|
||||
input.overrides =
|
||||
parseFlakeInputs(
|
||||
@@ -176,8 +182,8 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
)
|
||||
.first;
|
||||
} else if (attr.name == sFollows) {
|
||||
expectType(state, nString, *attr.value, attr.pos);
|
||||
auto follows(parseInputPath(attr.value->str()));
|
||||
expectType(state, nString, attr.value, attr.pos);
|
||||
auto follows(parseInputPath(attr.value.str()));
|
||||
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
|
||||
input.follows = follows;
|
||||
} else {
|
||||
@@ -218,7 +224,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
|
||||
static std::pair<std::map<FlakeId, FlakeInput>, std::optional<fetchers::Attrs>> parseFlakeInputs(
|
||||
EvalState & state,
|
||||
Value * value,
|
||||
Value & value,
|
||||
const PosIdx pos,
|
||||
const std::optional<Path> & baseDir,
|
||||
InputPath lockRootPath,
|
||||
@@ -228,10 +234,10 @@ static std::pair<std::map<FlakeId, FlakeInput>, std::optional<fetchers::Attrs>>
|
||||
{
|
||||
std::map<FlakeId, FlakeInput> inputs;
|
||||
|
||||
expectType(state, nAttrs, *value, pos);
|
||||
expectType(state, nAttrs, value, pos);
|
||||
|
||||
std::optional<fetchers::Attrs> selfAttrs = std::nullopt;
|
||||
for (const nix::Attr & inputAttr : *(*value).attrs) {
|
||||
for (const nix::Attr & inputAttr : *value.attrs()) {
|
||||
std::string inputName{state.ctx.symbols[inputAttr.name]};
|
||||
if (inputName == "self") {
|
||||
experimentalFeatureSettings.require(Xp::FlakeSelfAttrs);
|
||||
@@ -241,10 +247,10 @@ static std::pair<std::map<FlakeId, FlakeInput>, std::optional<fetchers::Attrs>>
|
||||
"'self' input attributes not allowed at %s", state.ctx.positions[inputAttr.pos]
|
||||
);
|
||||
}
|
||||
expectType(state, nAttrs, *inputAttr.value, inputAttr.pos);
|
||||
expectType(state, nAttrs, inputAttr.value, inputAttr.pos);
|
||||
|
||||
selfAttrs = selfAttrs.value_or(fetchers::Attrs{});
|
||||
for (auto & attr : *inputAttr.value->attrs) {
|
||||
for (auto & attr : *inputAttr.value.attrs()) {
|
||||
parseFlakeInputAttr(state, attr, *selfAttrs);
|
||||
}
|
||||
} else {
|
||||
@@ -328,14 +334,14 @@ static Flake getFlake(
|
||||
Value vInfo;
|
||||
state.eval(flakeExpr, vInfo);
|
||||
|
||||
if (auto description = vInfo.attrs->get(state.ctx.s.description)) {
|
||||
expectType(state, nString, *description->value, description->pos);
|
||||
flake.description = description->value->str();
|
||||
if (auto description = vInfo.attrs()->get(state.ctx.s.description)) {
|
||||
expectType(state, nString, description->value, description->pos);
|
||||
flake.description = description->value.str();
|
||||
}
|
||||
|
||||
auto sInputs = state.ctx.symbols.create("inputs");
|
||||
|
||||
if (auto inputs = vInfo.attrs->get(sInputs)) {
|
||||
if (auto inputs = vInfo.attrs()->get(sInputs)) {
|
||||
auto [flakeInputs, selfAttrs] =
|
||||
parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath, 0, true);
|
||||
flake.inputs = std::move(flakeInputs);
|
||||
@@ -361,11 +367,14 @@ static Flake getFlake(
|
||||
flake.resolvedRef = resolvedRef;
|
||||
}
|
||||
|
||||
if (auto outputs = vInfo.attrs->get(state.ctx.s.outputs)) {
|
||||
expectType(state, nFunction, *outputs->value, outputs->pos);
|
||||
if (auto outputs = vInfo.attrs()->get(state.ctx.s.outputs)) {
|
||||
expectType(state, nFunction, outputs->value, outputs->pos);
|
||||
|
||||
if (outputs->value->isLambda()) {
|
||||
if (auto pattern = dynamic_cast<AttrsPattern *>(outputs->value->lambda.fun->pattern.get()); pattern) {
|
||||
if (outputs->value.isLambda()) {
|
||||
if (auto pattern =
|
||||
dynamic_cast<AttrsPattern *>(outputs->value.lambda().fun->pattern.get());
|
||||
pattern)
|
||||
{
|
||||
for (auto & formal : pattern->formals) {
|
||||
if (formal.name != state.ctx.s.self)
|
||||
flake.inputs.emplace(
|
||||
@@ -383,46 +392,71 @@ static Flake getFlake(
|
||||
|
||||
auto sNixConfig = state.ctx.symbols.create("nixConfig");
|
||||
|
||||
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
|
||||
expectType(state, nAttrs, *nixConfig->value, nixConfig->pos);
|
||||
if (auto nixConfig = vInfo.attrs()->get(sNixConfig)) {
|
||||
expectType(state, nAttrs, nixConfig->value, nixConfig->pos);
|
||||
|
||||
for (auto & setting : *nixConfig->value->attrs) {
|
||||
forceTrivialValue(state, *setting.value, setting.pos);
|
||||
if (setting.value->type() == nString)
|
||||
for (auto & setting : *nixConfig->value.attrs()) {
|
||||
forceTrivialValue(state, setting.value, setting.pos);
|
||||
if (setting.value.type() == nString) {
|
||||
flake.config.settings.emplace(
|
||||
state.ctx.symbols[setting.name],
|
||||
std::string(state.forceStringNoCtx(*setting.value, setting.pos, "")));
|
||||
else if (setting.value->type() == nPath) {
|
||||
std::string(state.forceStringNoCtx(setting.value, setting.pos, ""))
|
||||
);
|
||||
} else if (setting.value.type() == nPath) {
|
||||
NixStringContext emptyContext = {};
|
||||
flake.config.settings.emplace(
|
||||
state.ctx.symbols[setting.name],
|
||||
state.coerceToString(setting.pos, *setting.value, emptyContext, "", StringCoercionMode::Strict, true, true) .toOwned());
|
||||
}
|
||||
else if (setting.value->type() == nInt)
|
||||
state
|
||||
.coerceToString(
|
||||
setting.pos,
|
||||
setting.value,
|
||||
emptyContext,
|
||||
"",
|
||||
StringCoercionMode::Strict,
|
||||
true,
|
||||
true
|
||||
)
|
||||
.toOwned()
|
||||
);
|
||||
} else if (setting.value.type() == nInt) {
|
||||
flake.config.settings.emplace(
|
||||
state.ctx.symbols[setting.name],
|
||||
state.forceInt(*setting.value, setting.pos, "").value);
|
||||
else if (setting.value->type() == nBool)
|
||||
state.forceInt(setting.value, setting.pos, "").value
|
||||
);
|
||||
} else if (setting.value.type() == nBool) {
|
||||
flake.config.settings.emplace(
|
||||
state.ctx.symbols[setting.name],
|
||||
Explicit<bool> { state.forceBool(*setting.value, setting.pos, "") });
|
||||
else if (setting.value->type() == nList) {
|
||||
Explicit<bool>{state.forceBool(setting.value, setting.pos, "")}
|
||||
);
|
||||
} else if (setting.value.type() == nList) {
|
||||
std::vector<std::string> ss;
|
||||
for (auto elem : setting.value->listItems()) {
|
||||
if (elem->type() != nString)
|
||||
state.ctx.errors.make<TypeError>("list element in flake configuration setting '%s' is %s while a string is expected",
|
||||
state.ctx.symbols[setting.name], showType(*setting.value)).debugThrow();
|
||||
ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos, ""));
|
||||
for (auto & elem : setting.value.listItems()) {
|
||||
if (elem.type() != nString) {
|
||||
state.ctx.errors
|
||||
.make<TypeError>(
|
||||
"list element in flake configuration setting '%s' is %s while a "
|
||||
"string is expected",
|
||||
state.ctx.symbols[setting.name],
|
||||
showType(setting.value)
|
||||
)
|
||||
.debugThrow();
|
||||
}
|
||||
ss.emplace_back(state.forceStringNoCtx(elem, setting.pos, ""));
|
||||
}
|
||||
flake.config.settings.emplace(state.ctx.symbols[setting.name], ss);
|
||||
} else {
|
||||
state.ctx.errors
|
||||
.make<TypeError>(
|
||||
"flake configuration setting '%s' is %s",
|
||||
state.ctx.symbols[setting.name],
|
||||
showType(setting.value)
|
||||
)
|
||||
.debugThrow();
|
||||
}
|
||||
else
|
||||
state.ctx.errors.make<TypeError>("flake configuration setting '%s' is %s",
|
||||
state.ctx.symbols[setting.name], showType(*setting.value)).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & attr : *vInfo.attrs) {
|
||||
for (auto & attr : *vInfo.attrs()) {
|
||||
if (attr.name != state.ctx.s.description &&
|
||||
attr.name != sInputs &&
|
||||
attr.name != state.ctx.s.outputs &&
|
||||
@@ -907,34 +941,39 @@ void callFlake(EvalState & state,
|
||||
const LockedFlake & lockedFlake,
|
||||
Value & vRes)
|
||||
{
|
||||
auto vLocks = state.ctx.mem.allocValue();
|
||||
auto vRootSrc = state.ctx.mem.allocValue();
|
||||
auto vRootSubdir = state.ctx.mem.allocValue();
|
||||
auto vTmp1 = state.ctx.mem.allocValue();
|
||||
auto vTmp2 = state.ctx.mem.allocValue();
|
||||
Value vLocks;
|
||||
Value vRootSrc;
|
||||
Value vRootSubdir;
|
||||
Value vTmp1;
|
||||
Value vTmp2;
|
||||
|
||||
vLocks->mkString(lockedFlake.lockFile.to_string());
|
||||
vLocks.mkString(lockedFlake.lockFile.to_string());
|
||||
|
||||
emitTreeAttrs(
|
||||
state.ctx,
|
||||
*lockedFlake.flake.sourceInfo,
|
||||
lockedFlake.flake.lockedRef.input,
|
||||
*vRootSrc,
|
||||
vRootSrc,
|
||||
false,
|
||||
lockedFlake.flake.forceDirty);
|
||||
lockedFlake.flake.forceDirty
|
||||
);
|
||||
|
||||
vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir);
|
||||
vRootSubdir.mkString(lockedFlake.flake.lockedRef.subdir);
|
||||
|
||||
if (!state.ctx.caches.vCallFlake) {
|
||||
state.ctx.caches.vCallFlake = allocRootValue(state.ctx.mem.allocValue());
|
||||
state.eval(state.ctx.parseExprFromString(
|
||||
#include "call-flake.nix.gen.hh"
|
||||
, CanonPath::root), **state.ctx.caches.vCallFlake);
|
||||
state.ctx.caches.vCallFlake = allocRootValue({});
|
||||
state.eval(
|
||||
state.ctx.parseExprFromString(
|
||||
#include "call-flake.nix.gen.hh"
|
||||
, CanonPath::root
|
||||
),
|
||||
*state.ctx.caches.vCallFlake
|
||||
);
|
||||
}
|
||||
|
||||
state.callFunction(**state.ctx.caches.vCallFlake, *vLocks, *vTmp1, noPos);
|
||||
state.callFunction(*vTmp1, *vRootSrc, *vTmp2, noPos);
|
||||
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
|
||||
state.callFunction(*state.ctx.caches.vCallFlake, vLocks, vTmp1, noPos);
|
||||
state.callFunction(vTmp1, vRootSrc, vTmp2, noPos);
|
||||
state.callFunction(vTmp2, vRootSubdir, vRes, noPos);
|
||||
}
|
||||
|
||||
void prim_getFlake(EvalState & state, Value * * args, Value & v)
|
||||
@@ -984,10 +1023,10 @@ void prim_flakeRefToString(
|
||||
state.forceAttrs(*args[0], noPos,
|
||||
"while evaluating the argument passed to builtins.flakeRefToString");
|
||||
fetchers::Attrs attrs;
|
||||
for (const auto & attr : *args[0]->attrs) {
|
||||
auto t = attr.value->type();
|
||||
for (const auto & attr : *args[0]->attrs()) {
|
||||
auto t = attr.value.type();
|
||||
if (t == nInt) {
|
||||
auto intValue = attr.value->integer.value;
|
||||
auto intValue = attr.value.integer().value;
|
||||
|
||||
if (intValue < 0) {
|
||||
state.ctx.errors.make<EvalError>("negative value given for flake ref attr %1%: %2%", state.ctx.symbols[attr.name], intValue).debugThrow();
|
||||
@@ -996,17 +1035,18 @@ void prim_flakeRefToString(
|
||||
|
||||
attrs.emplace(state.ctx.symbols[attr.name], asUnsigned);
|
||||
} else if (t == nBool) {
|
||||
attrs.emplace(state.ctx.symbols[attr.name],
|
||||
Explicit<bool> { attr.value->boolean });
|
||||
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool>{attr.value.boolean()});
|
||||
} else if (t == nString) {
|
||||
attrs.emplace(state.ctx.symbols[attr.name],
|
||||
std::string(attr.value->str()));
|
||||
attrs.emplace(state.ctx.symbols[attr.name], std::string(attr.value.str()));
|
||||
} else {
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"flake reference attribute sets may only contain integers, Booleans, "
|
||||
"and strings, but attribute '%s' is %s",
|
||||
state.ctx.symbols[attr.name],
|
||||
showType(*attr.value)).debugThrow();
|
||||
state.ctx.errors
|
||||
.make<EvalError>(
|
||||
"flake reference attribute sets may only contain integers, Booleans, "
|
||||
"and strings, but attribute '%s' is %s",
|
||||
state.ctx.symbols[attr.name],
|
||||
showType(attr.value)
|
||||
)
|
||||
.debugThrow();
|
||||
}
|
||||
}
|
||||
auto flakeRef = FlakeRef::fromAttrs(attrs);
|
||||
|
||||
+19
-15
@@ -95,6 +95,24 @@ inline void * gcAllocBytes(size_t n)
|
||||
return ptr;
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline size_t checkedArrayAllocSize(size_t size, size_t howMany)
|
||||
{
|
||||
// NOTE: size_t * size_t, which can definitely overflow.
|
||||
// Unsigned integer overflow is definitely a bug, but isn't undefined
|
||||
// behavior, so we can just check if we overflowed after the fact.
|
||||
// However, people can and do request zero sized allocations, so we need
|
||||
// to check that neither of our multiplicands were zero before complaining
|
||||
// about it.
|
||||
auto checkedSz = checked::Checked<size_t>(howMany) * size;
|
||||
if (checkedSz.overflowed()) {
|
||||
// Congrats, you done did an overflow.
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
return checkedSz.valueWrapping();
|
||||
}
|
||||
|
||||
/// Typed, safe wrapper around calloc() (transparently GC-enabled). Allocates
|
||||
/// enough for the requested count of the specified type. Also checks for
|
||||
/// nullptr (and throws @ref std::bad_alloc), and casts the void pointer to
|
||||
@@ -103,21 +121,7 @@ template<typename T>
|
||||
[[gnu::always_inline]]
|
||||
inline T * gcAllocType(size_t howMany = 1)
|
||||
{
|
||||
// NOTE: size_t * size_t, which can definitely overflow.
|
||||
// Unsigned integer overflow is definitely a bug, but isn't undefined
|
||||
// behavior, so we can just check if we overflowed after the fact.
|
||||
// However, people can and do request zero sized allocations, so we need
|
||||
// to check that neither of our multiplicands were zero before complaining
|
||||
// about it.
|
||||
// NOLINTNEXTLINE(bugprone-sizeof-expression): yeah we only seem to alloc pointers with this. the calculation *is* correct though!
|
||||
auto checkedSz = checked::Checked<size_t>(howMany) * sizeof(T);
|
||||
size_t sz = checkedSz.valueWrapping();
|
||||
if (checkedSz.overflowed()) {
|
||||
// Congrats, you done did an overflow.
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
return static_cast<T *>(gcAllocBytes(sz));
|
||||
return static_cast<T *>(gcAllocBytes(checkedArrayAllocSize(sizeof(T), howMany)));
|
||||
}
|
||||
|
||||
/// GC-transparently allocates a buffer for a C-string of @ref size *bytes*,
|
||||
|
||||
@@ -17,8 +17,8 @@ using SmallVector = boost::container::small_vector<T, nItems, TraceableAllocator
|
||||
/**
|
||||
* A vector of value pointers. See `SmallVector`.
|
||||
*/
|
||||
template <size_t nItems>
|
||||
using SmallValueVector = SmallVector<Value *, nItems>;
|
||||
template<size_t nItems>
|
||||
using SmallValueVector = SmallVector<Value, nItems>;
|
||||
|
||||
/**
|
||||
* A vector of values that must not be referenced after the vector is destroyed.
|
||||
|
||||
+58
-62
@@ -68,7 +68,9 @@ std::string DrvInfo::queryName(EvalState & state)
|
||||
if (!i) {
|
||||
state.ctx.errors.make<TypeError>("derivation name missing").debugThrow();
|
||||
}
|
||||
name = state.forceStringNoCtx(*i->value, noPos, "while evaluating the 'name' attribute of a derivation");
|
||||
name = state.forceStringNoCtx(
|
||||
i->value, noPos, "while evaluating the 'name' attribute of a derivation"
|
||||
);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
@@ -81,7 +83,7 @@ std::string DrvInfo::querySystem(EvalState & state)
|
||||
system = !i
|
||||
? "unknown"
|
||||
: state.forceStringNoCtx(
|
||||
*i->value, i->pos, "while evaluating the 'system' attribute of a derivation"
|
||||
i->value, i->pos, "while evaluating the 'system' attribute of a derivation"
|
||||
);
|
||||
}
|
||||
return system;
|
||||
@@ -98,7 +100,7 @@ std::optional<StorePath> DrvInfo::queryDrvPath(EvalState & state)
|
||||
} else {
|
||||
drvPath = {state.coerceToStorePath(
|
||||
i->pos,
|
||||
*i->value,
|
||||
i->value,
|
||||
context,
|
||||
"while evaluating the 'drvPath' attribute of a derivation"
|
||||
)};
|
||||
@@ -123,7 +125,7 @@ StorePath DrvInfo::queryOutPath(EvalState & state)
|
||||
NixStringContext context;
|
||||
if (i) {
|
||||
outPath = state.coerceToStorePath(
|
||||
i->pos, *i->value, context, "while evaluating the output path of a derivation"
|
||||
i->pos, i->value, context, "while evaluating the output path of a derivation"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -156,23 +158,17 @@ void DrvInfo::fillOutputs(EvalState & state, bool withPaths)
|
||||
|
||||
// NOTE(Qyriad): I don't think there is any codepath that can cause this to error.
|
||||
state.forceList(
|
||||
*outputs->value,
|
||||
outputs->pos,
|
||||
"while evaluating the 'outputs' attribute of a derivation"
|
||||
outputs->value, outputs->pos, "while evaluating the 'outputs' attribute of a derivation"
|
||||
);
|
||||
|
||||
for (auto [idx, elem] : enumerate(outputs->value->listItems())) {
|
||||
for (auto && [idx, elem] : enumerate(outputs->value.listItems())) {
|
||||
// NOTE(Qyriad): This error should be *extremely* rare in practice.
|
||||
// It is impossible to construct with `stdenv.mkDerivation`,
|
||||
// `builtins.derivation`, or even `derivationStrict`. As far as we can tell,
|
||||
// it is only possible by overriding a derivation attrset already created by
|
||||
// one of those with `//` to introduce the failing `outputs` entry.
|
||||
auto errMsg = fmt("while evaluating output %d of a derivation", idx);
|
||||
std::string_view outputName = state.forceStringNoCtx(
|
||||
*elem,
|
||||
outputs->pos,
|
||||
errMsg
|
||||
);
|
||||
std::string_view outputName = state.forceStringNoCtx(elem, outputs->pos, errMsg);
|
||||
|
||||
if (withPaths) {
|
||||
// Find the attr with this output's name...
|
||||
@@ -184,10 +180,10 @@ void DrvInfo::fillOutputs(EvalState & state, bool withPaths)
|
||||
|
||||
// Meanwhile we couldn't figure out any circumstances
|
||||
// that cause this to error.
|
||||
state.forceAttrs(*out->value, outputs->pos, errMsg);
|
||||
state.forceAttrs(out->value, outputs->pos, errMsg);
|
||||
|
||||
// ...and evaluate its `outPath` attribute.
|
||||
const Attr * outPath = out->value->attrs->get(state.ctx.s.outPath);
|
||||
const Attr * outPath = out->value.attrs()->get(state.ctx.s.outPath);
|
||||
if (outPath == nullptr) {
|
||||
continue;
|
||||
// FIXME: throw error?
|
||||
@@ -196,12 +192,7 @@ void DrvInfo::fillOutputs(EvalState & state, bool withPaths)
|
||||
NixStringContext context;
|
||||
// And idk what could possibly cause this one to error
|
||||
// that wouldn't error before here.
|
||||
auto storePath = state.coerceToStorePath(
|
||||
outPath->pos,
|
||||
*outPath->value,
|
||||
context,
|
||||
errMsg
|
||||
);
|
||||
auto storePath = state.coerceToStorePath(outPath->pos, outPath->value, context, errMsg);
|
||||
this->outputs.emplace(outputName, storePath);
|
||||
} else {
|
||||
this->outputs.emplace(outputName, std::nullopt);
|
||||
@@ -233,7 +224,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(EvalState & state, bool withPaths, bool o
|
||||
// explicitly selected-into output.
|
||||
if (const Attr * outSpecAttr = attrs->get(state.ctx.s.outputSpecified)) {
|
||||
bool outputSpecified = state.forceBool(
|
||||
*outSpecAttr->value,
|
||||
outSpecAttr->value,
|
||||
outSpecAttr->pos,
|
||||
"while evaluating the 'outputSpecified' attribute of a derivation"
|
||||
);
|
||||
@@ -253,15 +244,16 @@ DrvInfo::Outputs DrvInfo::queryOutputs(EvalState & state, bool withPaths, bool o
|
||||
/* ^ this shows during `nix-env -i` right under the bad derivation */
|
||||
if (!outTI->isList()) throw Error(errMsg + "expected a list but got %s", Uncolored(showType(outTI->type())));
|
||||
Outputs result;
|
||||
for (auto elem : outTI->listItems()) {
|
||||
if (elem->type() != nString)
|
||||
for (auto & elem : outTI->listItems()) {
|
||||
if (elem.type() != nString) {
|
||||
throw Error(
|
||||
errMsg + "element is %s where a string was expected",
|
||||
Uncolored(showType(elem->type()))
|
||||
Uncolored(showType(elem.type()))
|
||||
);
|
||||
auto out = outputs.find(std::string(elem->str()));
|
||||
}
|
||||
auto out = outputs.find(std::string(elem.str()));
|
||||
if (out == outputs.end()) {
|
||||
throw Error(errMsg + "output '%s' does not exist", elem->str());
|
||||
throw Error(errMsg + "output '%s' does not exist", elem.str());
|
||||
}
|
||||
result.insert(*out);
|
||||
}
|
||||
@@ -274,7 +266,7 @@ std::string DrvInfo::queryOutputName(EvalState & state)
|
||||
if (outputName == "" && attrs) {
|
||||
auto i = attrs->get(state.ctx.s.outputName);
|
||||
outputName = i ? state.forceStringNoCtx(
|
||||
*i->value, noPos, "while evaluating the output name of a derivation"
|
||||
i->value, noPos, "while evaluating the output name of a derivation"
|
||||
)
|
||||
: "";
|
||||
}
|
||||
@@ -290,8 +282,8 @@ Bindings * DrvInfo::getMeta(EvalState & state)
|
||||
if (!a) {
|
||||
return 0;
|
||||
}
|
||||
state.forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
|
||||
meta = a->value->attrs;
|
||||
state.forceAttrs(a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
|
||||
meta = a->value.attrs();
|
||||
return meta;
|
||||
}
|
||||
|
||||
@@ -310,17 +302,23 @@ bool DrvInfo::checkMeta(EvalState & state, Value & v)
|
||||
{
|
||||
state.forceValue(v, noPos);
|
||||
if (v.type() == nList) {
|
||||
for (auto elem : v.listItems())
|
||||
if (!checkMeta(state, *elem)) return false;
|
||||
for (auto & elem : v.listItems()) {
|
||||
if (!checkMeta(state, elem)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (v.type() == nAttrs) {
|
||||
auto i = v.attrs->get(state.ctx.s.outPath);
|
||||
auto i = v.attrs()->get(state.ctx.s.outPath);
|
||||
if (i) {
|
||||
return false;
|
||||
}
|
||||
for (auto & i : *v.attrs)
|
||||
if (!checkMeta(state, *i.value)) return false;
|
||||
for (auto & i : *v.attrs()) {
|
||||
if (!checkMeta(state, i.value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else return v.type() == nInt || v.type() == nBool || v.type() == nString ||
|
||||
@@ -332,10 +330,10 @@ Value * DrvInfo::queryMeta(EvalState & state, const std::string & name)
|
||||
{
|
||||
if (!getMeta(state)) return 0;
|
||||
auto a = meta->get(state.ctx.symbols.create(name));
|
||||
if (!a || !checkMeta(state, *a->value)) {
|
||||
if (!a || !checkMeta(state, a->value)) {
|
||||
return 0;
|
||||
}
|
||||
return a->value;
|
||||
return &a->value;
|
||||
}
|
||||
|
||||
|
||||
@@ -351,7 +349,9 @@ NixInt DrvInfo::queryMetaInt(EvalState & state, const std::string & name, NixInt
|
||||
{
|
||||
Value * v = queryMeta(state, name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nInt) return v->integer;
|
||||
if (v->type() == nInt) {
|
||||
return v->integer();
|
||||
}
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
integer meta fields. */
|
||||
@@ -366,7 +366,9 @@ bool DrvInfo::queryMetaBool(EvalState & state, const std::string & name, bool de
|
||||
{
|
||||
Value * v = queryMeta(state, name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nBool) return v->boolean;
|
||||
if (v->type() == nBool) {
|
||||
return v->boolean();
|
||||
}
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
Boolean meta fields. */
|
||||
@@ -380,8 +382,7 @@ bool DrvInfo::queryMetaBool(EvalState & state, const std::string & name, bool de
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
void DrvInfo::setMeta(EvalState & state, const std::string & name, Value * v)
|
||||
void DrvInfo::setMeta(EvalState & state, const std::string & name, Value & v)
|
||||
{
|
||||
getMeta(state);
|
||||
auto attrs = state.ctx.buildBindings(1 + (meta ? meta->size() : 0));
|
||||
@@ -390,7 +391,7 @@ void DrvInfo::setMeta(EvalState & state, const std::string & name, Value * v)
|
||||
for (auto i : *meta)
|
||||
if (i.name != sym)
|
||||
attrs.insert(i);
|
||||
if (v) attrs.insert(sym, v);
|
||||
attrs.insert(sym, v);
|
||||
meta = attrs.finish();
|
||||
}
|
||||
|
||||
@@ -409,7 +410,7 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||
state.forceValue(v, noPos);
|
||||
if (!state.isDerivation(v)) return true;
|
||||
|
||||
DrvInfo drv(attrPath, v.attrs);
|
||||
DrvInfo drv(attrPath, v.attrs());
|
||||
|
||||
drv.queryName(state);
|
||||
|
||||
@@ -462,19 +463,13 @@ static void getDerivations(EvalState & state, Value & vIn, PosIdx pos,
|
||||
if (v.type() == nList) {
|
||||
// NOTE we can't really deduplicate here because small lists don't have stable addresses
|
||||
// and can cause spurious duplicate detections due to v being on the stack.
|
||||
for (auto [n, elem] : enumerate(v.listItems())) {
|
||||
for (auto && [n, elem] : enumerate(v.listItems())) {
|
||||
std::string joinedAttrPath = addToPath(pathPrefix, fmt("%d", n));
|
||||
bool shouldRecurse = getDerivation(state, *elem, joinedAttrPath, drvs, ignoreAssertionFailures);
|
||||
bool shouldRecurse =
|
||||
getDerivation(state, elem, joinedAttrPath, drvs, ignoreAssertionFailures);
|
||||
if (shouldRecurse) {
|
||||
getDerivations(
|
||||
state,
|
||||
*elem,
|
||||
pos,
|
||||
joinedAttrPath,
|
||||
autoArgs,
|
||||
drvs,
|
||||
done,
|
||||
ignoreAssertionFailures
|
||||
state, elem, pos, joinedAttrPath, autoArgs, drvs, done, ignoreAssertionFailures
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -489,7 +484,7 @@ static void getDerivations(EvalState & state, Value & vIn, PosIdx pos,
|
||||
|
||||
/* Dont consider sets we've already seen, e.g. y in
|
||||
`rec { x.d = derivation {...}; y = x; }`. */
|
||||
auto const &[_, didInsert] = done.insert(v.attrs);
|
||||
auto const &[_, didInsert] = done.insert(v.attrs());
|
||||
if (!didInsert) {
|
||||
return;
|
||||
}
|
||||
@@ -497,14 +492,14 @@ static void getDerivations(EvalState & state, Value & vIn, PosIdx pos,
|
||||
// FIXME: what the fuck???
|
||||
/* !!! undocumented hackery to support combining channels in
|
||||
nix-env.cc. */
|
||||
bool combineChannels = v.attrs->get(state.ctx.symbols.create("_combineChannels"));
|
||||
bool combineChannels = v.attrs()->get(state.ctx.symbols.create("_combineChannels"));
|
||||
|
||||
/* Consider the attributes in sorted order to get more
|
||||
deterministic behaviour in nix-env operations (e.g. when
|
||||
there are names clashes between derivations, the derivation
|
||||
bound to the attribute with the "lower" name should take
|
||||
precedence). */
|
||||
for (auto & attr : v.attrs->lexicographicOrder(state.ctx.symbols)) {
|
||||
for (auto & attr : v.attrs()->lexicographicOrder(state.ctx.symbols)) {
|
||||
debug("evaluating attribute '%1%'", state.ctx.symbols[attr->name]);
|
||||
// FIXME: only consider attrs with identifier-like names?? Why???
|
||||
if (!std::regex_match(std::string(state.ctx.symbols[attr->name]), attrRegex)) {
|
||||
@@ -514,7 +509,7 @@ static void getDerivations(EvalState & state, Value & vIn, PosIdx pos,
|
||||
if (combineChannels) {
|
||||
getDerivations(
|
||||
state,
|
||||
*attr->value,
|
||||
attr->value,
|
||||
attr->pos,
|
||||
joinedAttrPath,
|
||||
autoArgs,
|
||||
@@ -522,18 +517,19 @@ static void getDerivations(EvalState & state, Value & vIn, PosIdx pos,
|
||||
done,
|
||||
ignoreAssertionFailures
|
||||
);
|
||||
} else if (getDerivation(state, *attr->value, joinedAttrPath, drvs, ignoreAssertionFailures)) {
|
||||
} else if (getDerivation(state, attr->value, joinedAttrPath, drvs, ignoreAssertionFailures))
|
||||
{
|
||||
/* If the value of this attribute is itself a set,
|
||||
should we recurse into it? => Only if it has a
|
||||
`recurseForDerivations = true' attribute. */
|
||||
if (attr->value->type() == nAttrs) {
|
||||
if (attr->value.type() == nAttrs) {
|
||||
const Attr * recurseForDrvs =
|
||||
attr->value->attrs->get(state.ctx.s.recurseForDerivations);
|
||||
attr->value.attrs()->get(state.ctx.s.recurseForDerivations);
|
||||
if (recurseForDrvs == nullptr) {
|
||||
continue;
|
||||
}
|
||||
bool shouldRecurse = state.forceBool(
|
||||
*recurseForDrvs->value,
|
||||
recurseForDrvs->value,
|
||||
attr->pos,
|
||||
fmt("while evaluating the '%s' attribute", Magenta("recurseForDerivations"))
|
||||
);
|
||||
@@ -543,7 +539,7 @@ static void getDerivations(EvalState & state, Value & vIn, PosIdx pos,
|
||||
|
||||
getDerivations(
|
||||
state,
|
||||
*attr->value,
|
||||
attr->value,
|
||||
attr->pos,
|
||||
joinedAttrPath,
|
||||
autoArgs,
|
||||
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
std::string queryMetaString(EvalState & state, const std::string & name);
|
||||
NixInt queryMetaInt(EvalState & state, const std::string & name, NixInt def);
|
||||
bool queryMetaBool(EvalState & state, const std::string & name, bool def);
|
||||
void setMeta(EvalState & state, const std::string & name, Value * v);
|
||||
void setMeta(EvalState & state, const std::string & name, Value & v);
|
||||
|
||||
/*
|
||||
MetaInfo queryMetaInfo(EvalState & state) const;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "lix/libexpr/json-to-value.hh"
|
||||
#include "gc-alloc.hh"
|
||||
#include "lix/libexpr/value.hh"
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libutil/json.hh"
|
||||
@@ -7,11 +8,6 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
/*
|
||||
* Used for `JSONObjectState`
|
||||
*/
|
||||
using ValueMap = GcMap<Symbol, Value *>;
|
||||
|
||||
// for more information, refer to
|
||||
// https://github.com/nlohmann/json/blob/master/include/nlohmann/detail/input/json_sax.hpp
|
||||
class JSONSax : nlohmann::json_sax<JSON> {
|
||||
@@ -25,13 +21,14 @@ class JSONSax : nlohmann::json_sax<JSON> {
|
||||
assert(false && "tried to close toplevel json parser state");
|
||||
}
|
||||
explicit JSONState(std::unique_ptr<JSONState> && p) : parent(std::move(p)) {}
|
||||
explicit JSONState(Value * v) : v(allocRootValue(v)) {}
|
||||
JSONState() = default;
|
||||
JSONState(JSONState & p) = delete;
|
||||
Value & value(EvalState & state)
|
||||
Value & value()
|
||||
{
|
||||
if (!v)
|
||||
v = allocRootValue(state.ctx.mem.allocValue());
|
||||
return **v;
|
||||
if (!v) {
|
||||
v = allocRootValue({});
|
||||
}
|
||||
return *v;
|
||||
}
|
||||
virtual ~JSONState() {}
|
||||
virtual void add() {}
|
||||
@@ -39,35 +36,41 @@ class JSONSax : nlohmann::json_sax<JSON> {
|
||||
|
||||
class JSONObjectState : public JSONState {
|
||||
using JSONState::JSONState;
|
||||
ValueMap attrs;
|
||||
GcMap<Symbol, Value> attrs;
|
||||
Symbol _key;
|
||||
std::unique_ptr<JSONState> resolve(EvalState & state) override
|
||||
{
|
||||
auto attrs2 = state.ctx.buildBindings(attrs.size());
|
||||
for (auto & i : attrs)
|
||||
attrs2.insert(i.first, i.second);
|
||||
parent->value(state).mkAttrs(attrs2.alreadySorted());
|
||||
parent->value().mkAttrs(attrs2.alreadySorted());
|
||||
return std::move(parent);
|
||||
}
|
||||
void add() override { v = nullptr; }
|
||||
void add() override
|
||||
{
|
||||
attrs.insert_or_assign(_key, value());
|
||||
v = nullptr;
|
||||
}
|
||||
public:
|
||||
void key(string_t & name, EvalState & state)
|
||||
{
|
||||
attrs.insert_or_assign(state.ctx.symbols.create(name), &value(state));
|
||||
_key = state.ctx.symbols.create(name);
|
||||
}
|
||||
};
|
||||
|
||||
class JSONListState : public JSONState {
|
||||
ValueVector values;
|
||||
GcVector<Value> values;
|
||||
std::unique_ptr<JSONState> resolve(EvalState & state) override
|
||||
{
|
||||
Value & v = parent->value(state);
|
||||
v = state.ctx.mem.newList(values.size());
|
||||
auto list = state.ctx.mem.newList(values.size());
|
||||
parent->value() = {NewValueAs::list, list};
|
||||
for (size_t n = 0; n < values.size(); ++n) {
|
||||
v.listElems()[n] = values[n];
|
||||
list->elems[n] = values[n];
|
||||
}
|
||||
return std::move(parent);
|
||||
}
|
||||
void add() override {
|
||||
void add() override
|
||||
{
|
||||
values.push_back(*v);
|
||||
v = nullptr;
|
||||
}
|
||||
@@ -82,25 +85,30 @@ class JSONSax : nlohmann::json_sax<JSON> {
|
||||
std::unique_ptr<JSONState> rs;
|
||||
|
||||
public:
|
||||
JSONSax(EvalState & state, Value & v) : state(state), rs(new JSONState(&v)) {};
|
||||
JSONSax(EvalState & state) : state(state), rs(new JSONState()) {};
|
||||
|
||||
Value result()
|
||||
{
|
||||
return rs->value();
|
||||
}
|
||||
|
||||
bool null() override
|
||||
{
|
||||
rs->value(state).mkNull();
|
||||
rs->value().mkNull();
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool boolean(bool val) override
|
||||
{
|
||||
rs->value(state).mkBool(val);
|
||||
rs->value().mkBool(val);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_integer(number_integer_t val) override
|
||||
{
|
||||
rs->value(state).mkInt(val);
|
||||
rs->value().mkInt(val);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
@@ -113,21 +121,21 @@ public:
|
||||
return number_float(static_cast<number_float_t>(val_), "");
|
||||
}
|
||||
NixInt::Inner val = val_;
|
||||
rs->value(state).mkInt(val);
|
||||
rs->value().mkInt(val);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_float(number_float_t val, const string_t & s) override
|
||||
{
|
||||
rs->value(state).mkFloat(val);
|
||||
rs->value().mkFloat(val);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string(string_t & val) override
|
||||
{
|
||||
rs->value(state).mkString(val);
|
||||
rs->value().mkString(val);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
@@ -178,10 +186,11 @@ public:
|
||||
|
||||
void parseJSON(EvalState & state, const std::string_view & s_, Value & v)
|
||||
{
|
||||
JSONSax parser(state, v);
|
||||
JSONSax parser(state);
|
||||
bool res = JSON::sax_parse(s_, &parser);
|
||||
if (!res)
|
||||
throw JSONParseError("Invalid JSON Value");
|
||||
v = parser.result();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+79
-3
@@ -9,7 +9,8 @@
|
||||
namespace nix {
|
||||
|
||||
ExprBlackHole eBlackHole;
|
||||
Expr *eBlackHoleAddr = &eBlackHole;
|
||||
|
||||
Value::Thunk Value::blackHole{{0}, &eBlackHole};
|
||||
|
||||
// FIXME: remove, because *symbols* are abstract and do not have a single
|
||||
// textual representation; see printIdentifier()
|
||||
@@ -47,11 +48,11 @@ JSON ExprLiteral::toJSON(const SymbolTable & symbols) const
|
||||
switch (v.type()) {
|
||||
case nInt:
|
||||
valueType = "Int";
|
||||
value = v.integer.value;
|
||||
value = v.integer().value;
|
||||
break;
|
||||
case nFloat:
|
||||
valueType = "Float";
|
||||
value = v.fpoint;
|
||||
value = v.fpoint();
|
||||
break;
|
||||
case nString:
|
||||
valueType = "String";
|
||||
@@ -320,6 +321,74 @@ JSON printAttrPathToJson(const SymbolTable & symbols, const AttrPath & attrPath)
|
||||
/* Computing levels/displacements for variables. */
|
||||
|
||||
namespace {
|
||||
// This is a one-pass static analyzer for
|
||||
// various topics.
|
||||
struct StaticAnalyzer : ExprVisitor
|
||||
{
|
||||
std::set<Symbol> staticallyUsedVariables;
|
||||
bool usedDynamicVariables = false;
|
||||
|
||||
StaticAnalyzer() {}
|
||||
using ExprVisitor::visit;
|
||||
|
||||
void visit(ExprDebugFrame & e, std::unique_ptr<Expr> & ptr) override
|
||||
{
|
||||
visit(e.inner);
|
||||
}
|
||||
void visit(ExprLiteral & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
void visit(ExprVar & e, std::unique_ptr<Expr> & ptr) override
|
||||
{
|
||||
staticallyUsedVariables.insert(e.name);
|
||||
}
|
||||
void visit(ExprInheritFrom & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
void visit(ExprSelect & e, std::unique_ptr<Expr> & ptr) override
|
||||
{
|
||||
if (e.isDynamic()) {
|
||||
usedDynamicVariables = true;
|
||||
}
|
||||
|
||||
visit(e.def);
|
||||
visit(e.e);
|
||||
}
|
||||
void visit(ExprOpHasAttr & e, std::unique_ptr<Expr> & ptr) override
|
||||
{
|
||||
if (e.isDynamic()) {
|
||||
usedDynamicVariables = true;
|
||||
}
|
||||
|
||||
visit(e.e);
|
||||
}
|
||||
void visit(ExprSet & e, std::unique_ptr<Expr> & ptr) override
|
||||
{
|
||||
// TODO: oh bro, we need to analyze dynamic attributes for their value expressions.
|
||||
}
|
||||
void visit(ExprList & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
void visit(ExprLambda & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
void visit(ExprCall & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
void visit(ExprLet & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
void visit(ExprWith & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
void visit(ExprIf & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
void visit(ExprAssert & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
void visit(ExprOpNot & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
#define BINOP(type) \
|
||||
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
|
||||
void visit(type & e, std::unique_ptr<Expr> & ptr) override \
|
||||
{ \
|
||||
visit(e.e1); \
|
||||
visit(e.e2); \
|
||||
}
|
||||
BINOP(ExprOpEq)
|
||||
BINOP(ExprOpNEq)
|
||||
BINOP(ExprOpAnd)
|
||||
BINOP(ExprOpOr)
|
||||
BINOP(ExprOpImpl)
|
||||
BINOP(ExprOpUpdate)
|
||||
BINOP(ExprOpConcatLists)
|
||||
#undef BINOP
|
||||
void visit(ExprConcatStrings & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
void visit(ExprPos & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
void visit(ExprBlackHole & e, std::unique_ptr<Expr> & ptr) override {}
|
||||
};
|
||||
struct VarBinder : ExprVisitor
|
||||
{
|
||||
Evaluator & es;
|
||||
@@ -589,6 +658,13 @@ void VarBinder::visit(ExprLambda & e, std::unique_ptr<Expr> & ptr)
|
||||
{
|
||||
withEnv(e.pattern->buildEnv(env.get()), [&] {
|
||||
e.pattern->accept(*this);
|
||||
/* TODO: If statically, e.body makes only use of some parameters and not the whole scope.
|
||||
* We shouldn't have to keep around all the environment data which might contain trapped
|
||||
* pointers. Analyze `e.body` and return its statically known set of used variables.
|
||||
* */
|
||||
DirectCallAnalyzer analyzer{es, env};
|
||||
analyzer.visit(e.body);
|
||||
e.shortcut = analyzer.shortcut;
|
||||
visit(e.body);
|
||||
});
|
||||
}
|
||||
|
||||
+69
-13
@@ -127,7 +127,7 @@ public:
|
||||
virtual JSON toJSON(const SymbolTable & symbols) const;
|
||||
virtual void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) = 0;
|
||||
virtual void eval(EvalState & state, Env & env, Value & v);
|
||||
virtual Value * maybeThunk(EvalState & state, Env & env);
|
||||
virtual Value maybeThunk(EvalState & state, Env & env);
|
||||
virtual void setName(Symbol name);
|
||||
PosIdx getPos() const { return pos; }
|
||||
|
||||
@@ -175,26 +175,51 @@ protected:
|
||||
Value v;
|
||||
ExprLiteral(const PosIdx pos) : Expr(pos) {};
|
||||
public:
|
||||
|
||||
ExprLiteral(const PosIdx pos, NewValueAs::integer_t, NixInt n) : Expr(pos) { v.mkInt(n); };
|
||||
ExprLiteral(const PosIdx pos, NewValueAs::integer_t, NixInt::Inner n) : Expr(pos) { v.mkInt(n); };
|
||||
ExprLiteral(const PosIdx pos, NewValueAs::floating_t, NixFloat nf) : Expr(pos) { v.mkFloat(nf); };
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
Value maybeThunk(EvalState & state, Env & env) override;
|
||||
JSON toJSON(const SymbolTable & symbols) const override;
|
||||
void eval(EvalState & state, Env & env, Value & v) override;
|
||||
void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) override { ev.visit(*this, ptr); }
|
||||
};
|
||||
|
||||
struct ExprInt : ExprLiteral
|
||||
{
|
||||
Value::Int i;
|
||||
ExprInt(const PosIdx pos, NixInt n) : ExprLiteral(pos), i{{Value::Acb::tInt}, n}
|
||||
{
|
||||
v = Value::isTaggableInteger(n) ? Value{NewValueAs::integer, n} : Value(i);
|
||||
}
|
||||
ExprInt(const PosIdx pos, NixInt::Inner n) : ExprInt(pos, NixInt(n)) {}
|
||||
};
|
||||
|
||||
struct ExprFloat : ExprLiteral
|
||||
{
|
||||
Value::Float f;
|
||||
ExprFloat(const PosIdx pos, NewValueAs::floating_t, double f)
|
||||
: ExprLiteral(pos)
|
||||
, f{{Value::Acb::tFloat}, f}
|
||||
{
|
||||
v = Value(this->f);
|
||||
}
|
||||
};
|
||||
|
||||
struct ExprString : ExprLiteral
|
||||
{
|
||||
std::string s;
|
||||
ExprString(const PosIdx pos, std::string &&s) : ExprLiteral(pos), s(std::move(s)) { v.mkString(this->s.data()); };
|
||||
Value::String strcb{.content = s.c_str(), .context = nullptr};
|
||||
ExprString(const PosIdx pos, std::string && s) : ExprLiteral(pos), s(std::move(s))
|
||||
{
|
||||
v = {NewValueAs::string, &strcb};
|
||||
}
|
||||
};
|
||||
|
||||
struct ExprPath : ExprLiteral
|
||||
{
|
||||
std::string s;
|
||||
ExprPath(const PosIdx pos, std::string s) : ExprLiteral(pos), s(std::move(s)) { v.mkPath(this->s.c_str()); };
|
||||
Value::String strcb{.content = s.c_str(), .context = Value::String::path};
|
||||
ExprPath(const PosIdx pos, std::string s) : ExprLiteral(pos), s(std::move(s))
|
||||
{
|
||||
v = {NewValueAs::path, &strcb};
|
||||
}
|
||||
};
|
||||
|
||||
typedef uint32_t Level;
|
||||
@@ -228,7 +253,7 @@ struct ExprVar : Expr
|
||||
|
||||
ExprVar(Symbol name) : name(name), needsRoot(false) { };
|
||||
ExprVar(const PosIdx & pos, Symbol name, bool needsRoot = false) : Expr(pos), name(name), needsRoot(needsRoot) { };
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
Value maybeThunk(EvalState & state, Env & env) override;
|
||||
JSON toJSON(const SymbolTable & symbols) const override;
|
||||
void eval(EvalState & state, Env & env, Value & v) override;
|
||||
void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) override { ev.visit(*this, ptr); }
|
||||
@@ -267,6 +292,17 @@ struct ExprSelect : Expr
|
||||
/** The path of attributes being selected. e.g. `bar.baz` in `foo.bar.baz.` */
|
||||
AttrPath attrPath;
|
||||
|
||||
bool isDynamic() const
|
||||
{
|
||||
for (auto & name : attrPath) {
|
||||
if (name.expr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, AttrPath attrPath, std::unique_ptr<Expr> def) : Expr(pos), e(std::move(e)), def(std::move(def)), attrPath(std::move(attrPath)) { };
|
||||
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, const PosIdx namePos, Symbol name) : Expr(pos), e(std::move(e)) { attrPath.push_back(AttrName(namePos, name)); };
|
||||
JSON toJSON(const SymbolTable & symbols) const override;
|
||||
@@ -278,6 +314,16 @@ struct ExprOpHasAttr : Expr
|
||||
{
|
||||
std::unique_ptr<Expr> e;
|
||||
AttrPath attrPath;
|
||||
|
||||
bool isDynamic() const
|
||||
{
|
||||
for (auto & name : attrPath) {
|
||||
if (name.expr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
ExprOpHasAttr(const PosIdx & pos, std::unique_ptr<Expr> e, AttrPath attrPath) : Expr(pos), e(std::move(e)), attrPath(std::move(attrPath)) { };
|
||||
JSON toJSON(const SymbolTable & symbols) const override;
|
||||
void eval(EvalState & state, Env & env, Value & v) override;
|
||||
@@ -370,7 +416,7 @@ struct ExprList : Expr
|
||||
JSON toJSON(const SymbolTable & symbols) const override;
|
||||
void eval(EvalState & state, Env & env, Value & v) override;
|
||||
void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) override { ev.visit(*this, ptr); }
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
Value maybeThunk(EvalState & state, Env & env) override;
|
||||
};
|
||||
|
||||
struct Pattern {
|
||||
@@ -384,7 +430,8 @@ struct Pattern {
|
||||
|
||||
virtual std::shared_ptr<const StaticEnv> buildEnv(const StaticEnv * up) = 0;
|
||||
virtual void accept(ExprVisitor & ev) = 0;
|
||||
virtual Env & match(ExprLambda & lambda, EvalState & state, Env & up, Value * arg, const PosIdx pos) = 0;
|
||||
virtual Env &
|
||||
match(ExprLambda & lambda, EvalState & state, Env & up, Value & arg, const PosIdx pos) = 0;
|
||||
|
||||
virtual void addBindingsToJSON(JSON & out, const SymbolTable & symbols) const = 0;
|
||||
};
|
||||
@@ -399,7 +446,8 @@ struct SimplePattern : Pattern
|
||||
|
||||
virtual std::shared_ptr<const StaticEnv> buildEnv(const StaticEnv * up) override;
|
||||
virtual void accept(ExprVisitor & ev) override;
|
||||
virtual Env & match(ExprLambda & lambda, EvalState & state, Env & up, Value * arg, const PosIdx pos) override;
|
||||
virtual Env &
|
||||
match(ExprLambda & lambda, EvalState & state, Env & up, Value & arg, const PosIdx pos) override;
|
||||
|
||||
virtual void addBindingsToJSON(JSON & out, const SymbolTable & symbols) const override;
|
||||
};
|
||||
@@ -420,7 +468,8 @@ struct AttrsPattern : Pattern
|
||||
|
||||
virtual std::shared_ptr<const StaticEnv> buildEnv(const StaticEnv * up) override;
|
||||
virtual void accept(ExprVisitor & ev) override;
|
||||
virtual Env & match(ExprLambda & lambda, EvalState & state, Env & up, Value * arg, const PosIdx pos) override;
|
||||
virtual Env &
|
||||
match(ExprLambda & lambda, EvalState & state, Env & up, Value & arg, const PosIdx pos) override;
|
||||
|
||||
virtual void addBindingsToJSON(JSON & out, const SymbolTable & symbols) const override;
|
||||
|
||||
@@ -451,6 +500,13 @@ struct ExprLambda : Expr
|
||||
Symbol name;
|
||||
std::unique_ptr<Pattern> pattern;
|
||||
std::unique_ptr<Expr> body;
|
||||
// This is a shortcut variant which
|
||||
// exhausts the body further lambda constructions
|
||||
// to transform x1: x2: …: xn: b
|
||||
// into { x1, …, xn }: b
|
||||
// This can be used when you know that you are
|
||||
// passing all the arguments at once.
|
||||
std::unique_ptr<ExprLambda> shortcut;
|
||||
ExprLambda(PosIdx pos, std::unique_ptr<Pattern> pattern, std::unique_ptr<Expr> body)
|
||||
: Expr(pos), pattern(std::move(pattern)), body(std::move(body))
|
||||
{
|
||||
|
||||
@@ -148,7 +148,7 @@ struct ExprState
|
||||
std::unique_ptr<Expr> negate(PosIdx pos, State & state)
|
||||
{
|
||||
std::vector<std::unique_ptr<Expr>> args(2);
|
||||
args[0] = std::make_unique<ExprLiteral>(pos, NewValueAs::integer, 0);
|
||||
args[0] = std::make_unique<ExprInt>(pos, 0);
|
||||
args[1] = popExprOnly();
|
||||
return std::make_unique<ExprCall>(pos, state.mkInternalVar(pos, state.s.sub), std::move(args));
|
||||
}
|
||||
@@ -507,7 +507,7 @@ template<> struct BuildAST<grammar::v1::expr::int_> {
|
||||
.pos = ps.positions[ps.at(in)],
|
||||
});
|
||||
}
|
||||
s.emplaceExpr<ExprLiteral>(ps.at(in), NewValueAs::integer, v);
|
||||
s.emplaceExpr<ExprInt>(ps.at(in), v);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -542,7 +542,7 @@ template<> struct BuildAST<grammar::v1::expr::float_> {
|
||||
});
|
||||
}
|
||||
}();
|
||||
s.emplaceExpr<ExprLiteral>(ps.at(in), NewValueAs::floating, v);
|
||||
s.emplaceExpr<ExprFloat>(ps.at(in), NewValueAs::floating, v);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+500
-315
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,7 @@ struct RegisterPrimOp
|
||||
* will get called during EvalState initialization, so there
|
||||
* may be primops not yet added and builtins is not yet sorted.
|
||||
*/
|
||||
RegisterPrimOp(PrimOp && primOp);
|
||||
RegisterPrimOp(PrimOpDetails && primOp);
|
||||
};
|
||||
|
||||
/* These primops are disabled without enableNativeCode, but plugins
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "lix/libstore/derivations.hh"
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include "value.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -147,9 +148,10 @@ void prim_getContext(EvalState & state, Value * * args, Value & v)
|
||||
infoAttrs.alloc(sAllOutputs).mkBool(true);
|
||||
if (!info.second.outputs.empty()) {
|
||||
auto & outputsVal = infoAttrs.alloc(state.ctx.s.outputs);
|
||||
outputsVal = state.ctx.mem.newList(info.second.outputs.size());
|
||||
auto content = state.ctx.mem.newList(info.second.outputs.size());
|
||||
outputsVal = {NewValueAs::list, content};
|
||||
for (const auto & [i, output] : enumerate(info.second.outputs))
|
||||
(outputsVal.listElems()[i] = state.ctx.mem.allocValue())->mkString(output);
|
||||
content->elems[i].mkString(output);
|
||||
}
|
||||
attrs.alloc(state.ctx.store->printStorePath(info.first)).mkAttrs(infoAttrs);
|
||||
}
|
||||
@@ -171,7 +173,7 @@ static void prim_appendContext(EvalState & state, Value * * args, Value & v)
|
||||
state.forceAttrs(*args[1], noPos, "while evaluating the second argument passed to builtins.appendContext");
|
||||
|
||||
auto sAllOutputs = state.ctx.symbols.create("allOutputs");
|
||||
for (auto & i : *args[1]->attrs) {
|
||||
for (auto & i : *args[1]->attrs()) {
|
||||
const auto & name = state.ctx.symbols[i.name];
|
||||
if (!state.ctx.store->isStorePath(name))
|
||||
state.ctx.errors.make<EvalError>(
|
||||
@@ -181,11 +183,11 @@ static void prim_appendContext(EvalState & state, Value * * args, Value & v)
|
||||
auto namePath = state.ctx.store->parseStorePath(name);
|
||||
if (!settings.readOnlyMode)
|
||||
state.aio.blockOn(state.ctx.store->ensurePath(namePath));
|
||||
state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context");
|
||||
auto a = i.value->attrs->get(state.ctx.s.path);
|
||||
state.forceAttrs(i.value, i.pos, "while evaluating the value of a string context");
|
||||
auto a = i.value.attrs()->get(state.ctx.s.path);
|
||||
if (a) {
|
||||
if (state.forceBool(
|
||||
*a->value, a->pos, "while evaluating the `path` attribute of a string context"
|
||||
a->value, a->pos, "while evaluating the `path` attribute of a string context"
|
||||
))
|
||||
{
|
||||
context.emplace(NixStringContextElem::Opaque{
|
||||
@@ -194,10 +196,10 @@ static void prim_appendContext(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
}
|
||||
|
||||
a = i.value->attrs->get(sAllOutputs);
|
||||
a = i.value.attrs()->get(sAllOutputs);
|
||||
if (a) {
|
||||
if (state.forceBool(
|
||||
*a->value,
|
||||
a->value,
|
||||
a->pos,
|
||||
"while evaluating the `allOutputs` attribute of a string context"
|
||||
))
|
||||
@@ -214,20 +216,20 @@ static void prim_appendContext(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
}
|
||||
|
||||
a = i.value->attrs->get(state.ctx.s.outputs);
|
||||
a = i.value.attrs()->get(state.ctx.s.outputs);
|
||||
if (a) {
|
||||
state.forceList(
|
||||
*a->value, a->pos, "while evaluating the `outputs` attribute of a string context"
|
||||
a->value, a->pos, "while evaluating the `outputs` attribute of a string context"
|
||||
);
|
||||
if (a->value->listSize() && !isDerivation(name)) {
|
||||
if (a->value.listSize() && !isDerivation(name)) {
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"tried to add derivation output context of %s, which is not a derivation, to a string",
|
||||
name
|
||||
).atPos(i.pos).debugThrow();
|
||||
}
|
||||
for (auto elem : a->value->listItems()) {
|
||||
for (auto & elem : a->value.listItems()) {
|
||||
auto outputName = state.forceStringNoCtx(
|
||||
*elem, a->pos, "while evaluating an output name within a string context"
|
||||
elem, a->pos, "while evaluating an output name within a string context"
|
||||
);
|
||||
context.emplace(NixStringContextElem::Built {
|
||||
.drvPath = makeConstantStorePath(namePath),
|
||||
|
||||
@@ -118,7 +118,7 @@ void prim_fetchClosure(EvalState & state, Value * * args, Value & v)
|
||||
std::optional<StorePathOrGap> toPath;
|
||||
std::optional<bool> inputAddressedMaybe;
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
for (auto & attr : *args[0]->attrs()) {
|
||||
const auto & attrName = state.ctx.symbols[attr.name];
|
||||
auto attrHint = [&]() -> std::string {
|
||||
return "while evaluating the '" + attrName + "' attribute passed to builtins.fetchClosure";
|
||||
@@ -126,27 +126,26 @@ void prim_fetchClosure(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
if (attrName == "fromPath") {
|
||||
NixStringContext context;
|
||||
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context, attrHint());
|
||||
fromPath = state.coerceToStorePath(attr.pos, attr.value, context, attrHint());
|
||||
}
|
||||
|
||||
else if (attrName == "toPath") {
|
||||
state.forceValue(*attr.value, attr.pos);
|
||||
bool isEmptyString = attr.value->type() == nString && attr.value->str().empty();
|
||||
state.forceValue(attr.value, attr.pos);
|
||||
bool isEmptyString = attr.value.type() == nString && attr.value.str().empty();
|
||||
if (isEmptyString) {
|
||||
toPath = StorePathOrGap {};
|
||||
}
|
||||
else {
|
||||
NixStringContext context;
|
||||
toPath = state.coerceToStorePath(attr.pos, *attr.value, context, attrHint());
|
||||
toPath = state.coerceToStorePath(attr.pos, attr.value, context, attrHint());
|
||||
}
|
||||
}
|
||||
|
||||
else if (attrName == "fromStore")
|
||||
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos,
|
||||
attrHint());
|
||||
fromStoreUrl = state.forceStringNoCtx(attr.value, attr.pos, attrHint());
|
||||
|
||||
else if (attrName == "inputAddressed")
|
||||
inputAddressedMaybe = state.forceBool(*attr.value, attr.pos, attrHint());
|
||||
inputAddressedMaybe = state.forceBool(attr.value, attr.pos, attrHint());
|
||||
|
||||
else
|
||||
throw Error({
|
||||
|
||||
@@ -17,25 +17,48 @@ static void prim_fetchMercurial(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
if (args[0]->type() == nAttrs) {
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
for (auto & attr : *args[0]->attrs()) {
|
||||
std::string_view n(state.ctx.symbols[attr.name]);
|
||||
if (n == "url")
|
||||
url = state.coerceToString(attr.pos, *attr.value, context,
|
||||
"while evaluating the `url` attribute passed to builtins.fetchMercurial",
|
||||
StringCoercionMode::Strict, false).toOwned();
|
||||
url = state
|
||||
.coerceToString(
|
||||
attr.pos,
|
||||
attr.value,
|
||||
context,
|
||||
"while evaluating the `url` attribute passed to "
|
||||
"builtins.fetchMercurial",
|
||||
StringCoercionMode::Strict,
|
||||
false
|
||||
)
|
||||
.toOwned();
|
||||
else if (n == "rev") {
|
||||
// Ugly: unlike fetchGit, here the "rev" attribute can
|
||||
// be both a revision or a branch/tag name.
|
||||
auto value = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `rev` attribute passed to builtins.fetchMercurial");
|
||||
if (std::regex_match(value.begin(), value.end(), revRegex))
|
||||
auto value = state.forceStringNoCtx(
|
||||
attr.value,
|
||||
attr.pos,
|
||||
"while evaluating the `rev` attribute passed to builtins.fetchMercurial"
|
||||
);
|
||||
if (std::regex_match(value.begin(), value.end(), revRegex)) {
|
||||
rev = Hash::parseAny(value, HashType::SHA1);
|
||||
else
|
||||
} else
|
||||
ref = value;
|
||||
}
|
||||
else if (n == "name")
|
||||
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.fetchMercurial");
|
||||
else
|
||||
state.ctx.errors.make<EvalError>("unsupported argument '%s' to 'fetchMercurial'", state.ctx.symbols[attr.name]).atPos(attr.pos).debugThrow();
|
||||
name = state.forceStringNoCtx(
|
||||
attr.value,
|
||||
attr.pos,
|
||||
"while evaluating the `name` attribute passed to builtins.fetchMercurial"
|
||||
);
|
||||
else {
|
||||
state.ctx.errors
|
||||
.make<EvalError>(
|
||||
"unsupported argument '%s' to 'fetchMercurial'",
|
||||
state.ctx.symbols[attr.name]
|
||||
)
|
||||
.atPos(attr.pos)
|
||||
.debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
if (url.empty())
|
||||
|
||||
@@ -122,35 +122,44 @@ static void fetchTree(
|
||||
|
||||
fetchers::Attrs attrs;
|
||||
|
||||
if (auto aType = args[0]->attrs->get(state.ctx.s.type)) {
|
||||
if (auto aType = args[0]->attrs()->get(state.ctx.s.type)) {
|
||||
if (type)
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"unexpected attribute 'type'"
|
||||
).atPos(pos).debugThrow();
|
||||
type = state.forceStringNoCtx(*aType->value, aType->pos, "while evaluating the `type` attribute passed to builtins.fetchTree");
|
||||
} else if (!type)
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"attribute 'type' is missing in call to 'fetchTree'"
|
||||
).atPos(pos).debugThrow();
|
||||
type = state.forceStringNoCtx(
|
||||
aType->value,
|
||||
aType->pos,
|
||||
"while evaluating the `type` attribute passed to builtins.fetchTree"
|
||||
);
|
||||
} else if (!type) {
|
||||
state.ctx.errors.make<EvalError>("attribute 'type' is missing in call to 'fetchTree'")
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
attrs.emplace("type", type.value());
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
for (auto & attr : *args[0]->attrs()) {
|
||||
if (attr.name == state.ctx.s.type) continue;
|
||||
state.forceValue(*attr.value, attr.pos);
|
||||
if (attr.value->type() == nPath || attr.value->type() == nString) {
|
||||
auto s = state.coerceToString(attr.pos, *attr.value, context, "", StringCoercionMode::Strict, false).toOwned();
|
||||
state.forceValue(attr.value, attr.pos);
|
||||
if (attr.value.type() == nPath || attr.value.type() == nString) {
|
||||
auto s =
|
||||
state
|
||||
.coerceToString(
|
||||
attr.pos, attr.value, context, "", StringCoercionMode::Strict, false
|
||||
)
|
||||
.toOwned();
|
||||
attrs.emplace(state.ctx.symbols[attr.name],
|
||||
state.ctx.symbols[attr.name] == "url"
|
||||
? type == "git"
|
||||
? fixURIForGit(s, state)
|
||||
: fixURI(s, state)
|
||||
: s);
|
||||
}
|
||||
else if (attr.value->type() == nBool)
|
||||
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool>{attr.value->boolean});
|
||||
else if (attr.value->type() == nInt) {
|
||||
auto intValue = attr.value->integer.value;
|
||||
} else if (attr.value.type() == nBool) {
|
||||
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool>{attr.value.boolean()});
|
||||
} else if (attr.value.type() == nInt) {
|
||||
auto intValue = attr.value.integer().value;
|
||||
|
||||
if (intValue < 0) {
|
||||
state.ctx.errors.make<EvalError>("negative value given for fetchTree attr %1%: %2%", state.ctx.symbols[attr.name], intValue).atPos(pos).debugThrow();
|
||||
@@ -158,9 +167,16 @@ static void fetchTree(
|
||||
unsigned long asUnsigned = intValue;
|
||||
|
||||
attrs.emplace(state.ctx.symbols[attr.name], asUnsigned);
|
||||
} else
|
||||
state.ctx.errors.make<TypeError>("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
|
||||
state.ctx.symbols[attr.name], showType(*attr.value)).debugThrow();
|
||||
} else {
|
||||
state.ctx.errors
|
||||
.make<TypeError>(
|
||||
"fetchTree argument '%s' is %s while a string, Boolean or integer is "
|
||||
"expected",
|
||||
state.ctx.symbols[attr.name],
|
||||
showType(attr.value)
|
||||
)
|
||||
.debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
if (!params.allowNameArgument)
|
||||
@@ -221,17 +237,32 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
||||
|
||||
if (args[0]->type() == nAttrs) {
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
for (auto & attr : *args[0]->attrs()) {
|
||||
std::string_view n(state.ctx.symbols[attr.name]);
|
||||
if (n == "url")
|
||||
url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch");
|
||||
else if (n == "sha256")
|
||||
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the sha256 of the content we should fetch"), HashType::SHA256);
|
||||
else if (n == "name")
|
||||
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the name of the content we should fetch");
|
||||
else
|
||||
url = state.forceStringNoCtx(
|
||||
attr.value, attr.pos, "while evaluating the url we should fetch"
|
||||
);
|
||||
else if (n == "sha256") {
|
||||
expectedHash = newHashAllowEmpty(
|
||||
state.forceStringNoCtx(
|
||||
attr.value,
|
||||
attr.pos,
|
||||
"while evaluating the sha256 of the content we should fetch"
|
||||
),
|
||||
HashType::SHA256
|
||||
);
|
||||
} else if (n == "name")
|
||||
name = state.forceStringNoCtx(
|
||||
attr.value,
|
||||
attr.pos,
|
||||
"while evaluating the name of the content we should fetch"
|
||||
);
|
||||
else {
|
||||
state.ctx.errors.make<EvalError>("unsupported argument '%s' to '%s'", n, who)
|
||||
.atPos(pos).debugThrow();
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
if (!url)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libexpr/extra-primops.hh"
|
||||
#include "value.hh"
|
||||
|
||||
#include <sstream>
|
||||
#include <toml.hpp>
|
||||
@@ -30,9 +31,10 @@ void prim_fromTOML(EvalState & state, Value ** args, Value & val)
|
||||
auto array = toml::get<std::vector<toml::value>>(t);
|
||||
|
||||
size_t size = array.size();
|
||||
v = state.ctx.mem.newList(size);
|
||||
auto list = state.ctx.mem.newList(size);
|
||||
v = {NewValueAs::list, list};
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
self(*(v.listElems()[i] = state.ctx.mem.allocValue()), array[i]);
|
||||
self(list->elems[i], array[i]);
|
||||
}
|
||||
} break;
|
||||
case toml::value_t::boolean:
|
||||
|
||||
@@ -9,11 +9,12 @@ namespace nix {
|
||||
|
||||
// See: https://github.com/NixOS/nix/issues/9730
|
||||
void printAmbiguous(
|
||||
Value &v,
|
||||
const SymbolTable &symbols,
|
||||
std::ostream &str,
|
||||
std::set<const void *> *seen,
|
||||
int depth)
|
||||
const Value & v,
|
||||
const SymbolTable & symbols,
|
||||
std::ostream & str,
|
||||
std::set<const void *> * seen,
|
||||
int depth
|
||||
)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
@@ -21,12 +22,16 @@ void printAmbiguous(
|
||||
str << "«too deep»";
|
||||
return;
|
||||
}
|
||||
if (v.isInvalid()) {
|
||||
str << "<INVALID>";
|
||||
return;
|
||||
}
|
||||
switch (v.type()) {
|
||||
case nInt:
|
||||
str << v.integer;
|
||||
str << v.integer();
|
||||
break;
|
||||
case nBool:
|
||||
printLiteralBool(str, v.boolean);
|
||||
printLiteralBool(str, v.boolean());
|
||||
break;
|
||||
case nString:
|
||||
escapeString(str, v.str());
|
||||
@@ -38,13 +43,13 @@ void printAmbiguous(
|
||||
str << "null";
|
||||
break;
|
||||
case nAttrs: {
|
||||
if (seen && !v.attrs->empty() && !seen->insert(v.attrs).second)
|
||||
if (seen && !v.attrs()->empty() && !seen->insert(v.attrs()).second)
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "{ ";
|
||||
for (auto & i : v.attrs->lexicographicOrder(symbols)) {
|
||||
for (auto & i : v.attrs()->lexicographicOrder(symbols)) {
|
||||
str << symbols[i->name] << " = ";
|
||||
printAmbiguous(*i->value, symbols, str, seen, depth - 1);
|
||||
printAmbiguous(i->value, symbols, str, seen, depth - 1);
|
||||
str << "; ";
|
||||
}
|
||||
str << "}";
|
||||
@@ -56,11 +61,8 @@ void printAmbiguous(
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "[ ";
|
||||
for (auto v2 : v.listItems()) {
|
||||
if (v2)
|
||||
printAmbiguous(*v2, symbols, str, seen, depth - 1);
|
||||
else
|
||||
str << "(nullptr)";
|
||||
for (auto & v2 : v.listItems()) {
|
||||
printAmbiguous(v2, symbols, str, seen, depth - 1);
|
||||
str << " ";
|
||||
}
|
||||
str << "]";
|
||||
@@ -89,10 +91,10 @@ void printAmbiguous(
|
||||
}
|
||||
break;
|
||||
case nExternal:
|
||||
str << *v.external;
|
||||
str << *v.external();
|
||||
break;
|
||||
case nFloat:
|
||||
str << v.fpoint;
|
||||
str << v.fpoint();
|
||||
break;
|
||||
default:
|
||||
printError("Lix evaluator internal error: printAmbiguous: invalid value type");
|
||||
|
||||
@@ -17,10 +17,10 @@ namespace nix {
|
||||
* See: https://github.com/NixOS/nix/issues/9730
|
||||
*/
|
||||
void printAmbiguous(
|
||||
Value &v,
|
||||
const SymbolTable &symbols,
|
||||
std::ostream &str,
|
||||
std::set<const void *> *seen,
|
||||
int depth);
|
||||
|
||||
const Value & v,
|
||||
const SymbolTable & symbols,
|
||||
std::ostream & str,
|
||||
std::set<const void *> * seen,
|
||||
int depth
|
||||
);
|
||||
}
|
||||
|
||||
+39
-32
@@ -177,7 +177,7 @@ private:
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
output << v.integer;
|
||||
output << v.integer();
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
@@ -186,7 +186,7 @@ private:
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
output << v.fpoint;
|
||||
output << v.fpoint();
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
@@ -195,7 +195,7 @@ private:
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
printLiteralBool(output, v.boolean);
|
||||
printLiteralBool(output, v.boolean());
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
@@ -232,12 +232,12 @@ private:
|
||||
|
||||
void printDerivation(Value & v)
|
||||
{
|
||||
auto i = v.attrs->get(state.ctx.s.drvPath);
|
||||
auto i = v.attrs()->get(state.ctx.s.drvPath);
|
||||
NixStringContext context;
|
||||
std::string storePath;
|
||||
if (i) {
|
||||
storePath = state.ctx.store->printStorePath(state.coerceToStorePath(
|
||||
i->pos, *i->value, context, "while evaluating the drvPath of a derivation"
|
||||
i->pos, i->value, context, "while evaluating the drvPath of a derivation"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -264,18 +264,18 @@ private:
|
||||
}
|
||||
|
||||
auto item = v[0].second;
|
||||
if (!item->value) {
|
||||
if (item->value.isInvalid()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options.force) {
|
||||
// The item is going to be forced during printing anyway, but we need its type now.
|
||||
state.forceValue(*item->value, noPos);
|
||||
state.forceValue(item->value, noPos);
|
||||
}
|
||||
|
||||
// Pretty-print single-item attrsets only if they contain nested
|
||||
// structures.
|
||||
auto itemType = item->value->type();
|
||||
auto itemType = item->value.type();
|
||||
return itemType == nList || itemType == nAttrs;
|
||||
}
|
||||
|
||||
@@ -283,14 +283,14 @@ private:
|
||||
{
|
||||
if (options.force && options.derivationPaths && state.isDerivation(v)) {
|
||||
printDerivation(v);
|
||||
} else if (seen && !v.attrs->empty() && !seen->insert(v.attrs).second) {
|
||||
} else if (seen && !v.attrs()->empty() && !seen->insert(v.attrs()).second) {
|
||||
printRepeated();
|
||||
} else if (depth < options.maxDepth || v.attrs->empty()) {
|
||||
} else if (depth < options.maxDepth || v.attrs()->empty()) {
|
||||
increaseIndent();
|
||||
output << "{";
|
||||
|
||||
AttrVec sorted;
|
||||
for (auto & i : *v.attrs)
|
||||
for (auto & i : *v.attrs())
|
||||
sorted.emplace_back(state.ctx.symbols[i.name], &i);
|
||||
|
||||
if (options.maxAttrs == std::numeric_limits<size_t>::max())
|
||||
@@ -324,7 +324,7 @@ private:
|
||||
}
|
||||
|
||||
output << " = ";
|
||||
print(*i.second->value, depth + 1);
|
||||
print(i.second->value, depth + 1);
|
||||
output << ";";
|
||||
attrsPrinted++;
|
||||
printedHere++;
|
||||
@@ -338,7 +338,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldPrettyPrintList(std::span<Value * const> list)
|
||||
bool shouldPrettyPrintList(std::span<Value> list)
|
||||
{
|
||||
if (!options.shouldPrettyPrint() || list.empty()) {
|
||||
return false;
|
||||
@@ -349,19 +349,19 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
auto item = list[0];
|
||||
if (!item) {
|
||||
auto & item = list[0];
|
||||
if (item.isInvalid()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options.force) {
|
||||
// The item is going to be forced during printing anyway, but we need its type now.
|
||||
state.forceValue(*item, noPos);
|
||||
state.forceValue(item, noPos);
|
||||
}
|
||||
|
||||
// Pretty-print single-item lists only if they contain nested
|
||||
// structures.
|
||||
auto itemType = item->type();
|
||||
auto itemType = item.type();
|
||||
return itemType == nList || itemType == nAttrs;
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ private:
|
||||
auto listItems = v.listItems();
|
||||
auto prettyPrint = shouldPrettyPrintList(listItems);
|
||||
size_t printedHere = 0;
|
||||
for (auto elem : listItems) {
|
||||
for (auto & elem : listItems) {
|
||||
printSpace(prettyPrint);
|
||||
|
||||
if (listItemsPrinted >= options.maxListItems) {
|
||||
@@ -386,11 +386,7 @@ private:
|
||||
break;
|
||||
}
|
||||
|
||||
if (elem) {
|
||||
print(*elem, depth + 1);
|
||||
} else {
|
||||
printNullptr();
|
||||
}
|
||||
print(elem, depth + 1);
|
||||
listItemsPrinted++;
|
||||
printedHere++;
|
||||
}
|
||||
@@ -411,23 +407,23 @@ private:
|
||||
|
||||
if (v.isLambda()) {
|
||||
output << "lambda";
|
||||
if (v.lambda.fun) {
|
||||
if (v.lambda.fun->name) {
|
||||
output << " " << state.ctx.symbols[v.lambda.fun->name];
|
||||
if (v.lambda().fun) {
|
||||
if (v.lambda().fun->name) {
|
||||
output << " " << state.ctx.symbols[v.lambda().fun->name];
|
||||
}
|
||||
|
||||
std::ostringstream s;
|
||||
s << state.ctx.positions[v.lambda.fun->pos];
|
||||
s << state.ctx.positions[v.lambda().fun->pos];
|
||||
output << " @ " << filterANSIEscapes(s.str());
|
||||
}
|
||||
} else if (v.isPrimOp()) {
|
||||
if (v.primOp)
|
||||
output << *v.primOp;
|
||||
if (v.primOp())
|
||||
output << *v.primOp();
|
||||
else
|
||||
output << "primop";
|
||||
} else if (v.isPrimOpApp()) {
|
||||
output << "partially applied ";
|
||||
auto primOp = v.primOpAppPrimOp();
|
||||
auto primOp = v.app().target().primOp();
|
||||
if (primOp)
|
||||
output << *primOp;
|
||||
else
|
||||
@@ -468,7 +464,7 @@ private:
|
||||
|
||||
void printExternal(Value & v)
|
||||
{
|
||||
v.external->print(output);
|
||||
v.external()->print(output);
|
||||
}
|
||||
|
||||
void printUnknown()
|
||||
@@ -495,11 +491,22 @@ private:
|
||||
checkInterrupt();
|
||||
|
||||
try {
|
||||
if (v.isInvalid()) {
|
||||
if (options.ansiColors) {
|
||||
output << ANSI_MAGENTA;
|
||||
}
|
||||
output << "«invalid»";
|
||||
if (options.ansiColors) {
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.force) {
|
||||
state.forceValue(v, noPos);
|
||||
}
|
||||
|
||||
switch (v.type()) {
|
||||
switch (v.type(true)) {
|
||||
|
||||
case nInt:
|
||||
printInt(v);
|
||||
|
||||
@@ -47,6 +47,8 @@ private:
|
||||
*/
|
||||
std::string contents;
|
||||
|
||||
Value::String strcb;
|
||||
|
||||
/*
|
||||
* A value containing a string that can be immediately passed to the evaluator.
|
||||
*/
|
||||
@@ -55,7 +57,8 @@ private:
|
||||
public:
|
||||
explicit InternedSymbol(std::string_view s)
|
||||
: contents(s)
|
||||
, underlyingValue(NewValueAs::string, contents.c_str(), nullptr)
|
||||
, strcb{.content = contents.c_str(), .context = nullptr}
|
||||
, underlyingValue(NewValueAs::string, &strcb)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -79,9 +82,9 @@ public:
|
||||
return contents;
|
||||
}
|
||||
|
||||
const Value * toValuePtr() const
|
||||
Value toValue() const
|
||||
{
|
||||
return &underlyingValue;
|
||||
return underlyingValue;
|
||||
}
|
||||
|
||||
friend std::ostream & operator<<(std::ostream & os, const InternedSymbol & symbol);
|
||||
|
||||
@@ -19,11 +19,11 @@ JSON printValueAsJSON(EvalState & state, bool strict,
|
||||
switch (v.type()) {
|
||||
|
||||
case nInt:
|
||||
out = v.integer.value;
|
||||
out = v.integer().value;
|
||||
break;
|
||||
|
||||
case nBool:
|
||||
out = v.boolean;
|
||||
out = v.boolean();
|
||||
break;
|
||||
|
||||
case nString:
|
||||
@@ -51,24 +51,27 @@ JSON printValueAsJSON(EvalState & state, bool strict,
|
||||
out = *maybeString;
|
||||
break;
|
||||
}
|
||||
auto i = v.attrs->get(state.ctx.s.outPath);
|
||||
auto i = v.attrs()->get(state.ctx.s.outPath);
|
||||
if (!i) {
|
||||
out = JSON::object();
|
||||
StringSet names;
|
||||
for (auto & j : *v.attrs)
|
||||
for (auto & j : *v.attrs())
|
||||
names.emplace(state.ctx.symbols[j.name]);
|
||||
for (auto & j : names) {
|
||||
const Attr & a(*v.attrs->get(state.ctx.symbols.create(j)));
|
||||
const Attr & a(*v.attrs()->get(state.ctx.symbols.create(j)));
|
||||
try {
|
||||
out[j] = printValueAsJSON(state, strict, *a.value, a.pos, context, copyToStore);
|
||||
out[j] =
|
||||
printValueAsJSON(state, strict, a.value, a.pos, context, copyToStore);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(state.ctx.positions[a.pos],
|
||||
HintFmt("while evaluating attribute '%1%'", j));
|
||||
e.addTrace(
|
||||
state.ctx.positions[a.pos],
|
||||
HintFmt("while evaluating attribute '%1%'", j)
|
||||
);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore);
|
||||
return printValueAsJSON(state, strict, i->value, i->pos, context, copyToStore);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -78,7 +81,7 @@ JSON printValueAsJSON(EvalState & state, bool strict,
|
||||
int i = 0;
|
||||
for (auto elem : v.listItems()) {
|
||||
try {
|
||||
out.push_back(printValueAsJSON(state, strict, *elem, pos, context, copyToStore));
|
||||
out.push_back(printValueAsJSON(state, strict, elem, pos, context, copyToStore));
|
||||
} catch (Error & e) {
|
||||
e.addTrace(state.ctx.positions[pos],
|
||||
HintFmt("while evaluating list element at index %1%", i));
|
||||
@@ -90,11 +93,11 @@ JSON printValueAsJSON(EvalState & state, bool strict,
|
||||
}
|
||||
|
||||
case nExternal:
|
||||
return v.external->printValueAsJSON(state, strict, context, copyToStore);
|
||||
return v.external()->printValueAsJSON(state, strict, context, copyToStore);
|
||||
break;
|
||||
|
||||
case nFloat:
|
||||
out = v.fpoint;
|
||||
out = v.fpoint();
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
|
||||
+27
-22
@@ -43,8 +43,7 @@ static void showAttrs(EvalState & state, bool strict, bool location,
|
||||
if (location && a.pos) posToXML(state, xmlAttrs, state.ctx.positions[a.pos]);
|
||||
|
||||
XMLOpenElement _(doc, "attr", xmlAttrs);
|
||||
printValueAsXML(state, strict, location,
|
||||
*a.value, doc, context, drvsSeen, a.pos);
|
||||
printValueAsXML(state, strict, location, a.value, doc, context, drvsSeen, a.pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,11 +59,11 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
switch (v.type()) {
|
||||
|
||||
case nInt:
|
||||
doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer)));
|
||||
doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer())));
|
||||
break;
|
||||
|
||||
case nBool:
|
||||
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false"));
|
||||
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean() ? "true" : "false"));
|
||||
break;
|
||||
|
||||
case nString:
|
||||
@@ -85,43 +84,49 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
if (state.isDerivation(v)) {
|
||||
XMLAttrs xmlAttrs;
|
||||
|
||||
auto a = v.attrs->get(state.ctx.symbols.create("derivation"));
|
||||
auto a = v.attrs()->get(state.ctx.symbols.create("derivation"));
|
||||
|
||||
Path drvPath;
|
||||
a = v.attrs->get(state.ctx.s.drvPath);
|
||||
a = v.attrs()->get(state.ctx.s.drvPath);
|
||||
if (a) {
|
||||
if (strict) state.forceValue(*a->value, a->pos);
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["drvPath"] = drvPath = a->value->str();
|
||||
if (strict) {
|
||||
state.forceValue(a->value, a->pos);
|
||||
}
|
||||
if (a->value.type() == nString) {
|
||||
xmlAttrs["drvPath"] = drvPath = a->value.str();
|
||||
}
|
||||
}
|
||||
|
||||
a = v.attrs->get(state.ctx.s.outPath);
|
||||
a = v.attrs()->get(state.ctx.s.outPath);
|
||||
if (a) {
|
||||
if (strict) state.forceValue(*a->value, a->pos);
|
||||
if (a->value->type() == nString) {
|
||||
xmlAttrs["outPath"] = a->value->str();
|
||||
if (strict) {
|
||||
state.forceValue(a->value, a->pos);
|
||||
}
|
||||
if (a->value.type() == nString) {
|
||||
xmlAttrs["outPath"] = a->value.str();
|
||||
}
|
||||
}
|
||||
|
||||
XMLOpenElement _(doc, "derivation", xmlAttrs);
|
||||
|
||||
if (drvPath != "" && drvsSeen.insert(drvPath).second)
|
||||
showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
|
||||
showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
|
||||
else
|
||||
doc.writeEmptyElement("repeated");
|
||||
}
|
||||
|
||||
else {
|
||||
XMLOpenElement _(doc, "attrs");
|
||||
showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
|
||||
showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case nList: {
|
||||
XMLOpenElement _(doc, "list");
|
||||
for (auto v2 : v.listItems())
|
||||
printValueAsXML(state, strict, location, *v2, doc, context, drvsSeen, pos);
|
||||
for (auto & v2 : v.listItems()) {
|
||||
printValueAsXML(state, strict, location, v2, doc, context, drvsSeen, pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -132,10 +137,10 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
break;
|
||||
}
|
||||
XMLAttrs xmlAttrs;
|
||||
if (location) posToXML(state, xmlAttrs, state.ctx.positions[v.lambda.fun->pos]);
|
||||
if (location) posToXML(state, xmlAttrs, state.ctx.positions[v.lambda().fun->pos]);
|
||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||
|
||||
if (auto formals = dynamic_cast<AttrsPattern *>(v.lambda.fun->pattern.get()); formals) {
|
||||
if (auto formals = dynamic_cast<AttrsPattern *>(v.lambda().fun->pattern.get()); formals) {
|
||||
XMLAttrs attrs;
|
||||
if (formals->name) attrs["name"] = state.ctx.symbols[formals->name];
|
||||
if (formals->ellipsis) attrs["ellipsis"] = "1";
|
||||
@@ -143,17 +148,17 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
for (const AttrsPattern::Formal & i : formals->lexicographicOrder(state.ctx.symbols))
|
||||
doc.writeEmptyElement("attr", singletonAttrs("name", state.ctx.symbols[i.name]));
|
||||
} else
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", state.ctx.symbols[v.lambda.fun->pattern->name]));
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", state.ctx.symbols[v.lambda().fun->pattern->name]));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case nExternal:
|
||||
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos);
|
||||
v.external()->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos);
|
||||
break;
|
||||
|
||||
case nFloat:
|
||||
doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint)));
|
||||
doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint())));
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
|
||||
+17
-38
@@ -9,26 +9,23 @@
|
||||
namespace nix
|
||||
{
|
||||
|
||||
Value Value::EMPTY_LIST{Value::list_t{}, {}};
|
||||
static const Value::List emptyListData{.size = 0};
|
||||
Value Value::EMPTY_LIST{Value::list_t{}, &emptyListData};
|
||||
|
||||
static void copyContextToValue(Value & v, const NixStringContext & context)
|
||||
const Value::Null Value::NULL_ACB = {{Value::Acb::tNull}};
|
||||
|
||||
static void copyContextToValue(Value::String & s, const NixStringContext & context)
|
||||
{
|
||||
if (!context.empty()) {
|
||||
size_t n = 0;
|
||||
v.string.context = gcAllocType<char const *>(context.size() + 1);
|
||||
s.context = gcAllocType<char const *>(context.size() + 1);
|
||||
for (auto & i : context)
|
||||
v.string.context[n++] = gcCopyStringIfNeeded(i.to_string());
|
||||
v.string.context[n] = 0;
|
||||
s.context[n++] = gcCopyStringIfNeeded(i.to_string());
|
||||
s.context[n] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Value::Value(primop_t, PrimOp & primop)
|
||||
: internalType(tPrimOp)
|
||||
, primOp(&primop)
|
||||
, _primop_pad(0)
|
||||
{
|
||||
}
|
||||
|
||||
Value::Value(primop_t, PrimOp & primop) : raw(tag(tAuxiliary, &primop)) {}
|
||||
|
||||
void Value::print(EvalState & state, std::ostream & str, PrintOptions options)
|
||||
{
|
||||
@@ -37,33 +34,16 @@ void Value::print(EvalState & state, std::ostream & str, PrintOptions options)
|
||||
|
||||
bool Value::isTrivial() const
|
||||
{
|
||||
return
|
||||
internalType != tApp
|
||||
&& internalType != tPrimOpApp
|
||||
&& (internalType != tThunk
|
||||
|| (thunk.expr->try_cast<ExprSet>()
|
||||
&& static_cast<ExprSet *>(thunk.expr)->dynamicAttrs.empty())
|
||||
|| thunk.expr->try_cast<ExprLambda>()
|
||||
|| thunk.expr->try_cast<ExprList>());
|
||||
}
|
||||
|
||||
PrimOp * Value::primOpAppPrimOp() const
|
||||
{
|
||||
Value * left = primOpApp.left;
|
||||
while (left && !left->isPrimOp()) {
|
||||
left = left->primOpApp.left;
|
||||
}
|
||||
|
||||
if (!left)
|
||||
return nullptr;
|
||||
return left->primOp;
|
||||
return internalType() != tApp
|
||||
&& (internalType() != tThunk
|
||||
|| (thunk().expr->try_cast<ExprSet>()
|
||||
&& static_cast<ExprSet *>(thunk().expr)->dynamicAttrs.empty())
|
||||
|| thunk().expr->try_cast<ExprLambda>() || thunk().expr->try_cast<ExprList>());
|
||||
}
|
||||
|
||||
void Value::mkPrimOp(PrimOp * p)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tPrimOp;
|
||||
primOp = p;
|
||||
*this = {NewValueAs::primop, *p};
|
||||
}
|
||||
|
||||
void Value::mkString(std::string_view s)
|
||||
@@ -74,16 +54,15 @@ void Value::mkString(std::string_view s)
|
||||
void Value::mkString(std::string_view s, const NixStringContext & context)
|
||||
{
|
||||
mkString(s);
|
||||
copyContextToValue(*this, context);
|
||||
copyContextToValue(*untag<String *>(), context);
|
||||
}
|
||||
|
||||
void Value::mkStringMove(const char * s, const NixStringContext & context)
|
||||
{
|
||||
mkString(s);
|
||||
copyContextToValue(*this, context);
|
||||
copyContextToValue(*untag<String *>(), context);
|
||||
}
|
||||
|
||||
|
||||
void Value::mkPath(const SourcePath & path)
|
||||
{
|
||||
*this = Value(NewValueAs::path, path);
|
||||
|
||||
+546
-373
File diff suppressed because it is too large
Load Diff
+11
-9
@@ -96,24 +96,26 @@ struct CmdBundle : InstallableCommand
|
||||
lockFlags
|
||||
};
|
||||
|
||||
auto vRes = evaluator->mem.allocValue();
|
||||
evalState->callFunction(*bundler.toValue(*evalState).first, *val, *vRes, noPos);
|
||||
Value vRes;
|
||||
auto fn = bundler.toValue(*evalState).first;
|
||||
evalState->callFunction(fn, val, vRes, noPos);
|
||||
|
||||
if (!evalState->isDerivation(*vRes))
|
||||
if (!evalState->isDerivation(vRes)) {
|
||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||
}
|
||||
|
||||
auto attr1 = vRes->attrs->get(evaluator->s.drvPath);
|
||||
auto attr1 = vRes.attrs()->get(evaluator->s.drvPath);
|
||||
if (!attr1)
|
||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||
|
||||
NixStringContext context2;
|
||||
auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2, "");
|
||||
auto drvPath = evalState->coerceToStorePath(attr1->pos, attr1->value, context2, "");
|
||||
|
||||
auto attr2 = vRes->attrs->get(evaluator->s.outPath);
|
||||
auto attr2 = vRes.attrs()->get(evaluator->s.outPath);
|
||||
if (!attr2)
|
||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||
|
||||
auto outPath = evalState->coerceToStorePath(attr2->pos, *attr2->value, context2, "");
|
||||
auto outPath = evalState->coerceToStorePath(attr2->pos, attr2->value, context2, "");
|
||||
|
||||
aio().blockOn(store->buildPaths({
|
||||
DerivedPath::Built {
|
||||
@@ -123,10 +125,10 @@ struct CmdBundle : InstallableCommand
|
||||
}));
|
||||
|
||||
if (!outLink) {
|
||||
auto * attr = vRes->attrs->get(evaluator->s.name);
|
||||
auto * attr = vRes.attrs()->get(evaluator->s.name);
|
||||
if (!attr)
|
||||
throw Error("attribute 'name' missing");
|
||||
outLink = evalState->forceStringNoCtx(*attr->value, attr->pos, "");
|
||||
outLink = evalState->forceStringNoCtx(attr->value, attr->pos, "");
|
||||
}
|
||||
|
||||
// TODO: will crash if not a localFSStore?
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ struct CmdEdit : InstallableCommand
|
||||
auto [v, pos] = installableValue->toValue(*state);
|
||||
|
||||
try {
|
||||
return findPackageFilename(*state, *v, installable->what());
|
||||
return findPackageFilename(*state, v, installable->what());
|
||||
} catch (NoPositionInfo &) {
|
||||
throw Error("cannot find position information for '%s", installableValue->what());
|
||||
}
|
||||
|
||||
+13
-8
@@ -79,21 +79,26 @@ struct CmdEval : MixJSON, InstallableCommand, MixReadOnlyOption
|
||||
NixStringContext context;
|
||||
|
||||
if (apply) {
|
||||
auto vApply = evaluator->mem.allocValue();
|
||||
state->eval(evaluator->parseExprFromString(*apply, CanonPath::fromCwd()), *vApply);
|
||||
auto vRes = evaluator->mem.allocValue();
|
||||
state->callFunction(*vApply, *v, *vRes, noPos);
|
||||
Value vApply;
|
||||
state->eval(evaluator->parseExprFromString(*apply, CanonPath::fromCwd()), vApply);
|
||||
Value vRes;
|
||||
state->callFunction(vApply, v, vRes, noPos);
|
||||
v = vRes;
|
||||
}
|
||||
|
||||
if (raw) {
|
||||
logger->pause();
|
||||
writeFull(STDOUT_FILENO, *state->coerceToString(noPos, *v, context, "while generating the eval command output"));
|
||||
writeFull(
|
||||
STDOUT_FILENO,
|
||||
*state->coerceToString(
|
||||
noPos, v, context, "while generating the eval command output"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
else if (json)
|
||||
{
|
||||
logger->cout("%s", printValueAsJSON(*state, true, *v, pos, context, false));
|
||||
logger->cout("%s", printValueAsJSON(*state, true, v, pos, context, false));
|
||||
}
|
||||
|
||||
else
|
||||
@@ -102,8 +107,8 @@ struct CmdEval : MixJSON, InstallableCommand, MixReadOnlyOption
|
||||
"%s",
|
||||
ValuePrinter(
|
||||
*state,
|
||||
*v,
|
||||
PrintOptions {
|
||||
v,
|
||||
PrintOptions{
|
||||
.force = true,
|
||||
.derivationPaths = true,
|
||||
.errors = ErrorPrintBehavior::ThrowTopLevel,
|
||||
|
||||
+105
-71
@@ -182,22 +182,24 @@ static void enumerateOutputs(
|
||||
{
|
||||
state.forceAttrs(vFlake, noPos, "while evaluating a flake to get its outputs");
|
||||
|
||||
auto aOutputs = vFlake.attrs->get(state.ctx.symbols.create("outputs"));
|
||||
auto aOutputs = vFlake.attrs()->get(state.ctx.symbols.create("outputs"));
|
||||
assert(aOutputs);
|
||||
|
||||
state.forceAttrs(*aOutputs->value, noPos, "while evaluating the outputs of a flake");
|
||||
state.forceAttrs(aOutputs->value, noPos, "while evaluating the outputs of a flake");
|
||||
|
||||
auto sHydraJobs = state.ctx.symbols.create("hydraJobs");
|
||||
|
||||
/* Hack: ensure that hydraJobs is evaluated before anything
|
||||
else. This way we can disable IFD for hydraJobs and then enable
|
||||
it for other outputs. */
|
||||
if (auto attr = aOutputs->value->attrs->get(sHydraJobs))
|
||||
callback(state.ctx.symbols[attr->name], *attr->value, attr->pos);
|
||||
if (auto attr = aOutputs->value.attrs()->get(sHydraJobs)) {
|
||||
callback(state.ctx.symbols[attr->name], attr->value, attr->pos);
|
||||
}
|
||||
|
||||
for (auto & attr : *aOutputs->value->attrs) {
|
||||
if (attr.name != sHydraJobs)
|
||||
callback(state.ctx.symbols[attr.name], *attr.value, attr.pos);
|
||||
for (auto & attr : *aOutputs->value.attrs()) {
|
||||
if (attr.name != sHydraJobs) {
|
||||
callback(state.ctx.symbols[attr.name], attr.value, attr.pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,7 +465,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
if (!v.isLambda()) {
|
||||
throw Error("overlay is not a function, but %s instead", showType(v));
|
||||
}
|
||||
auto body = v.lambda.fun->body->try_cast<ExprLambda>();
|
||||
auto body = v.lambda().fun->body->try_cast<ExprLambda>();
|
||||
if (!body)
|
||||
throw Error("overlay is not a function with two arguments, but only takes one");
|
||||
if (body->body->try_cast<ExprLambda>())
|
||||
@@ -499,15 +501,16 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
if (state->isDerivation(v))
|
||||
throw Error("jobset should not be a derivation at top-level");
|
||||
|
||||
for (auto & attr : *v.attrs) {
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr : *v.attrs()) {
|
||||
state->forceAttrs(attr.value, attr.pos, "");
|
||||
auto attrPath2 = concatStrings(attrPath, ".", evaluator->symbols[attr.name]);
|
||||
if (state->isDerivation(*attr.value)) {
|
||||
if (state->isDerivation(attr.value)) {
|
||||
Activity act(*logger, lvlInfo, actUnknown,
|
||||
fmt("checking Hydra job '%s'", attrPath2));
|
||||
checkDerivation(attrPath2, *attr.value, attr.pos);
|
||||
} else
|
||||
checkHydraJobs(attrPath2, *attr.value, attr.pos);
|
||||
checkDerivation(attrPath2, attr.value, attr.pos);
|
||||
} else {
|
||||
checkHydraJobs(attrPath2, attr.value, attr.pos);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Error & e) {
|
||||
@@ -522,9 +525,10 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
fmt("checking NixOS configuration '%s'", attrPath));
|
||||
Bindings & bindings(*evaluator->mem.allocBindings(0));
|
||||
auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first;
|
||||
state->forceValue(*vToplevel, pos);
|
||||
if (!state->isDerivation(*vToplevel))
|
||||
state->forceValue(vToplevel, pos);
|
||||
if (!state->isDerivation(vToplevel)) {
|
||||
throw Error("attribute 'config.system.build.toplevel' is not a derivation");
|
||||
}
|
||||
} catch (Error & e) {
|
||||
e.addTrace(resolve(pos), HintFmt("while checking the NixOS configuration '%s'", attrPath));
|
||||
reportError(e);
|
||||
@@ -538,11 +542,11 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
|
||||
state->forceAttrs(v, pos, "");
|
||||
|
||||
if (auto attr = v.attrs->get(evaluator->symbols.create("path"))) {
|
||||
if (auto attr = v.attrs()->get(evaluator->symbols.create("path"))) {
|
||||
if (attr->name == evaluator->symbols.create("path")) {
|
||||
NixStringContext context;
|
||||
auto path = state->ctx.paths.checkSourcePath(
|
||||
state->coerceToPath(attr->pos, *attr->value, context, "")
|
||||
state->coerceToPath(attr->pos, attr->value, context, "")
|
||||
);
|
||||
if (!path.pathExists())
|
||||
throw Error("template '%s' refers to a non-existent path '%s'", attrPath, path);
|
||||
@@ -551,12 +555,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
} else
|
||||
throw Error("template '%s' lacks attribute 'path'", attrPath);
|
||||
|
||||
if (auto attr = v.attrs->get(evaluator->symbols.create("description")))
|
||||
state->forceStringNoCtx(*attr->value, attr->pos, "");
|
||||
if (auto attr = v.attrs()->get(evaluator->symbols.create("description")))
|
||||
state->forceStringNoCtx(attr->value, attr->pos, "");
|
||||
else
|
||||
throw Error("template '%s' lacks attribute 'description'", attrPath);
|
||||
|
||||
for (auto & attr : *v.attrs) {
|
||||
for (auto & attr : *v.attrs()) {
|
||||
std::string_view name(evaluator->symbols[attr.name]);
|
||||
if (name != "path" && name != "description" && name != "welcomeText")
|
||||
throw Error("template '%s' has unsupported attribute '%s'", attrPath, name);
|
||||
@@ -584,12 +588,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
{
|
||||
Activity act(*logger, lvlInfo, actUnknown, "evaluating flake");
|
||||
|
||||
auto vFlake = evaluator->mem.allocValue();
|
||||
flake::callFlake(*state, flake, *vFlake);
|
||||
Value vFlake;
|
||||
flake::callFlake(*state, flake, vFlake);
|
||||
|
||||
enumerateOutputs(
|
||||
*state,
|
||||
*vFlake,
|
||||
vFlake,
|
||||
[&](const std::string_view name, Value & vOutput, const PosIdx pos) {
|
||||
Activity act(*logger, lvlInfo, actUnknown,
|
||||
fmt("checking flake output '%s'", name));
|
||||
@@ -617,15 +621,20 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
|
||||
if (name == "checks") {
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
for (auto & attr : *vOutput.attrs()) {
|
||||
const auto & attr_name = evaluator->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value->attrs) {
|
||||
state->forceAttrs(attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value.attrs()) {
|
||||
auto drvPath = checkDerivation(
|
||||
fmt("%s.%s.%s", name, attr_name, evaluator->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
fmt("%s.%s.%s",
|
||||
name,
|
||||
attr_name,
|
||||
evaluator->symbols[attr2.name]),
|
||||
attr2.value,
|
||||
attr2.pos
|
||||
);
|
||||
if (drvPath && attr_name == evalSettings.getCurrentSystem()) {
|
||||
drvPaths.push_back(DerivedPath::Built {
|
||||
.drvPath = makeConstantStorePath(*drvPath),
|
||||
@@ -640,13 +649,11 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "formatter")
|
||||
{
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
for (auto & attr : *vOutput.attrs()) {
|
||||
const auto & attr_name = evaluator->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
checkApp(
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
checkApp(fmt("%s.%s", name, attr_name), attr.value, attr.pos);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -654,15 +661,21 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "packages" || name == "devShells")
|
||||
{
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
for (auto & attr : *vOutput.attrs()) {
|
||||
const auto & attr_name = evaluator->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value->attrs)
|
||||
state->forceAttrs(attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value.attrs()) {
|
||||
checkDerivation(
|
||||
fmt("%s.%s.%s", name, attr_name, evaluator->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
fmt("%s.%s.%s",
|
||||
name,
|
||||
attr_name,
|
||||
evaluator->symbols[attr2.name]),
|
||||
attr2.value,
|
||||
attr2.pos
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -670,15 +683,21 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "apps")
|
||||
{
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
for (auto & attr : *vOutput.attrs()) {
|
||||
const auto & attr_name = evaluator->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value->attrs)
|
||||
state->forceAttrs(attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value.attrs()) {
|
||||
checkApp(
|
||||
fmt("%s.%s.%s", name, attr_name, evaluator->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
fmt("%s.%s.%s",
|
||||
name,
|
||||
attr_name,
|
||||
evaluator->symbols[attr2.name]),
|
||||
attr2.value,
|
||||
attr2.pos
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -686,13 +705,13 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "defaultPackage" || name == "devShell")
|
||||
{
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
for (auto & attr : *vOutput.attrs()) {
|
||||
const auto & attr_name = evaluator->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
checkDerivation(
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
fmt("%s.%s", name, attr_name), attr.value, attr.pos
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -700,13 +719,11 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "defaultApp")
|
||||
{
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
for (auto & attr : *vOutput.attrs()) {
|
||||
const auto & attr_name = evaluator->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
if (checkSystemType(attr_name, attr.pos) ) {
|
||||
checkApp(
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
checkApp(fmt("%s.%s", name, attr_name), attr.value, attr.pos);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -714,7 +731,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "legacyPackages")
|
||||
{
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
for (auto & attr : *vOutput.attrs()) {
|
||||
checkSystemName(evaluator->symbols[attr.name], attr.pos);
|
||||
checkSystemType(evaluator->symbols[attr.name], attr.pos);
|
||||
// FIXME: do getDerivations?
|
||||
@@ -729,9 +746,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "overlays")
|
||||
{
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs)
|
||||
checkOverlay(fmt("%s.%s", name, evaluator->symbols[attr.name]),
|
||||
*attr.value, attr.pos);
|
||||
for (auto & attr : *vOutput.attrs())
|
||||
checkOverlay(
|
||||
fmt("%s.%s", name, evaluator->symbols[attr.name]),
|
||||
attr.value,
|
||||
attr.pos
|
||||
);
|
||||
}
|
||||
|
||||
else if (name == "nixosModule")
|
||||
@@ -742,17 +762,23 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "nixosModules")
|
||||
{
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs)
|
||||
checkModule(fmt("%s.%s", name, evaluator->symbols[attr.name]),
|
||||
*attr.value, attr.pos);
|
||||
for (auto & attr : *vOutput.attrs())
|
||||
checkModule(
|
||||
fmt("%s.%s", name, evaluator->symbols[attr.name]),
|
||||
attr.value,
|
||||
attr.pos
|
||||
);
|
||||
}
|
||||
|
||||
else if (name == "nixosConfigurations")
|
||||
{
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs)
|
||||
checkNixOSConfiguration(fmt("%s.%s", name, evaluator->symbols[attr.name]),
|
||||
*attr.value, attr.pos);
|
||||
for (auto & attr : *vOutput.attrs())
|
||||
checkNixOSConfiguration(
|
||||
fmt("%s.%s", name, evaluator->symbols[attr.name]),
|
||||
attr.value,
|
||||
attr.pos
|
||||
);
|
||||
}
|
||||
|
||||
else if (name == "hydraJobs")
|
||||
@@ -768,21 +794,24 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "templates")
|
||||
{
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs)
|
||||
checkTemplate(fmt("%s.%s", name, evaluator->symbols[attr.name]),
|
||||
*attr.value, attr.pos);
|
||||
for (auto & attr : *vOutput.attrs())
|
||||
checkTemplate(
|
||||
fmt("%s.%s", name, evaluator->symbols[attr.name]),
|
||||
attr.value,
|
||||
attr.pos
|
||||
);
|
||||
}
|
||||
|
||||
else if (name == "defaultBundler")
|
||||
{
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
for (auto & attr : *vOutput.attrs()) {
|
||||
const auto & attr_name = evaluator->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
checkBundler(
|
||||
fmt("%s.%s", name, attr_name),
|
||||
*attr.value, attr.pos);
|
||||
fmt("%s.%s", name, attr_name), attr.value, attr.pos
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -790,15 +819,20 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
else if (name == "bundlers")
|
||||
{
|
||||
state->forceAttrs(vOutput, pos, "");
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
for (auto & attr : *vOutput.attrs()) {
|
||||
const auto & attr_name = evaluator->symbols[attr.name];
|
||||
checkSystemName(attr_name, attr.pos);
|
||||
if (checkSystemType(attr_name, attr.pos)) {
|
||||
state->forceAttrs(*attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value->attrs) {
|
||||
state->forceAttrs(attr.value, attr.pos, "");
|
||||
for (auto & attr2 : *attr.value.attrs()) {
|
||||
checkBundler(
|
||||
fmt("%s.%s.%s", name, attr_name, evaluator->symbols[attr2.name]),
|
||||
*attr2.value, attr2.pos);
|
||||
fmt("%s.%s.%s",
|
||||
name,
|
||||
attr_name,
|
||||
evaluator->symbols[attr2.name]),
|
||||
attr2.value,
|
||||
attr2.pos
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+24
-15
@@ -357,23 +357,28 @@ static void showHelp(AsyncIoRoot & aio, std::vector<std::string> subcommand, Nix
|
||||
Evaluator evaluator(aio, {}, aio.blockOn(openStore("dummy://")));
|
||||
auto state = evaluator.begin(aio);
|
||||
|
||||
auto vGenerateManpage = evaluator.mem.allocValue();
|
||||
state->eval(evaluator.parseExprFromString(
|
||||
#include "generate-manpage.nix.gen.hh"
|
||||
, CanonPath::root), *vGenerateManpage);
|
||||
Value vGenerateManpage;
|
||||
state->eval(
|
||||
evaluator.parseExprFromString(
|
||||
#include "generate-manpage.nix.gen.hh"
|
||||
, CanonPath::root
|
||||
),
|
||||
vGenerateManpage
|
||||
);
|
||||
|
||||
auto vDump = evaluator.mem.allocValue();
|
||||
vDump->mkString(toplevel.dumpCli());
|
||||
Value vDump;
|
||||
vDump.mkString(toplevel.dumpCli());
|
||||
|
||||
auto vRes = evaluator.mem.allocValue();
|
||||
state->callFunction(*vGenerateManpage, evaluator.builtins.get("false"), *vRes, noPos);
|
||||
state->callFunction(*vRes, *vDump, *vRes, noPos);
|
||||
Value vRes;
|
||||
state->callFunction(vGenerateManpage, evaluator.builtins.get("false"), vRes, noPos);
|
||||
state->callFunction(vRes, vDump, vRes, noPos);
|
||||
|
||||
auto attr = vRes->attrs->get(evaluator.symbols.create(mdName + ".md"));
|
||||
auto attr = vRes.attrs()->get(evaluator.symbols.create(mdName + ".md"));
|
||||
if (!attr)
|
||||
throw UsageError("`nix` has no subcommand '%s'", concatStringsSep("", subcommand));
|
||||
|
||||
auto markdown = state->forceString(*attr->value, noPos, "while evaluating the lowdown help text");
|
||||
auto markdown =
|
||||
state->forceString(attr->value, noPos, "while evaluating the lowdown help text");
|
||||
|
||||
RunPager pager;
|
||||
std::cout << renderMarkdownToTerminal(markdown) << "\n";
|
||||
@@ -523,12 +528,16 @@ void mainWrapped(AsyncIoRoot & aio, int argc, char * * argv)
|
||||
auto res = JSON::object();
|
||||
res["builtins"] = ({
|
||||
auto builtinsJson = JSON::object();
|
||||
auto builtins = state.builtins.env.values[0]->attrs;
|
||||
auto builtins = state.builtins.env.values[0].attrs();
|
||||
for (auto & builtin : *builtins) {
|
||||
auto b = JSON::object();
|
||||
if (!builtin.value->isPrimOp()) continue;
|
||||
auto primOp = builtin.value->primOp;
|
||||
if (!primOp->doc) continue;
|
||||
if (!builtin.value.isPrimOp()) {
|
||||
continue;
|
||||
}
|
||||
auto primOp = builtin.value.primOp();
|
||||
if (!primOp->doc) {
|
||||
continue;
|
||||
}
|
||||
b["arity"] = primOp->arity;
|
||||
b["args"] = primOp->args;
|
||||
b["doc"] = trim(stripIndentation(primOp->doc));
|
||||
|
||||
+28
-13
@@ -35,16 +35,19 @@ std::string resolveMirrorUrl(EvalState & state, const std::string & url)
|
||||
vMirrors);
|
||||
state.forceAttrs(vMirrors, noPos, "while evaluating the set of all mirrors");
|
||||
|
||||
auto mirrorList = vMirrors.attrs->get(state.ctx.symbols.create(mirrorName));
|
||||
auto mirrorList = vMirrors.attrs()->get(state.ctx.symbols.create(mirrorName));
|
||||
if (!mirrorList) {
|
||||
throw Error("unknown mirror name '%s'", mirrorName);
|
||||
}
|
||||
state.forceList(*mirrorList->value, noPos, "while evaluating one mirror configuration");
|
||||
state.forceList(mirrorList->value, noPos, "while evaluating one mirror configuration");
|
||||
|
||||
if (mirrorList->value->listSize() < 1)
|
||||
if (mirrorList->value.listSize() < 1) {
|
||||
throw Error("mirror URL '%s' did not expand to anything", url);
|
||||
}
|
||||
|
||||
std::string mirror(state.forceString(*mirrorList->value->listElems()[0], noPos, "while evaluating the first available mirror"));
|
||||
std::string mirror(state.forceString(
|
||||
mirrorList->value.listElems()[0], noPos, "while evaluating the first available mirror"
|
||||
));
|
||||
return mirror + (mirror.ends_with("/") ? "" : "/") + s.substr(p + 1);
|
||||
}
|
||||
|
||||
@@ -207,30 +210,42 @@ static int main_nix_prefetch_url(AsyncIoRoot & aio, std::string programName, Str
|
||||
evaluator->paths.resolveExprPath(
|
||||
aio.blockOn(lookupFileArg(*evaluator, args.empty() ? "." : args[0])).unwrap()),
|
||||
vRoot);
|
||||
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
|
||||
Value v(findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
|
||||
state->forceAttrs(v, noPos, "while evaluating the source attribute to prefetch");
|
||||
|
||||
/* Extract the URL. */
|
||||
auto * attr = v.attrs->get(evaluator->symbols.create("urls"));
|
||||
auto * attr = v.attrs()->get(evaluator->symbols.create("urls"));
|
||||
if (!attr)
|
||||
throw Error("attribute 'urls' missing");
|
||||
state->forceList(*attr->value, noPos, "while evaluating the urls to prefetch");
|
||||
if (attr->value->listSize() < 1)
|
||||
state->forceList(attr->value, noPos, "while evaluating the urls to prefetch");
|
||||
if (attr->value.listSize() < 1) {
|
||||
throw Error("'urls' list is empty");
|
||||
url = state->forceString(*attr->value->listElems()[0], noPos, "while evaluating the first url from the urls list");
|
||||
}
|
||||
url = state->forceString(
|
||||
attr->value.listElems()[0],
|
||||
noPos,
|
||||
"while evaluating the first url from the urls list"
|
||||
);
|
||||
|
||||
/* Extract the hash mode. */
|
||||
auto attr2 = v.attrs->get(evaluator->symbols.create("outputHashMode"));
|
||||
auto attr2 = v.attrs()->get(evaluator->symbols.create("outputHashMode"));
|
||||
if (!attr2)
|
||||
printInfo("warning: this does not look like a fetchurl call");
|
||||
else
|
||||
unpack = state->forceString(*attr2->value, noPos, "while evaluating the outputHashMode of the source to prefetch") == "recursive";
|
||||
unpack = state->forceString(
|
||||
attr2->value,
|
||||
noPos,
|
||||
"while evaluating the outputHashMode of the source to prefetch"
|
||||
)
|
||||
== "recursive";
|
||||
|
||||
/* Extract the name. */
|
||||
if (!name) {
|
||||
auto attr3 = v.attrs->get(evaluator->symbols.create("name"));
|
||||
auto attr3 = v.attrs()->get(evaluator->symbols.create("name"));
|
||||
if (!attr3)
|
||||
name = state->forceString(*attr3->value, noPos, "while evaluating the name of the source to prefetch");
|
||||
name = state->forceString(
|
||||
attr3->value, noPos, "while evaluating the name of the source to prefetch"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -66,15 +66,15 @@ struct CmdRepl : RawInstallablesCommand
|
||||
if (file){
|
||||
auto [val, pos] = installable.toValue(*state);
|
||||
auto what = installable.what();
|
||||
state->forceValue(*val, pos);
|
||||
state->forceValue(val, pos);
|
||||
auto autoArgs = getAutoArgs(*evaluator);
|
||||
auto valPost = evaluator->mem.allocValue();
|
||||
state->autoCallFunction(*autoArgs, *val, *valPost, pos);
|
||||
state->forceValue(*valPost, pos);
|
||||
Value valPost;
|
||||
state->autoCallFunction(*autoArgs, val, valPost, pos);
|
||||
state->forceValue(valPost, pos);
|
||||
values.push_back( {valPost, what });
|
||||
} else {
|
||||
auto [val, pos] = installable.toValue(*state);
|
||||
values.push_back( {val, what} );
|
||||
values.push_back({val, what});
|
||||
}
|
||||
}
|
||||
return values;
|
||||
|
||||
@@ -292,12 +292,14 @@ struct CmdUpgradeNix : MixDryRun, EvalCommand
|
||||
|
||||
auto evaluator = std::make_unique<Evaluator>(aio(), SearchPath{}, store);
|
||||
auto state = evaluator->begin(aio());
|
||||
auto v = evaluator->mem.allocValue();
|
||||
state->eval(evaluator->parseExprFromString(data, CanonPath("/no-such-path")), *v);
|
||||
Value v;
|
||||
state->eval(evaluator->parseExprFromString(data, CanonPath("/no-such-path")), v);
|
||||
Bindings & bindings(*evaluator->mem.allocBindings(0));
|
||||
auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first;
|
||||
auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, v).first;
|
||||
|
||||
return store->parseStorePath(state->forceString(*v2, noPos, "while evaluating the path tho latest nix version"));
|
||||
return store->parseStorePath(
|
||||
state->forceString(v2, noPos, "while evaluating the path tho latest nix version")
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
#include "buffered-io.hh"
|
||||
#include "eval-args.hh"
|
||||
|
||||
static nix::Value *releaseExprTopLevelValue(nix::EvalState &state,
|
||||
nix::Bindings &autoArgs,
|
||||
MyArgs &args) {
|
||||
static nix::Value releaseExprTopLevelValue(nix::EvalState &state,
|
||||
nix::Bindings &autoArgs,
|
||||
MyArgs &args) {
|
||||
nix::Value vTop;
|
||||
|
||||
if (args.fromArgs) {
|
||||
@@ -57,9 +57,9 @@ static nix::Value *releaseExprTopLevelValue(nix::EvalState &state,
|
||||
vTop);
|
||||
}
|
||||
|
||||
auto vRoot = state.ctx.mem.allocValue();
|
||||
nix::Value vRoot;
|
||||
|
||||
state.autoCallFunction(autoArgs, vTop, *vRoot, {});
|
||||
state.autoCallFunction(autoArgs, vTop, vRoot, {});
|
||||
|
||||
return vRoot;
|
||||
}
|
||||
@@ -78,13 +78,13 @@ static std::string attrPathJoin(nix::JSON input) {
|
||||
static std::optional<Constituents>
|
||||
readConstituents(const nix::Value *v, nix::box_ptr<nix::EvalState> &state,
|
||||
nix::ref<nix::eval_cache::CachingEvaluator> &evaluator) {
|
||||
auto a = v->attrs->get(state->ctx.symbols.create("_hydraAggregate"));
|
||||
if (a && state->forceBool(*a->value, a->pos,
|
||||
auto a = v->attrs()->get(state->ctx.symbols.create("_hydraAggregate"));
|
||||
if (a && state->forceBool(a->value, a->pos,
|
||||
"while evaluating the "
|
||||
"`_hydraAggregate` attribute")) {
|
||||
std::vector<std::string> constituents;
|
||||
std::vector<std::string> namedConstituents;
|
||||
auto a = v->attrs->get(state->ctx.symbols.create("constituents"));
|
||||
auto a = v->attrs()->get(state->ctx.symbols.create("constituents"));
|
||||
if (!a)
|
||||
state->ctx.errors
|
||||
.make<nix::EvalError>("derivation must have a ‘constituents’ "
|
||||
@@ -92,7 +92,7 @@ readConstituents(const nix::Value *v, nix::box_ptr<nix::EvalState> &state,
|
||||
.debugThrow(nix::always_progresses); // we can't have a debugger here
|
||||
|
||||
nix::NixStringContext context;
|
||||
state->coerceToString(a->pos, *a->value, context,
|
||||
state->coerceToString(a->pos, a->value, context,
|
||||
"while evaluating the `constituents` attribute",
|
||||
nix::StringCoercionMode::ToString, false);
|
||||
for (auto &c : context)
|
||||
@@ -106,14 +106,14 @@ readConstituents(const nix::Value *v, nix::box_ptr<nix::EvalState> &state,
|
||||
},
|
||||
c.raw);
|
||||
|
||||
state->forceList(*a->value, a->pos,
|
||||
state->forceList(a->value, a->pos,
|
||||
"while evaluating the "
|
||||
"`constituents` attribute");
|
||||
for (unsigned int n = 0; n < a->value->listSize(); ++n) {
|
||||
auto v = a->value->listElems()[n];
|
||||
state->forceValue(*v, nix::noPos);
|
||||
if (v->type() == nix::nString)
|
||||
namedConstituents.emplace_back(v->str());
|
||||
for (unsigned int n = 0; n < a->value.listSize(); ++n) {
|
||||
auto v = a->value.listElems()[n];
|
||||
state->forceValue(v, nix::noPos);
|
||||
if (v.type() == nix::nString)
|
||||
namedConstituents.emplace_back(v.str());
|
||||
}
|
||||
|
||||
return Constituents(constituents, namedConstituents);
|
||||
@@ -126,7 +126,7 @@ void worker(nix::ref<nix::eval_cache::CachingEvaluator> evaluator,
|
||||
nix::Bindings &autoArgs, nix::AutoCloseFD &to,
|
||||
nix::AutoCloseFD &from, MyArgs &args, nix::AsyncIoRoot &aio) {
|
||||
|
||||
nix::Value *vRoot = [&]() {
|
||||
nix::Value vRoot = [&]() {
|
||||
auto state = evaluator->begin(aio);
|
||||
if (args.flake) {
|
||||
auto [flakeRef, fragment, outputSpec] =
|
||||
@@ -168,18 +168,18 @@ void worker(nix::ref<nix::eval_cache::CachingEvaluator> evaluator,
|
||||
nix::JSON{{"attr", attrPathS}, {"attrPath", path}};
|
||||
try {
|
||||
auto vTmp =
|
||||
nix::findAlongAttrPath(*state, attrPathS, autoArgs, *vRoot)
|
||||
nix::findAlongAttrPath(*state, attrPathS, autoArgs, vRoot)
|
||||
.first;
|
||||
|
||||
auto v = evaluator->mem.allocValue();
|
||||
state->autoCallFunction(autoArgs, *vTmp, *v, {});
|
||||
nix::Value v;
|
||||
state->autoCallFunction(autoArgs, vTmp, v, {});
|
||||
|
||||
if (v->type() == nix::nAttrs) {
|
||||
if (auto drvInfo = nix::getDerivation(*state, *v, false)) {
|
||||
if (v.type() == nix::nAttrs) {
|
||||
if (auto drvInfo = nix::getDerivation(*state, v, false)) {
|
||||
std::optional<Constituents> maybeConstituents;
|
||||
if (args.constituents) {
|
||||
maybeConstituents =
|
||||
readConstituents(v, state, evaluator);
|
||||
readConstituents(&v, state, evaluator);
|
||||
}
|
||||
auto drv = Drv(attrPathS, *state, *drvInfo, args,
|
||||
maybeConstituents);
|
||||
@@ -197,16 +197,16 @@ void worker(nix::ref<nix::eval_cache::CachingEvaluator> evaluator,
|
||||
// = true;` for top-level attrset
|
||||
|
||||
for (auto &i :
|
||||
v->attrs->lexicographicOrder(evaluator->symbols)) {
|
||||
v.attrs()->lexicographicOrder(evaluator->symbols)) {
|
||||
const std::string_view name = evaluator->symbols[i->name];
|
||||
attrs.emplace_back(name);
|
||||
|
||||
if (name == "recurseForDerivations" &&
|
||||
!args.forceRecurse) {
|
||||
auto attrv = v->attrs->get(
|
||||
auto attrv = v.attrs()->get(
|
||||
evaluator->s.recurseForDerivations);
|
||||
recurse = state->forceBool(
|
||||
*attrv->value, attrv->pos,
|
||||
attrv->value, attrv->pos,
|
||||
"while evaluating recurseForDerivations");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
[ null <PRIMOP> <PRIMOP-APP> <LAMBDA> [ [ «repeated» ] ] ]
|
||||
[ null <PRIMOP> <PRIMOP-APP> <LAMBDA> [ «repeated» ] ]
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
[ [ 0 1 3 7 15 31 63 127 255 511 1023 2047 4095 8191 16383 32767 65535 131071 262143 524287 1048575 2097151 4194303 8388607 16777215 33554431 67108863 134217727 268435455 536870911 1073741823 2147483647 4294967295 8589934591 17179869183 34359738367 68719476735 137438953471 274877906943 549755813887 1099511627775 2199023255551 4398046511103 8796093022207 17592186044415 35184372088831 70368744177663 140737488355327 281474976710655 562949953421311 1125899906842623 2251799813685247 4503599627370495 9007199254740991 18014398509481983 36028797018963967 72057594037927935 144115188075855871 288230376151711743 576460752303423487 1152921504606846975 2305843009213693951 4611686018427387903 9223372036854775807 ] [ -1 -2 -4 -8 -16 -32 -64 -128 -256 -512 -1024 -2048 -4096 -8192 -16384 -32768 -65536 -131072 -262144 -524288 -1048576 -2097152 -4194304 -8388608 -16777216 -33554432 -67108864 -134217728 -268435456 -536870912 -1073741824 -2147483648 -4294967296 -8589934592 -17179869184 -34359738368 -68719476736 -137438953472 -274877906944 -549755813888 -1099511627776 -2199023255552 -4398046511104 -8796093022208 -17592186044416 -35184372088832 -70368744177664 -140737488355328 -281474976710656 -562949953421312 -1125899906842624 -2251799813685248 -4503599627370496 -9007199254740992 -18014398509481984 -36028797018963968 -72057594037927936 -144115188075855872 -288230376151711744 -576460752303423488 -1152921504606846976 -2305843009213693952 -4611686018427387904 -9223372036854775808 ] ]
|
||||
@@ -0,0 +1,8 @@
|
||||
let
|
||||
positive = n: acc: if n == 0 then [] else [ acc ] ++ positive (n - 1) (acc * 2 + 1);
|
||||
negative = n: acc: if n == 0 then [] else [ acc ] ++ negative (n - 1) (acc * 2);
|
||||
in
|
||||
[
|
||||
(positive 64 0)
|
||||
(negative 64 (-1))
|
||||
]
|
||||
@@ -1,6 +1,10 @@
|
||||
[[test]]
|
||||
runner = "eval-okay"
|
||||
|
||||
[[test]]
|
||||
runner = "eval-okay"
|
||||
in = "in-int-range.nix"
|
||||
|
||||
[[test]]
|
||||
runner = "eval-okay"
|
||||
in = "in-override.nix"
|
||||
|
||||
@@ -46,6 +46,7 @@ class NixSettings:
|
||||
config = dedent(f"""
|
||||
show-trace = true
|
||||
sandbox = true
|
||||
substituters =
|
||||
extra-sandbox-paths = {" ".join(env.path.to_sandbox_paths())}
|
||||
""")
|
||||
# Note: newline at the end is required due to nix being nix;
|
||||
|
||||
@@ -84,35 +84,35 @@ namespace nix {
|
||||
if (arg.type() != nInt) {
|
||||
return false;
|
||||
}
|
||||
return arg.integer.value == v;
|
||||
return arg.integer().value == v;
|
||||
}
|
||||
|
||||
MATCHER_P(IsFloatEq, v, fmt("The float is equal to \"%1%\"", v)) {
|
||||
if (arg.type() != nFloat) {
|
||||
return false;
|
||||
}
|
||||
return arg.fpoint == v;
|
||||
return arg.fpoint() == v;
|
||||
}
|
||||
|
||||
MATCHER(IsTrue, "") {
|
||||
if (arg.type() != nBool) {
|
||||
return false;
|
||||
}
|
||||
return arg.boolean == true;
|
||||
return arg.boolean() == true;
|
||||
}
|
||||
|
||||
MATCHER(IsFalse, "") {
|
||||
if (arg.type() != nBool) {
|
||||
return false;
|
||||
}
|
||||
return arg.boolean == false;
|
||||
return arg.boolean() == false;
|
||||
}
|
||||
|
||||
MATCHER_P(IsPathEq, p, fmt("Is a path equal to \"%1%\"", p)) {
|
||||
if (arg.type() != nPath) {
|
||||
*result_listener << "Expected a path got " << arg.type();
|
||||
return false;
|
||||
} else if (std::string_view(arg._path) != p) {
|
||||
} else if (std::string_view(arg.string().content) != p) {
|
||||
*result_listener << "Expected a path that equals \"" << p
|
||||
<< "\" but got: " << arg.path();
|
||||
return false;
|
||||
@@ -136,8 +136,8 @@ namespace nix {
|
||||
if (arg.type() != nAttrs) {
|
||||
*result_listener << "Expected set got " << arg.type();
|
||||
return false;
|
||||
} else if (arg.attrs->size() != (size_t)n) {
|
||||
*result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs->size();
|
||||
} else if (arg.attrs()->size() != (size_t)n) {
|
||||
*result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs()->size();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace nix {
|
||||
class AttrPathEval : public LibExprTest
|
||||
{
|
||||
public:
|
||||
std::pair<Value *, PosIdx> testFindAlongAttrPath(std::string expr, std::string path);
|
||||
std::pair<Value, PosIdx> testFindAlongAttrPath(std::string expr, std::string path);
|
||||
};
|
||||
|
||||
RC_GTEST_PROP(AttrPath, prop_round_trip, ())
|
||||
@@ -29,7 +29,7 @@ RC_GTEST_PROP(AttrPath, prop_round_trip, ())
|
||||
RC_ASSERT(strings == unparsedReparsed);
|
||||
}
|
||||
|
||||
std::pair<Value *, PosIdx> AttrPathEval::testFindAlongAttrPath(std::string expr, std::string path)
|
||||
std::pair<Value, PosIdx> AttrPathEval::testFindAlongAttrPath(std::string expr, std::string path)
|
||||
{
|
||||
auto v = eval(expr);
|
||||
auto bindings = evalState().ctx.buildBindings(0).finish();
|
||||
|
||||
@@ -27,9 +27,9 @@ RC_GTEST_FIXTURE_PROP(
|
||||
prop_opaque_path_round_trip,
|
||||
(const SingleDerivedPath::Opaque & o))
|
||||
{
|
||||
auto * v = evaluator.mem.allocValue();
|
||||
evaluator.paths.mkStorePathString(o.path, *v);
|
||||
auto d = state.coerceToSingleDerivedPath(noPos, *v, "");
|
||||
Value v;
|
||||
evaluator.paths.mkStorePathString(o.path, v);
|
||||
auto d = state.coerceToSingleDerivedPath(noPos, v, "");
|
||||
RC_ASSERT(SingleDerivedPath { o } == d);
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@ RC_GTEST_FIXTURE_PROP(
|
||||
prop_derived_path_built_out_path_round_trip,
|
||||
(const SingleDerivedPath::Built & b, const StorePath & outPath))
|
||||
{
|
||||
auto * v = evaluator.mem.allocValue();
|
||||
state.mkOutputString(*v, b, outPath);
|
||||
auto [d, _] = state.coerceToSingleDerivedPathUnchecked(noPos, *v, "");
|
||||
Value v;
|
||||
state.mkOutputString(v, b, outPath);
|
||||
auto [d, _] = state.coerceToSingleDerivedPathUnchecked(noPos, v, "");
|
||||
RC_ASSERT(SingleDerivedPath { b } == d);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,22 +71,22 @@ namespace nix {
|
||||
auto v = eval("builtins.tryEval (throw \"\")");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(2));
|
||||
auto s = createSymbol("success");
|
||||
auto p = v.attrs->get(s);
|
||||
auto p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsFalse());
|
||||
ASSERT_THAT(p->value, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, tryEvalSuccess) {
|
||||
auto v = eval("builtins.tryEval 123");
|
||||
ASSERT_THAT(v, IsAttrs());
|
||||
auto s = createSymbol("success");
|
||||
auto p = v.attrs->get(s);
|
||||
auto p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsTrue());
|
||||
ASSERT_THAT(p->value, IsTrue());
|
||||
s = createSymbol("value");
|
||||
p = v.attrs->get(s);
|
||||
p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsIntEq(123));
|
||||
ASSERT_THAT(p->value, IsIntEq(123));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, getEnv) {
|
||||
@@ -134,8 +134,8 @@ namespace nix {
|
||||
TEST_F(PrimOpTest, attrValues) {
|
||||
auto v = eval("builtins.attrValues { x = \"foo\"; a = 1; }");
|
||||
ASSERT_THAT(v, IsListOfSize(2));
|
||||
ASSERT_THAT(*v.listElems()[0], IsIntEq(1));
|
||||
ASSERT_THAT(*v.listElems()[1], IsStringEq("foo"));
|
||||
ASSERT_THAT(v.listElems()[0], IsIntEq(1));
|
||||
ASSERT_THAT(v.listElems()[1], IsStringEq("foo"));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, getAttr) {
|
||||
@@ -184,14 +184,14 @@ namespace nix {
|
||||
TEST_F(PrimOpTest, removeAttrsRetains) {
|
||||
auto v = eval("builtins.removeAttrs { x = 1; y = 2; } [\"x\"]");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
ASSERT_NE(v.attrs->get(createSymbol("y")), nullptr);
|
||||
ASSERT_NE(v.attrs()->get(createSymbol("y")), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, listToAttrsEmptyList) {
|
||||
auto v = eval("builtins.listToAttrs []");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(0));
|
||||
ASSERT_EQ(v.type(), nAttrs);
|
||||
ASSERT_EQ(v.attrs->size(), 0);
|
||||
ASSERT_EQ(v.attrs()->size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, listToAttrsNotFieldName) {
|
||||
@@ -201,54 +201,54 @@ namespace nix {
|
||||
TEST_F(PrimOpTest, listToAttrs) {
|
||||
auto v = eval("builtins.listToAttrs [ { name = \"key\"; value = 123; } ]");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto key = v.attrs->get(createSymbol("key"));
|
||||
auto key = v.attrs()->get(createSymbol("key"));
|
||||
ASSERT_NE(key, nullptr);
|
||||
ASSERT_THAT(*key->value, IsIntEq(123));
|
||||
ASSERT_THAT(key->value, IsIntEq(123));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, intersectAttrs) {
|
||||
auto v = eval("builtins.intersectAttrs { a = 1; b = 2; } { b = 3; c = 4; }");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto b = v.attrs->get(createSymbol("b"));
|
||||
auto b = v.attrs()->get(createSymbol("b"));
|
||||
ASSERT_NE(b, nullptr);
|
||||
ASSERT_THAT(*b->value, IsIntEq(3));
|
||||
ASSERT_THAT(b->value, IsIntEq(3));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, catAttrs) {
|
||||
auto v = eval("builtins.catAttrs \"a\" [{a = 1;} {b = 0;} {a = 2;}]");
|
||||
ASSERT_THAT(v, IsListOfSize(2));
|
||||
ASSERT_THAT(*v.listElems()[0], IsIntEq(1));
|
||||
ASSERT_THAT(*v.listElems()[1], IsIntEq(2));
|
||||
ASSERT_THAT(v.listElems()[0], IsIntEq(1));
|
||||
ASSERT_THAT(v.listElems()[1], IsIntEq(2));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, functionArgs) {
|
||||
auto v = eval("builtins.functionArgs ({ x, y ? 123}: 1)");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(2));
|
||||
|
||||
auto x = v.attrs->get(createSymbol("x"));
|
||||
auto x = v.attrs()->get(createSymbol("x"));
|
||||
ASSERT_NE(x, nullptr);
|
||||
ASSERT_THAT(*x->value, IsFalse());
|
||||
ASSERT_THAT(x->value, IsFalse());
|
||||
|
||||
auto y = v.attrs->get(createSymbol("y"));
|
||||
auto y = v.attrs()->get(createSymbol("y"));
|
||||
ASSERT_NE(y, nullptr);
|
||||
ASSERT_THAT(*y->value, IsTrue());
|
||||
ASSERT_THAT(y->value, IsTrue());
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, mapAttrs) {
|
||||
auto v = eval("builtins.mapAttrs (name: value: value * 10) { a = 1; b = 2; }");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(2));
|
||||
|
||||
auto a = v.attrs->get(createSymbol("a"));
|
||||
auto a = v.attrs()->get(createSymbol("a"));
|
||||
ASSERT_NE(a, nullptr);
|
||||
ASSERT_THAT(*a->value, IsThunk());
|
||||
state.forceValue(*a->value, noPos);
|
||||
ASSERT_THAT(*a->value, IsIntEq(10));
|
||||
ASSERT_THAT(a->value, IsThunk());
|
||||
state.forceValue(a->value, noPos);
|
||||
ASSERT_THAT(a->value, IsIntEq(10));
|
||||
|
||||
auto b = v.attrs->get(createSymbol("b"));
|
||||
auto b = v.attrs()->get(createSymbol("b"));
|
||||
ASSERT_NE(b, nullptr);
|
||||
ASSERT_THAT(*b->value, IsThunk());
|
||||
state.forceValue(*b->value, noPos);
|
||||
ASSERT_THAT(*b->value, IsIntEq(20));
|
||||
ASSERT_THAT(b->value, IsThunk());
|
||||
state.forceValue(b->value, noPos);
|
||||
ASSERT_THAT(b->value, IsIntEq(20));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, isList) {
|
||||
@@ -288,7 +288,7 @@ namespace nix {
|
||||
auto v = eval("builtins.tail [ 3 2 1 0 ]");
|
||||
ASSERT_THAT(v, IsListOfSize(3));
|
||||
for (const auto [n, elem] : enumerate(v.listItems()))
|
||||
ASSERT_THAT(*elem, IsIntEq(2 - static_cast<int>(n)));
|
||||
ASSERT_THAT(elem, IsIntEq(2 - static_cast<int>(n)));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, tailEmpty) {
|
||||
@@ -299,26 +299,26 @@ namespace nix {
|
||||
auto v = eval("map (x: \"foo\" + x) [ \"bar\" \"bla\" \"abc\" ]");
|
||||
ASSERT_THAT(v, IsListOfSize(3));
|
||||
auto elem = v.listElems()[0];
|
||||
ASSERT_THAT(*elem, IsThunk());
|
||||
state.forceValue(*elem, noPos);
|
||||
ASSERT_THAT(*elem, IsStringEq("foobar"));
|
||||
ASSERT_THAT(elem, IsThunk());
|
||||
state.forceValue(elem, noPos);
|
||||
ASSERT_THAT(elem, IsStringEq("foobar"));
|
||||
|
||||
elem = v.listElems()[1];
|
||||
ASSERT_THAT(*elem, IsThunk());
|
||||
state.forceValue(*elem, noPos);
|
||||
ASSERT_THAT(*elem, IsStringEq("foobla"));
|
||||
ASSERT_THAT(elem, IsThunk());
|
||||
state.forceValue(elem, noPos);
|
||||
ASSERT_THAT(elem, IsStringEq("foobla"));
|
||||
|
||||
elem = v.listElems()[2];
|
||||
ASSERT_THAT(*elem, IsThunk());
|
||||
state.forceValue(*elem, noPos);
|
||||
ASSERT_THAT(*elem, IsStringEq("fooabc"));
|
||||
ASSERT_THAT(elem, IsThunk());
|
||||
state.forceValue(elem, noPos);
|
||||
ASSERT_THAT(elem, IsStringEq("fooabc"));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, filter) {
|
||||
auto v = eval("builtins.filter (x: x == 2) [ 3 2 3 2 3 2 ]");
|
||||
ASSERT_THAT(v, IsListOfSize(3));
|
||||
for (const auto elem : v.listItems())
|
||||
ASSERT_THAT(*elem, IsIntEq(2));
|
||||
ASSERT_THAT(elem, IsIntEq(2));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, elemTrue) {
|
||||
@@ -335,7 +335,7 @@ namespace nix {
|
||||
auto v = eval("builtins.concatLists [[1 2] [3 4]]");
|
||||
ASSERT_THAT(v, IsListOfSize(4));
|
||||
for (const auto [i, elem] : enumerate(v.listItems()))
|
||||
ASSERT_THAT(*elem, IsIntEq(static_cast<int>(i)+1));
|
||||
ASSERT_THAT(elem, IsIntEq(static_cast<int>(i) + 1));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, length) {
|
||||
@@ -373,9 +373,9 @@ namespace nix {
|
||||
ASSERT_EQ(v.type(), nList);
|
||||
ASSERT_EQ(v.listSize(), 3);
|
||||
for (const auto [i, elem] : enumerate(v.listItems())) {
|
||||
ASSERT_THAT(*elem, IsThunk());
|
||||
state.forceValue(*elem, noPos);
|
||||
ASSERT_THAT(*elem, IsIntEq(static_cast<int>(i)+1));
|
||||
ASSERT_THAT(elem, IsThunk());
|
||||
state.forceValue(elem, noPos);
|
||||
ASSERT_THAT(elem, IsIntEq(static_cast<int>(i) + 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,27 +386,27 @@ namespace nix {
|
||||
|
||||
const std::vector<int> numbers = { 42, 77, 147, 249, 483, 526 };
|
||||
for (const auto [n, elem] : enumerate(v.listItems()))
|
||||
ASSERT_THAT(*elem, IsIntEq(numbers[n]));
|
||||
ASSERT_THAT(elem, IsIntEq(numbers[n]));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, partition) {
|
||||
auto v = eval("builtins.partition (x: x > 10) [1 23 9 3 42]");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(2));
|
||||
|
||||
auto right = v.attrs->get(createSymbol("right"));
|
||||
auto right = v.attrs()->get(createSymbol("right"));
|
||||
ASSERT_NE(right, nullptr);
|
||||
ASSERT_THAT(*right->value, IsListOfSize(2));
|
||||
ASSERT_THAT(*right->value->listElems()[0], IsIntEq(23));
|
||||
ASSERT_THAT(*right->value->listElems()[1], IsIntEq(42));
|
||||
ASSERT_THAT(right->value, IsListOfSize(2));
|
||||
ASSERT_THAT(right->value.listElems()[0], IsIntEq(23));
|
||||
ASSERT_THAT(right->value.listElems()[1], IsIntEq(42));
|
||||
|
||||
auto wrong = v.attrs->get(createSymbol("wrong"));
|
||||
auto wrong = v.attrs()->get(createSymbol("wrong"));
|
||||
ASSERT_NE(wrong, nullptr);
|
||||
ASSERT_EQ(wrong->value->type(), nList);
|
||||
ASSERT_EQ(wrong->value->listSize(), 3);
|
||||
ASSERT_THAT(*wrong->value, IsListOfSize(3));
|
||||
ASSERT_THAT(*wrong->value->listElems()[0], IsIntEq(1));
|
||||
ASSERT_THAT(*wrong->value->listElems()[1], IsIntEq(9));
|
||||
ASSERT_THAT(*wrong->value->listElems()[2], IsIntEq(3));
|
||||
ASSERT_EQ(wrong->value.type(), nList);
|
||||
ASSERT_EQ(wrong->value.listSize(), 3);
|
||||
ASSERT_THAT(wrong->value, IsListOfSize(3));
|
||||
ASSERT_THAT(wrong->value.listElems()[0], IsIntEq(1));
|
||||
ASSERT_THAT(wrong->value.listElems()[1], IsIntEq(9));
|
||||
ASSERT_THAT(wrong->value.listElems()[2], IsIntEq(3));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, concatMap) {
|
||||
@@ -416,7 +416,7 @@ namespace nix {
|
||||
|
||||
const std::vector<int> numbers = { 1, 2, 0, 3, 4, 0 };
|
||||
for (const auto [n, elem] : enumerate(v.listItems()))
|
||||
ASSERT_THAT(*elem, IsIntEq(numbers[n]));
|
||||
ASSERT_THAT(elem, IsIntEq(numbers[n]));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, addInt) {
|
||||
@@ -636,14 +636,14 @@ namespace nix {
|
||||
auto v = eval("derivation");
|
||||
ASSERT_EQ(v.type(), nFunction);
|
||||
ASSERT_TRUE(v.isLambda());
|
||||
ASSERT_NE(v.lambda.fun, nullptr);
|
||||
ASSERT_TRUE(dynamic_cast<AttrsPattern *>(v.lambda.fun->pattern.get()));
|
||||
ASSERT_NE(v.lambda().fun, nullptr);
|
||||
ASSERT_TRUE(dynamic_cast<AttrsPattern *>(v.lambda().fun->pattern.get()));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, currentTime) {
|
||||
auto v = eval("builtins.currentTime");
|
||||
ASSERT_EQ(v.type(), nInt);
|
||||
ASSERT_TRUE(v.integer > 0);
|
||||
ASSERT_TRUE(v.integer() > 0);
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, splitVersion) {
|
||||
@@ -652,7 +652,7 @@ namespace nix {
|
||||
|
||||
const std::vector<std::string_view> strings = { "1", "2", "3", "git" };
|
||||
for (const auto [n, p] : enumerate(v.listItems()))
|
||||
ASSERT_THAT(*p, IsStringEq(strings[n]));
|
||||
ASSERT_THAT(p, IsStringEq(strings[n]));
|
||||
}
|
||||
|
||||
class CompareVersionsPrimOpTest :
|
||||
@@ -704,13 +704,13 @@ namespace nix {
|
||||
auto v = eval(expr);
|
||||
ASSERT_THAT(v, IsAttrsOfSize(2));
|
||||
|
||||
auto name = v.attrs->get(createSymbol("name"));
|
||||
auto name = v.attrs()->get(createSymbol("name"));
|
||||
ASSERT_TRUE(name);
|
||||
ASSERT_THAT(*name->value, IsStringEq(expectedName));
|
||||
ASSERT_THAT(name->value, IsStringEq(expectedName));
|
||||
|
||||
auto version = v.attrs->get(createSymbol("version"));
|
||||
auto version = v.attrs()->get(createSymbol("version"));
|
||||
ASSERT_TRUE(version);
|
||||
ASSERT_THAT(*version->value, IsStringEq(expectedVersion));
|
||||
ASSERT_THAT(version->value, IsStringEq(expectedVersion));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
@@ -741,12 +741,12 @@ namespace nix {
|
||||
auto v = eval("builtins.split \"(a)b\" \"abc\"");
|
||||
ASSERT_THAT(v, IsListOfSize(3));
|
||||
|
||||
ASSERT_THAT(*v.listElems()[0], IsStringEq(""));
|
||||
ASSERT_THAT(v.listElems()[0], IsStringEq(""));
|
||||
|
||||
ASSERT_THAT(*v.listElems()[1], IsListOfSize(1));
|
||||
ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a"));
|
||||
ASSERT_THAT(v.listElems()[1], IsListOfSize(1));
|
||||
ASSERT_THAT(v.listElems()[1].listElems()[0], IsStringEq("a"));
|
||||
|
||||
ASSERT_THAT(*v.listElems()[2], IsStringEq("c"));
|
||||
ASSERT_THAT(v.listElems()[2], IsStringEq("c"));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, split2) {
|
||||
@@ -754,17 +754,17 @@ namespace nix {
|
||||
auto v = eval("builtins.split \"([ac])\" \"abc\"");
|
||||
ASSERT_THAT(v, IsListOfSize(5));
|
||||
|
||||
ASSERT_THAT(*v.listElems()[0], IsStringEq(""));
|
||||
ASSERT_THAT(v.listElems()[0], IsStringEq(""));
|
||||
|
||||
ASSERT_THAT(*v.listElems()[1], IsListOfSize(1));
|
||||
ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a"));
|
||||
ASSERT_THAT(v.listElems()[1], IsListOfSize(1));
|
||||
ASSERT_THAT(v.listElems()[1].listElems()[0], IsStringEq("a"));
|
||||
|
||||
ASSERT_THAT(*v.listElems()[2], IsStringEq("b"));
|
||||
ASSERT_THAT(v.listElems()[2], IsStringEq("b"));
|
||||
|
||||
ASSERT_THAT(*v.listElems()[3], IsListOfSize(1));
|
||||
ASSERT_THAT(*v.listElems()[3]->listElems()[0], IsStringEq("c"));
|
||||
ASSERT_THAT(v.listElems()[3], IsListOfSize(1));
|
||||
ASSERT_THAT(v.listElems()[3].listElems()[0], IsStringEq("c"));
|
||||
|
||||
ASSERT_THAT(*v.listElems()[4], IsStringEq(""));
|
||||
ASSERT_THAT(v.listElems()[4], IsStringEq(""));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, split3) {
|
||||
@@ -772,23 +772,23 @@ namespace nix {
|
||||
ASSERT_THAT(v, IsListOfSize(5));
|
||||
|
||||
// First list element
|
||||
ASSERT_THAT(*v.listElems()[0], IsStringEq(""));
|
||||
ASSERT_THAT(v.listElems()[0], IsStringEq(""));
|
||||
|
||||
// 2nd list element is a list [ "" null ]
|
||||
ASSERT_THAT(*v.listElems()[1], IsListOfSize(2));
|
||||
ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a"));
|
||||
ASSERT_THAT(*v.listElems()[1]->listElems()[1], IsNull());
|
||||
ASSERT_THAT(v.listElems()[1], IsListOfSize(2));
|
||||
ASSERT_THAT(v.listElems()[1].listElems()[0], IsStringEq("a"));
|
||||
ASSERT_THAT(v.listElems()[1].listElems()[1], IsNull());
|
||||
|
||||
// 3rd element
|
||||
ASSERT_THAT(*v.listElems()[2], IsStringEq("b"));
|
||||
ASSERT_THAT(v.listElems()[2], IsStringEq("b"));
|
||||
|
||||
// 4th element is a list: [ null "c" ]
|
||||
ASSERT_THAT(*v.listElems()[3], IsListOfSize(2));
|
||||
ASSERT_THAT(*v.listElems()[3]->listElems()[0], IsNull());
|
||||
ASSERT_THAT(*v.listElems()[3]->listElems()[1], IsStringEq("c"));
|
||||
ASSERT_THAT(v.listElems()[3], IsListOfSize(2));
|
||||
ASSERT_THAT(v.listElems()[3].listElems()[0], IsNull());
|
||||
ASSERT_THAT(v.listElems()[3].listElems()[1], IsStringEq("c"));
|
||||
|
||||
// 5th element is the empty string
|
||||
ASSERT_THAT(*v.listElems()[4], IsStringEq(""));
|
||||
ASSERT_THAT(v.listElems()[4], IsStringEq(""));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, split4) {
|
||||
@@ -798,12 +798,12 @@ namespace nix {
|
||||
auto second = v.listElems()[1];
|
||||
auto third = v.listElems()[2];
|
||||
|
||||
ASSERT_THAT(*first, IsStringEq(" "));
|
||||
ASSERT_THAT(first, IsStringEq(" "));
|
||||
|
||||
ASSERT_THAT(*second, IsListOfSize(1));
|
||||
ASSERT_THAT(*second->listElems()[0], IsStringEq("FOO"));
|
||||
ASSERT_THAT(second, IsListOfSize(1));
|
||||
ASSERT_THAT(second.listElems()[0], IsStringEq("FOO"));
|
||||
|
||||
ASSERT_THAT(*third, IsStringEq(" "));
|
||||
ASSERT_THAT(third, IsStringEq(" "));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, match1) {
|
||||
@@ -819,14 +819,14 @@ namespace nix {
|
||||
TEST_F(PrimOpTest, match3) {
|
||||
auto v = eval("builtins.match \"a(b)(c)\" \"abc\"");
|
||||
ASSERT_THAT(v, IsListOfSize(2));
|
||||
ASSERT_THAT(*v.listElems()[0], IsStringEq("b"));
|
||||
ASSERT_THAT(*v.listElems()[1], IsStringEq("c"));
|
||||
ASSERT_THAT(v.listElems()[0], IsStringEq("b"));
|
||||
ASSERT_THAT(v.listElems()[1], IsStringEq("c"));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, match4) {
|
||||
auto v = eval("builtins.match \"[[:space:]]+([[:upper:]]+)[[:space:]]+\" \" FOO \"");
|
||||
ASSERT_THAT(v, IsListOfSize(1));
|
||||
ASSERT_THAT(*v.listElems()[0], IsStringEq("FOO"));
|
||||
ASSERT_THAT(v.listElems()[0], IsStringEq("FOO"));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, attrNames) {
|
||||
@@ -836,7 +836,7 @@ namespace nix {
|
||||
// ensure that the list is sorted
|
||||
const std::vector<std::string_view> expected { "a", "x", "y", "z" };
|
||||
for (const auto [n, elem] : enumerate(v.listItems()))
|
||||
ASSERT_THAT(*elem, IsStringEq(expected[n]));
|
||||
ASSERT_THAT(elem, IsStringEq(expected[n]));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, genericClosure_not_strict) {
|
||||
|
||||
@@ -67,13 +67,13 @@ namespace nix {
|
||||
TEST_F(TrivialExpressionTest, updateAttrs) {
|
||||
auto v = eval("{ a = 1; } // { b = 2; a = 3; }");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(2));
|
||||
auto a = v.attrs->get(createSymbol("a"));
|
||||
auto a = v.attrs()->get(createSymbol("a"));
|
||||
ASSERT_NE(a, nullptr);
|
||||
ASSERT_THAT(*a->value, IsIntEq(3));
|
||||
ASSERT_THAT(a->value, IsIntEq(3));
|
||||
|
||||
auto b = v.attrs->get(createSymbol("b"));
|
||||
auto b = v.attrs()->get(createSymbol("b"));
|
||||
ASSERT_NE(b, nullptr);
|
||||
ASSERT_THAT(*b->value, IsIntEq(2));
|
||||
ASSERT_THAT(b->value, IsIntEq(2));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, hasAttrOpFalse) {
|
||||
@@ -168,21 +168,21 @@ namespace nix {
|
||||
auto v = eval(expr);
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
|
||||
auto a = v.attrs->get(createSymbol("a"));
|
||||
auto a = v.attrs()->get(createSymbol("a"));
|
||||
ASSERT_NE(a, nullptr);
|
||||
|
||||
ASSERT_THAT(*a->value, IsThunk());
|
||||
state.forceValue(*a->value, noPos);
|
||||
ASSERT_THAT(a->value, IsThunk());
|
||||
state.forceValue(a->value, noPos);
|
||||
|
||||
ASSERT_THAT(*a->value, IsAttrsOfSize(2));
|
||||
ASSERT_THAT(a->value, IsAttrsOfSize(2));
|
||||
|
||||
auto b = a->value->attrs->get(createSymbol("b"));
|
||||
auto b = a->value.attrs()->get(createSymbol("b"));
|
||||
ASSERT_NE(b, nullptr);
|
||||
ASSERT_THAT(*b->value, IsIntEq(1));
|
||||
ASSERT_THAT(b->value, IsIntEq(1));
|
||||
|
||||
auto c = a->value->attrs->get(createSymbol("c"));
|
||||
auto c = a->value.attrs()->get(createSymbol("c"));
|
||||
ASSERT_NE(c, nullptr);
|
||||
ASSERT_THAT(*c->value, IsIntEq(2));
|
||||
ASSERT_THAT(c->value, IsIntEq(2));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
@@ -202,9 +202,9 @@ namespace nix {
|
||||
TEST_F(TrivialExpressionTest, bindOr) {
|
||||
auto v = eval("{ or = 1; }");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto b = v.attrs->get(createSymbol("or"));
|
||||
auto b = v.attrs()->get(createSymbol("or"));
|
||||
ASSERT_NE(b, nullptr);
|
||||
ASSERT_THAT(*b->value, IsIntEq(1));
|
||||
ASSERT_THAT(b->value, IsIntEq(1));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, orCantBeUsed) {
|
||||
|
||||
+116
-115
@@ -1,3 +1,4 @@
|
||||
#include "lix/libexpr/nixexpr.hh"
|
||||
#include "lix/libutil/canon-path.hh"
|
||||
#include "lix/libutil/source-path.hh"
|
||||
#include "lix/libutil/terminal.hh"
|
||||
@@ -65,8 +66,8 @@ TEST_F(ValuePrintingTests, tAttrs)
|
||||
vTwo.mkInt(2);
|
||||
|
||||
BindingsBuilder builder = evaluator.buildBindings(10);
|
||||
builder.insert(evaluator.symbols.create("one"), &vOne);
|
||||
builder.insert(evaluator.symbols.create("two"), &vTwo);
|
||||
builder.insert(evaluator.symbols.create("one"), vOne);
|
||||
builder.insert(evaluator.symbols.create("two"), vTwo);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
@@ -82,33 +83,36 @@ TEST_F(ValuePrintingTests, tList)
|
||||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
Value vList = evaluator.mem.newList(5);
|
||||
vList.bigList.elems[0] = &vOne;
|
||||
vList.bigList.elems[1] = &vTwo;
|
||||
vList.bigList.size = 3;
|
||||
auto vList = evaluator.mem.newList(5);
|
||||
vList->elems[0] = vOne;
|
||||
vList->elems[1] = vTwo;
|
||||
vList->size = 3;
|
||||
|
||||
test(vList, "[ 1 2 «nullptr» ]");
|
||||
test(Value(NewValueAs::list, vList), "[ 1 2 «invalid» ]");
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, vThunk)
|
||||
{
|
||||
Value vThunk;
|
||||
ExprLiteral e(noPos, NewValueAs::integer, 0);
|
||||
vThunk.mkThunk(nullptr, e);
|
||||
EvalMemory mem;
|
||||
Env env;
|
||||
ExprInt e(noPos, 0);
|
||||
Value vThunk{NewValueAs::thunk, mem, env, e};
|
||||
|
||||
test(vThunk, "«thunk»");
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, vApp)
|
||||
{
|
||||
Value vApp;
|
||||
vApp.mkApp(nullptr, nullptr);
|
||||
EvalMemory mem;
|
||||
Value vFn{NewValueAs::null};
|
||||
Value vApp{NewValueAs::app, mem, vFn, vFn};
|
||||
|
||||
test(vApp, "«thunk»");
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, vLambda)
|
||||
{
|
||||
EvalMemory mem;
|
||||
Env env {
|
||||
.up = nullptr,
|
||||
.values = { }
|
||||
@@ -116,11 +120,12 @@ TEST_F(ValuePrintingTests, vLambda)
|
||||
PosTable::Origin origin = evaluator.positions.addOrigin(std::monostate(), 1);
|
||||
auto posIdx = evaluator.positions.add(origin, 0);
|
||||
|
||||
ExprLambda eLambda(posIdx, std::make_unique<AttrsPattern>(), std::make_unique<ExprLiteral>(noPos, NewValueAs::integer, 0));
|
||||
ExprLambda eLambda(
|
||||
posIdx, std::make_unique<AttrsPattern>(), std::make_unique<ExprInt>(noPos, 0)
|
||||
);
|
||||
eLambda.pattern->name = createSymbol("a");
|
||||
|
||||
Value vLambda;
|
||||
vLambda.mkLambda(&env, &eLambda);
|
||||
Value vLambda{NewValueAs::lambda, mem, env, eLambda};
|
||||
|
||||
test(vLambda, "«lambda @ «none»:1:1»");
|
||||
|
||||
@@ -132,9 +137,7 @@ TEST_F(ValuePrintingTests, vLambda)
|
||||
TEST_F(ValuePrintingTests, vPrimOp)
|
||||
{
|
||||
Value vPrimOp;
|
||||
PrimOp primOp{
|
||||
.name = "puppy"
|
||||
};
|
||||
PrimOp primOp{{.name = "puppy"}};
|
||||
vPrimOp.mkPrimOp(&primOp);
|
||||
|
||||
test(vPrimOp, "«primop puppy»");
|
||||
@@ -142,14 +145,12 @@ TEST_F(ValuePrintingTests, vPrimOp)
|
||||
|
||||
TEST_F(ValuePrintingTests, vPrimOpApp)
|
||||
{
|
||||
PrimOp primOp{
|
||||
.name = "puppy"
|
||||
};
|
||||
EvalMemory mem;
|
||||
PrimOp primOp{{.name = "puppy"}};
|
||||
Value vPrimOp;
|
||||
vPrimOp.mkPrimOp(&primOp);
|
||||
|
||||
Value vPrimOpApp;
|
||||
vPrimOpApp.mkPrimOpApp(&vPrimOp, nullptr);
|
||||
Value vPrimOpApp{NewValueAs::app, mem, vPrimOp, vPrimOp};
|
||||
|
||||
test(vPrimOpApp, "«partially applied primop puppy»");
|
||||
}
|
||||
@@ -189,8 +190,7 @@ TEST_F(ValuePrintingTests, vFloat)
|
||||
|
||||
TEST_F(ValuePrintingTests, vBlackhole)
|
||||
{
|
||||
Value vBlackhole;
|
||||
vBlackhole.mkBlackhole();
|
||||
Value vBlackhole{NewValueAs::blackhole};
|
||||
test(vBlackhole, "«potential infinite recursion»");
|
||||
}
|
||||
|
||||
@@ -210,23 +210,23 @@ TEST_F(ValuePrintingTests, depthAttrs)
|
||||
vAttrsEmpty.mkAttrs(builderEmpty.finish());
|
||||
|
||||
BindingsBuilder builderNested = evaluator.buildBindings(1);
|
||||
builderNested.insert(evaluator.symbols.create("zero"), &vZero);
|
||||
builderNested.insert(evaluator.symbols.create("zero"), vZero);
|
||||
Value vAttrsNested;
|
||||
vAttrsNested.mkAttrs(builderNested.finish());
|
||||
|
||||
BindingsBuilder builder = evaluator.buildBindings(10);
|
||||
builder.insert(evaluator.symbols.create("one"), &vOne);
|
||||
builder.insert(evaluator.symbols.create("two"), &vTwo);
|
||||
builder.insert(evaluator.symbols.create("empty"), &vAttrsEmpty);
|
||||
builder.insert(evaluator.symbols.create("nested"), &vAttrsNested);
|
||||
builder.insert(evaluator.symbols.create("one"), vOne);
|
||||
builder.insert(evaluator.symbols.create("two"), vTwo);
|
||||
builder.insert(evaluator.symbols.create("empty"), vAttrsEmpty);
|
||||
builder.insert(evaluator.symbols.create("nested"), vAttrsNested);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
BindingsBuilder builder2 = evaluator.buildBindings(10);
|
||||
builder2.insert(evaluator.symbols.create("one"), &vOne);
|
||||
builder2.insert(evaluator.symbols.create("two"), &vTwo);
|
||||
builder2.insert(evaluator.symbols.create("nested"), &vAttrs);
|
||||
builder2.insert(evaluator.symbols.create("one"), vOne);
|
||||
builder2.insert(evaluator.symbols.create("two"), vTwo);
|
||||
builder2.insert(evaluator.symbols.create("nested"), vAttrs);
|
||||
|
||||
Value vNested;
|
||||
vNested.mkAttrs(builder2.finish());
|
||||
@@ -246,26 +246,27 @@ TEST_F(ValuePrintingTests, depthList)
|
||||
vTwo.mkInt(2);
|
||||
|
||||
BindingsBuilder builder = evaluator.buildBindings(10);
|
||||
builder.insert(evaluator.symbols.create("one"), &vOne);
|
||||
builder.insert(evaluator.symbols.create("two"), &vTwo);
|
||||
builder.insert(evaluator.symbols.create("one"), vOne);
|
||||
builder.insert(evaluator.symbols.create("two"), vTwo);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
BindingsBuilder builder2 = evaluator.buildBindings(10);
|
||||
builder2.insert(evaluator.symbols.create("one"), &vOne);
|
||||
builder2.insert(evaluator.symbols.create("two"), &vTwo);
|
||||
builder2.insert(evaluator.symbols.create("nested"), &vAttrs);
|
||||
builder2.insert(evaluator.symbols.create("one"), vOne);
|
||||
builder2.insert(evaluator.symbols.create("two"), vTwo);
|
||||
builder2.insert(evaluator.symbols.create("nested"), vAttrs);
|
||||
|
||||
Value vNested;
|
||||
vNested.mkAttrs(builder2.finish());
|
||||
|
||||
Value vList = evaluator.mem.newList(5);
|
||||
vList.bigList.elems[0] = &vOne;
|
||||
vList.bigList.elems[1] = &vTwo;
|
||||
vList.bigList.elems[2] = &vNested;
|
||||
vList.bigList.size = 3;
|
||||
auto list = evaluator.mem.newList(5);
|
||||
list->elems[0] = vOne;
|
||||
list->elems[1] = vTwo;
|
||||
list->elems[2] = vNested;
|
||||
list->size = 3;
|
||||
|
||||
Value vList{NewValueAs::list, list};
|
||||
test(vList, "[ 1 2 { ... } ]", PrintOptions { .maxDepth = 1 });
|
||||
test(vList, "[ 1 2 { nested = { ... }; one = 1; two = 2; } ]", PrintOptions { .maxDepth = 2 });
|
||||
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", PrintOptions { .maxDepth = 3 });
|
||||
@@ -309,8 +310,8 @@ TEST_F(ValuePrintingTests, attrsTypeFirst)
|
||||
vApple.mkString("apple");
|
||||
|
||||
BindingsBuilder builder = evaluator.buildBindings(10);
|
||||
builder.insert(evaluator.symbols.create("type"), &vType);
|
||||
builder.insert(evaluator.symbols.create("apple"), &vApple);
|
||||
builder.insert(evaluator.symbols.create("type"), vType);
|
||||
builder.insert(evaluator.symbols.create("apple"), vApple);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
@@ -420,8 +421,8 @@ TEST_F(ValuePrintingTests, ansiColorsAttrs)
|
||||
vTwo.mkInt(2);
|
||||
|
||||
BindingsBuilder builder = evaluator.buildBindings(10);
|
||||
builder.insert(evaluator.symbols.create("one"), &vOne);
|
||||
builder.insert(evaluator.symbols.create("two"), &vTwo);
|
||||
builder.insert(evaluator.symbols.create("one"), vOne);
|
||||
builder.insert(evaluator.symbols.create("two"), vTwo);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
@@ -439,7 +440,7 @@ TEST_F(ValuePrintingTests, ansiColorsDerivation)
|
||||
vDerivation.mkString("derivation");
|
||||
|
||||
BindingsBuilder builder = evaluator.buildBindings(10);
|
||||
builder.insert(evaluator.s.type, &vDerivation);
|
||||
builder.insert(evaluator.s.type, vDerivation);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
@@ -466,14 +467,14 @@ TEST_F(ValuePrintingTests, ansiColorsError)
|
||||
auto & e = evaluator.parseExprFromString("{ a = throw \"uh oh!\"; }", {CanonPath::root});
|
||||
state.eval(e, vError);
|
||||
|
||||
test(*vError.attrs->begin()->value,
|
||||
ANSI_RED
|
||||
"«error: uh oh!»"
|
||||
ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
});
|
||||
test(
|
||||
vError.attrs()->begin()->value,
|
||||
ANSI_RED "«error: uh oh!»" ANSI_NORMAL,
|
||||
PrintOptions{
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsDerivationError)
|
||||
@@ -517,12 +518,11 @@ TEST_F(ValuePrintingTests, ansiColorsAssert)
|
||||
state.eval(e, v);
|
||||
|
||||
ASSERT_EQ(v.type(), nAttrs);
|
||||
test(*v.attrs->begin()->value,
|
||||
ANSI_RED "«error: assertion failed»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true
|
||||
});
|
||||
test(
|
||||
v.attrs()->begin()->value,
|
||||
ANSI_RED "«error: assertion failed»" ANSI_NORMAL,
|
||||
PrintOptions{.ansiColors = true, .force = true}
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsList)
|
||||
@@ -533,20 +533,22 @@ TEST_F(ValuePrintingTests, ansiColorsList)
|
||||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
Value vList = evaluator.mem.newList(5);
|
||||
vList.bigList.elems[0] = &vOne;
|
||||
vList.bigList.elems[1] = &vTwo;
|
||||
vList.bigList.size = 3;
|
||||
auto vList = evaluator.mem.newList(5);
|
||||
vList->elems[0] = vOne;
|
||||
vList->elems[1] = vTwo;
|
||||
vList->size = 3;
|
||||
|
||||
test(vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_CYAN "2" ANSI_NORMAL " " ANSI_MAGENTA "«nullptr»" ANSI_NORMAL " ]",
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(
|
||||
Value(NewValueAs::list, vList),
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_CYAN "2" ANSI_NORMAL " " ANSI_MAGENTA
|
||||
"«invalid»" ANSI_NORMAL " ]",
|
||||
PrintOptions{.ansiColors = true}
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsLambda)
|
||||
{
|
||||
EvalMemory mem;
|
||||
Env env {
|
||||
.up = nullptr,
|
||||
.values = { }
|
||||
@@ -554,11 +556,12 @@ TEST_F(ValuePrintingTests, ansiColorsLambda)
|
||||
PosTable::Origin origin = evaluator.positions.addOrigin(std::monostate(), 1);
|
||||
auto posIdx = evaluator.positions.add(origin, 0);
|
||||
|
||||
ExprLambda eLambda(posIdx, std::make_unique<AttrsPattern>(), std::make_unique<ExprLiteral>(noPos, NewValueAs::integer, 0));
|
||||
ExprLambda eLambda(
|
||||
posIdx, std::make_unique<AttrsPattern>(), std::make_unique<ExprInt>(noPos, 0)
|
||||
);
|
||||
eLambda.pattern->name = createSymbol("a");
|
||||
|
||||
Value vLambda;
|
||||
vLambda.mkLambda(&env, &eLambda);
|
||||
Value vLambda{NewValueAs::lambda, mem, env, eLambda};
|
||||
|
||||
test(vLambda,
|
||||
ANSI_BLUE "«lambda @ «none»:1:1»" ANSI_NORMAL,
|
||||
@@ -579,9 +582,7 @@ TEST_F(ValuePrintingTests, ansiColorsLambda)
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsPrimOp)
|
||||
{
|
||||
PrimOp primOp{
|
||||
.name = "puppy"
|
||||
};
|
||||
PrimOp primOp{{.name = "puppy"}};
|
||||
Value v;
|
||||
v.mkPrimOp(&primOp);
|
||||
|
||||
@@ -594,14 +595,12 @@ TEST_F(ValuePrintingTests, ansiColorsPrimOp)
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsPrimOpApp)
|
||||
{
|
||||
PrimOp primOp{
|
||||
.name = "puppy"
|
||||
};
|
||||
EvalMemory mem;
|
||||
PrimOp primOp{{.name = "puppy"}};
|
||||
Value vPrimOp;
|
||||
vPrimOp.mkPrimOp(&primOp);
|
||||
|
||||
Value v;
|
||||
v.mkPrimOpApp(&vPrimOp, nullptr);
|
||||
Value v{NewValueAs::app, mem, vPrimOp, vPrimOp};
|
||||
|
||||
test(v,
|
||||
ANSI_BLUE "«partially applied primop puppy»" ANSI_NORMAL,
|
||||
@@ -612,9 +611,10 @@ TEST_F(ValuePrintingTests, ansiColorsPrimOpApp)
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsThunk)
|
||||
{
|
||||
Value v;
|
||||
ExprLiteral e(noPos, NewValueAs::integer, 0);
|
||||
v.mkThunk(nullptr, e);
|
||||
EvalMemory mem;
|
||||
Env env;
|
||||
ExprInt e(noPos, 0);
|
||||
Value v{NewValueAs::thunk, mem, env, e};
|
||||
|
||||
test(v,
|
||||
ANSI_MAGENTA "«thunk»" ANSI_NORMAL,
|
||||
@@ -625,8 +625,7 @@ TEST_F(ValuePrintingTests, ansiColorsThunk)
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsBlackhole)
|
||||
{
|
||||
Value v;
|
||||
v.mkBlackhole();
|
||||
Value v{NewValueAs::blackhole};
|
||||
|
||||
test(v,
|
||||
ANSI_RED "«potential infinite recursion»" ANSI_NORMAL,
|
||||
@@ -641,14 +640,14 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsRepeated)
|
||||
vZero.mkInt(0);
|
||||
|
||||
BindingsBuilder innerBuilder = evaluator.buildBindings(1);
|
||||
innerBuilder.insert(evaluator.symbols.create("x"), &vZero);
|
||||
innerBuilder.insert(evaluator.symbols.create("x"), vZero);
|
||||
|
||||
Value vInner;
|
||||
vInner.mkAttrs(innerBuilder.finish());
|
||||
|
||||
BindingsBuilder builder = evaluator.buildBindings(10);
|
||||
builder.insert(evaluator.symbols.create("a"), &vInner);
|
||||
builder.insert(evaluator.symbols.create("b"), &vInner);
|
||||
builder.insert(evaluator.symbols.create("a"), vInner);
|
||||
builder.insert(evaluator.symbols.create("b"), vInner);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
@@ -666,21 +665,21 @@ TEST_F(ValuePrintingTests, ansiColorsListRepeated)
|
||||
vZero.mkInt(0);
|
||||
|
||||
BindingsBuilder innerBuilder = evaluator.buildBindings(1);
|
||||
innerBuilder.insert(evaluator.symbols.create("x"), &vZero);
|
||||
innerBuilder.insert(evaluator.symbols.create("x"), vZero);
|
||||
|
||||
Value vInner;
|
||||
vInner.mkAttrs(innerBuilder.finish());
|
||||
|
||||
Value vList = evaluator.mem.newList(3);
|
||||
vList.bigList.elems[0] = &vInner;
|
||||
vList.bigList.elems[1] = &vInner;
|
||||
vList.bigList.size = 2;
|
||||
auto vList = evaluator.mem.newList(3);
|
||||
vList->elems[0] = vInner;
|
||||
vList->elems[1] = vInner;
|
||||
vList->size = 2;
|
||||
|
||||
test(vList,
|
||||
"[ { x = " ANSI_CYAN "0" ANSI_NORMAL "; } " ANSI_MAGENTA "«repeated»" ANSI_NORMAL " ]",
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(
|
||||
Value(NewValueAs::list, vList),
|
||||
"[ { x = " ANSI_CYAN "0" ANSI_NORMAL "; } " ANSI_MAGENTA "«repeated»" ANSI_NORMAL " ]",
|
||||
PrintOptions{.ansiColors = true}
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, listRepeated)
|
||||
@@ -689,16 +688,17 @@ TEST_F(ValuePrintingTests, listRepeated)
|
||||
vZero.mkInt(0);
|
||||
|
||||
BindingsBuilder innerBuilder = evaluator.buildBindings(1);
|
||||
innerBuilder.insert(evaluator.symbols.create("x"), &vZero);
|
||||
innerBuilder.insert(evaluator.symbols.create("x"), vZero);
|
||||
|
||||
Value vInner;
|
||||
vInner.mkAttrs(innerBuilder.finish());
|
||||
|
||||
Value vList = evaluator.mem.newList(3);
|
||||
vList.bigList.elems[0] = &vInner;
|
||||
vList.bigList.elems[1] = &vInner;
|
||||
vList.bigList.size = 2;
|
||||
auto list = evaluator.mem.newList(3);
|
||||
list->elems[0] = vInner;
|
||||
list->elems[1] = vInner;
|
||||
list->size = 2;
|
||||
|
||||
Value vList(NewValueAs::list, list);
|
||||
test(vList, "[ { x = 0; } «repeated» ]", PrintOptions { });
|
||||
test(vList,
|
||||
"[ { x = 0; } { x = 0; } ]",
|
||||
@@ -716,8 +716,8 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
|
||||
vTwo.mkInt(2);
|
||||
|
||||
BindingsBuilder builder = evaluator.buildBindings(10);
|
||||
builder.insert(evaluator.symbols.create("one"), &vOne);
|
||||
builder.insert(evaluator.symbols.create("two"), &vTwo);
|
||||
builder.insert(evaluator.symbols.create("one"), vOne);
|
||||
builder.insert(evaluator.symbols.create("two"), vTwo);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
@@ -732,7 +732,7 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
|
||||
Value vThree;
|
||||
vThree.mkInt(3);
|
||||
|
||||
builder.insert(evaluator.symbols.create("three"), &vThree);
|
||||
builder.insert(evaluator.symbols.create("three"), vThree);
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
@@ -751,10 +751,11 @@ TEST_F(ValuePrintingTests, ansiColorsListElided)
|
||||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
Value vList = evaluator.mem.newList(4);
|
||||
vList.bigList.elems[0] = &vOne;
|
||||
vList.bigList.elems[1] = &vTwo;
|
||||
vList.bigList.size = 2;
|
||||
auto list = evaluator.mem.newList(4);
|
||||
Value vList{NewValueAs::list, list};
|
||||
list->elems[0] = vOne;
|
||||
list->elems[1] = vTwo;
|
||||
list->size = 2;
|
||||
|
||||
test(vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«1 item elided»" ANSI_NORMAL " ]",
|
||||
@@ -766,8 +767,8 @@ TEST_F(ValuePrintingTests, ansiColorsListElided)
|
||||
Value vThree;
|
||||
vThree.mkInt(3);
|
||||
|
||||
vList.bigList.elems[2] = &vThree;
|
||||
vList.bigList.size = 3;
|
||||
list->elems[2] = vThree;
|
||||
list->size = 3;
|
||||
|
||||
test(vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«2 items elided»" ANSI_NORMAL " ]",
|
||||
@@ -786,7 +787,7 @@ TEST_F(ValuePrintingTests, osc8InAttrSets)
|
||||
|
||||
auto vZero = Value{NewValueAs::integer, NixInt{0}};
|
||||
|
||||
builder.insert(evaluator.symbols.create("x"), &vZero, pos);
|
||||
builder.insert(evaluator.symbols.create("x"), vZero, pos);
|
||||
auto vAttrs = Value{NewValueAs::attrs, builder.finish()};
|
||||
|
||||
auto hyperlink = makeHyperlink("x", makeHyperlinkLocalPath("/dev/null", 1));
|
||||
|
||||
Reference in New Issue
Block a user