diff --git a/src/libexpr-test-support/tests/libexpr.hh b/src/libexpr-test-support/tests/libexpr.hh index 045607e87..095ea1d0e 100644 --- a/src/libexpr-test-support/tests/libexpr.hh +++ b/src/libexpr-test-support/tests/libexpr.hh @@ -40,6 +40,12 @@ namespace nix { return v; } + Value * maybeThunk(std::string input, bool forceValue = true) { + Expr * e = state.parseExprFromString(input, state.rootPath(CanonPath::root)); + assert(e); + return e->maybeThunk(state, state.baseEnv); + } + Symbol createSymbol(const char * value) { return state.symbols.create(value); } diff --git a/src/libexpr-tests/eval.cc b/src/libexpr-tests/eval.cc index 93d3f658f..61f6be0db 100644 --- a/src/libexpr-tests/eval.cc +++ b/src/libexpr-tests/eval.cc @@ -138,4 +138,27 @@ TEST(nix_isAllowedURI, non_scheme_colon) { ASSERT_FALSE(isAllowedURI("https://foo/bar:baz", allowed)); } -} // namespace nix \ No newline at end of file +class EvalStateTest : public LibExprTest {}; + +TEST_F(EvalStateTest, getBuiltins_ok) { + auto evaled = maybeThunk("builtins"); + auto & builtins = state.getBuiltins(); + ASSERT_TRUE(builtins.type() == nAttrs); + ASSERT_EQ(evaled, &builtins); +} + +TEST_F(EvalStateTest, getBuiltin_ok) { + auto & builtin = state.getBuiltin("toString"); + ASSERT_TRUE(builtin.type() == nFunction); + // FIXME + // auto evaled = maybeThunk("builtins.toString"); + // ASSERT_EQ(evaled, &builtin); + auto & builtin2 = state.getBuiltin("true"); + ASSERT_EQ(state.forceBool(builtin2, noPos, "in unit test"), true); +} + +TEST_F(EvalStateTest, getBuiltin_fail) { + ASSERT_THROW(state.getBuiltin("nonexistent"), EvalError); +} + +} // namespace nix diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 83a1cf4e9..2fe2d5249 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -448,7 +448,7 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info) /* Install value the base environment. */ staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl); baseEnv.values[baseEnvDispl++] = v; - baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(name2), v)); + getBuiltins().payload.attrs->push_back(Attr(symbols.create(name2), v)); } } @@ -516,16 +516,26 @@ Value * EvalState::addPrimOp(PrimOp && primOp) else { staticBaseEnv->vars.emplace_back(envName, baseEnvDispl); baseEnv.values[baseEnvDispl++] = v; - baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(primOp.name), v)); + getBuiltins().payload.attrs->push_back(Attr(symbols.create(primOp.name), v)); } return v; } +Value & EvalState::getBuiltins() +{ + return *baseEnv.values[0]; +} + + Value & EvalState::getBuiltin(const std::string & name) { - return *baseEnv.values[0]->attrs()->find(symbols.create(name))->value; + auto it = getBuiltins().attrs()->get(symbols.create(name)); + if (it) + return *it->value; + else + error("builtin '%1%' not found", name).debugThrow(); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 6c0bae451..107334ffe 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -623,8 +623,19 @@ private: public: + /** + * Retrieve a specific builtin, equivalent to evaluating `builtins.${name}`. + * @param name The attribute name of the builtin to retrieve. + * @throws EvalError if the builtin does not exist. + */ Value & getBuiltin(const std::string & name); + /** + * Retrieve the `builtins` attrset, equivalent to evaluating the reference `builtins`. + * Always returns an attribute set value. + */ + Value & getBuiltins(); + struct Doc { Pos pos; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 42136b467..503632328 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -4938,7 +4938,7 @@ void EvalState::createBaseEnv() /* Now that we've added all primops, sort the `builtins' set, because attribute lookups expect it to be sorted. */ - baseEnv.values[0]->payload.attrs->sort(); + getBuiltins().payload.attrs->sort(); staticBaseEnv->sort(); diff --git a/src/nix/main.cc b/src/nix/main.cc index eff2d60a4..b0e26e093 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -435,7 +435,8 @@ void mainWrapped(int argc, char * * argv) evalSettings.pureEval = false; EvalState state({}, openStore("dummy://"), fetchSettings, evalSettings); auto builtinsJson = nlohmann::json::object(); - for (auto & builtin : *state.baseEnv.values[0]->attrs()) { + for (auto & builtinPtr : state.getBuiltins().attrs()->lexicographicOrder(state.symbols)) { + auto & builtin = *builtinPtr; auto b = nlohmann::json::object(); if (!builtin.value->isPrimOp()) continue; auto primOp = builtin.value->primOp();