libexpr: move builtin env to new class

Change-Id: Ib76891372be756917bddcf5eaa24ccf8e1288035
This commit is contained in:
eldritch horrors 2024-11-27 02:09:08 +01:00
parent 1ed0814859
commit 1e064e08fa
9 changed files with 146 additions and 110 deletions

View file

@ -148,7 +148,7 @@ static void getAllExprs(EvalState & state,
vArg->mkString(path2.path.abs());
if (seen.size() == maxAttrs)
throw Error("too many Nix expressions in directory '%1%'", path);
attrs.alloc(attrName).mkApp(&state.getBuiltin("import"), vArg);
attrs.alloc(attrName).mkApp(&state.builtins.get("import"), vArg);
}
else if (st.type == InputAccessor::tDirectory)
/* `path2' is a directory (with no default.nix in it);

View file

@ -237,7 +237,7 @@ NixRepl::NixRepl(const SearchPath & searchPath, nix::ref<Store> store, EvalState
: AbstractNixRepl(state)
, debugTraceIndex(0)
, getValues(getValues)
, staticEnv(new StaticEnv(nullptr, state.staticBaseEnv.get()))
, staticEnv(new StaticEnv(nullptr, state.builtins.staticEnv.get()))
, interacter(makeInteracter())
{
}
@ -784,7 +784,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
else if (command == ":doc") {
Value v;
evalString(arg, v);
if (auto doc = state.getDoc(v)) {
if (auto doc = state.builtins.getDoc(v)) {
std::string markdown;
if (!doc->args.empty() && doc->name) {
@ -894,12 +894,12 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
void NixRepl::initEnv()
{
env = &state.mem.allocEnv(envSize);
env->up = &state.baseEnv;
env->up = &state.builtins.env;
displ = 0;
staticEnv->vars.clear();
varNames.clear();
for (auto & i : state.staticBaseEnv->vars)
for (auto & i : state.builtins.staticEnv->vars)
varNames.emplace(state.symbols[i.first]);
}
@ -971,7 +971,7 @@ Value * NixRepl::getReplOverlaysEvalFunction()
auto & expr = state.parseExprFromString(
code,
SourcePath(evalReplInitFilesPath),
state.staticBaseEnv
state.builtins.staticEnv
);
state.eval(expr, **replOverlaysEvalFunction);

View file

@ -1,7 +1,7 @@
---
name: builtins
type: attrs
constructorArgs: [buildBindings(128).finish()]
constructorArgs: [mem.buildBindings(symbols, 128).finish()]
renameInGlobalScope: false
---
Contains all the [built-in functions](@docroot@/language/builtins.md) and values.

View file

@ -1,7 +1,7 @@
---
name: nixPath
type: list
implementation: getNixPath(*this, searchPath)
implementation: prepareNixPath(searchPath)
---
The search path used to resolve angle bracket path lookups.

View file

@ -1,7 +1,7 @@
---
name: storeDir
type: string
constructorArgs: [store->config().storeDir]
constructorArgs: [storeDir]
---
Logical file system location of the [Nix store](@docroot@/glossary.md#gloss-store) currently in use.

View file

@ -262,30 +262,46 @@ EvalMemory::EvalMemory()
assert(libexprInitialised);
}
EvalBuiltins::EvalBuiltins(
EvalMemory & mem,
SymbolTable & symbols,
const SearchPath & searchPath,
const Path & storeDir,
size_t size
)
: mem(mem)
, symbols(symbols)
, env(mem.allocEnv(size))
, staticEnv{std::make_shared<StaticEnv>(nullptr, nullptr)}
{
createBaseEnv(searchPath, storeDir);
}
EvalState::EvalState(
const SearchPath & _searchPath,
ref<Store> store,
std::shared_ptr<Store> buildStore)
: s(symbols)
, searchPath([&] {
SearchPath searchPath;
if (!evalSettings.pureEval) {
for (auto & i : _searchPath.elements)
searchPath.elements.emplace_back(SearchPath::Elem {i});
for (auto & i : evalSettings.nixPath.get())
searchPath.elements.emplace_back(SearchPath::Elem::parse(i));
}
return searchPath;
}())
, builtins(mem, symbols, searchPath, store->config().storeDir)
, repair(NoRepair)
, store(store)
, buildStore(buildStore ? buildStore : store)
, regexCache(makeRegexCache())
, baseEnv(mem.allocEnv(128))
, staticBaseEnv{std::make_shared<StaticEnv>(nullptr, nullptr)}
{
countCalls = getEnv("NIX_COUNT_CALLS").value_or("0") != "0";
static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes");
/* Initialise the Nix expression search path. */
if (!evalSettings.pureEval) {
for (auto & i : _searchPath.elements)
searchPath.elements.emplace_back(SearchPath::Elem {i});
for (auto & i : evalSettings.nixPath.get())
searchPath.elements.emplace_back(SearchPath::Elem::parse(i));
}
if (evalSettings.restrictEval || evalSettings.pureEval) {
allowedPaths = std::optional(PathSet());
@ -308,8 +324,6 @@ EvalState::EvalState(
allowPath(path);
}
}
createBaseEnv();
}
@ -426,7 +440,7 @@ Path EvalState::toRealPath(const Path & path, const NixStringContext & context)
}
Value * EvalState::addConstant(const std::string & name, const Value & v, Constant info)
Value * EvalBuiltins::addConstant(const std::string & name, const Value & v, Constant info)
{
Value * v2 = mem.allocValue();
*v2 = v;
@ -435,7 +449,7 @@ Value * EvalState::addConstant(const std::string & name, const Value & v, Consta
}
void EvalState::addConstant(const std::string & name, Value * v, Constant info)
void EvalBuiltins::addConstant(const std::string & name, Value * v, Constant info)
{
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
@ -450,9 +464,9 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info)
assert(info.type == gotType);
/* Install value the base environment. */
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
staticEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
env.values[baseEnvDispl++] = v;
env.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
}
}
@ -463,7 +477,7 @@ std::ostream & operator<<(std::ostream & output, PrimOp & primOp)
return output;
}
Value * EvalState::addPrimOp(PrimOp && primOp)
Value * EvalBuiltins::addPrimOp(PrimOp && primOp)
{
/* Hack to make constants lazy: turn them into a application of
the primop to a dummy value. */
@ -485,20 +499,20 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
Value * v = mem.allocValue();
v->mkPrimOp(new PrimOp(primOp));
staticBaseEnv->vars.emplace_back(envName, baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v));
staticEnv->vars.emplace_back(envName, baseEnvDispl);
env.values[baseEnvDispl++] = v;
env.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v));
return v;
}
Value & EvalState::getBuiltin(const std::string & name)
Value & EvalBuiltins::get(const std::string & name)
{
return *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
return *env.values[0]->attrs->find(symbols.create(name))->value;
}
std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
std::optional<EvalBuiltins::Doc> EvalBuiltins::getDoc(Value & v)
{
if (v.isPrimOp()) {
auto v2 = &v;
@ -764,7 +778,7 @@ static inline void mkThunk(Value & v, Env & env, Expr & expr)
void EvalState::mkThunk_(Value & v, Expr & expr)
{
mkThunk(v, baseEnv, expr);
mkThunk(v, builtins.env, expr);
}
@ -927,7 +941,7 @@ void EvalState::evalFile(const SourcePath & path_, Value & v)
? makeDebugTraceStacker(
*this,
e,
this->baseEnv,
this->builtins.env,
e.getPos() ? std::make_shared<Pos>(positions[e.getPos()]) : nullptr,
"while evaluating the file '%1%':", resolvedPath.to_string())
: nullptr;
@ -951,7 +965,7 @@ void EvalState::resetFileCache()
void EvalState::eval(Expr & e, Value & v)
{
e.eval(*this, baseEnv, v);
e.eval(*this, builtins.env, v);
}
@ -2667,7 +2681,7 @@ SourcePath resolveExprPath(SourcePath path)
Expr & EvalState::parseExprFromFile(const SourcePath & path)
{
return parseExprFromFile(path, staticBaseEnv);
return parseExprFromFile(path, builtins.staticEnv);
}
@ -2696,7 +2710,7 @@ Expr & EvalState::parseExprFromString(
const FeatureSettings & featureSettings
)
{
return parseExprFromString(std::move(s), basePath, staticBaseEnv, featureSettings);
return parseExprFromString(std::move(s), basePath, builtins.staticEnv, featureSettings);
}
@ -2704,7 +2718,9 @@ Expr & EvalState::parseStdin()
{
//Activity act(*logger, lvlTalkative, "parsing standard input");
auto s = make_ref<std::string>(drainFD(0));
return *parse(s->data(), s->size(), Pos::Stdin{.source = s}, CanonPath::fromCwd(), staticBaseEnv);
return *parse(
s->data(), s->size(), Pos::Stdin{.source = s}, CanonPath::fromCwd(), builtins.staticEnv
);
}

View file

@ -263,15 +263,88 @@ private:
Statistics stats;
};
class EvalBuiltins
{
EvalMemory & mem;
SymbolTable & symbols;
public:
explicit EvalBuiltins(
EvalMemory & mem,
SymbolTable & symbols,
const SearchPath & searchPath,
const Path & storeDir,
size_t size = 128
);
/**
* The base environment, containing the builtin functions and
* values.
*/
Env & env;
/**
* The same, but used during parsing to resolve variables.
*/
std::shared_ptr<StaticEnv> staticEnv; // !!! should be private
/**
* Name and documentation about every constant.
*
* Constants from primops are hard to crawl, and their docs will go
* here too.
*/
std::vector<std::pair<std::string, Constant>> constantInfos;
private:
unsigned int baseEnvDispl = 0;
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, Value * v, Constant info);
Value * addPrimOp(PrimOp && primOp);
Value prepareNixPath(const SearchPath & searchPath);
public:
Value & get(const std::string & name);
struct Doc
{
Pos pos;
std::optional<std::string> name;
size_t arity;
std::vector<std::string> args;
/**
* Unlike the other `doc` fields in this file, this one should never be
* `null`.
*/
const char * doc;
};
std::optional<Doc> getDoc(Value & v);
};
class EvalState
{
friend class EvalBuiltins;
public:
SymbolTable symbols;
PosTable positions;
const StaticSymbols s;
EvalMemory mem;
private:
SearchPath searchPath;
public:
EvalBuiltins builtins;
/**
* If set, force copying files to the Nix store even if they
* already exist there.
@ -318,8 +391,6 @@ private:
using FileEvalCache = GcMap<SourcePath, Value>;
FileEvalCache fileEvalCache;
SearchPath searchPath;
std::map<std::string, std::optional<std::string>> searchPathResolved;
/**
@ -529,58 +600,6 @@ public:
*/
SingleDerivedPath coerceToSingleDerivedPath(const PosIdx pos, Value & v, std::string_view errorCtx);
public:
/**
* The base environment, containing the builtin functions and
* values.
*/
Env & baseEnv;
/**
* The same, but used during parsing to resolve variables.
*/
std::shared_ptr<StaticEnv> staticBaseEnv; // !!! should be private
/**
* Name and documentation about every constant.
*
* Constants from primops are hard to crawl, and their docs will go
* here too.
*/
std::vector<std::pair<std::string, Constant>> constantInfos;
private:
unsigned int baseEnvDispl = 0;
void createBaseEnv();
Value * addConstant(const std::string & name, const Value & v, Constant info);
void addConstant(const std::string & name, Value * v, Constant info);
Value * addPrimOp(PrimOp && primOp);
public:
Value & getBuiltin(const std::string & name);
struct Doc
{
Pos pos;
std::optional<std::string> name;
size_t arity;
std::vector<std::string> args;
/**
* Unlike the other `doc` fields in this file, this one should never be
* `null`.
*/
const char * doc;
};
std::optional<Doc> getDoc(Value & v);
private:
inline Value * lookupVar(Env * env, const ExprVar & var, bool noEval);

View file

@ -231,9 +231,11 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
state.forceAttrs(*vScope, pos, "while evaluating the first argument passed to builtins.scopedImport");
Env * env = &state.mem.allocEnv(vScope->attrs->size());
env->up = &state.baseEnv;
env->up = &state.builtins.env;
auto staticEnv = std::make_shared<StaticEnv>(nullptr, state.staticBaseEnv.get(), vScope->attrs->size());
auto staticEnv = std::make_shared<StaticEnv>(
nullptr, state.builtins.staticEnv.get(), vScope->attrs->size()
);
unsigned int displ = 0;
for (auto & attr : *vScope->attrs) {
@ -1375,7 +1377,7 @@ static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Va
auto epath = state.mem.allocValue();
epath->mkPath(path + name);
if (!readFileType)
readFileType = &state.getBuiltin("readFileType");
readFileType = &state.builtins.get("readFileType");
attr.mkApp(readFileType, epath);
} else {
// This branch of the conditional is much more likely.
@ -2785,23 +2787,23 @@ RegisterPrimOp::RegisterPrimOp(PrimOp && primOp)
}
static Value getNixPath(EvalState & state, SearchPath & searchPath)
Value EvalBuiltins::prepareNixPath(const SearchPath & searchPath)
{
Value v;
v = state.mem.newList(searchPath.elements.size());
v = mem.newList(searchPath.elements.size());
int n = 0;
for (auto & i : searchPath.elements) {
auto attrs = state.buildBindings(2);
auto attrs = mem.buildBindings(symbols, 2);
attrs.alloc("path").mkString(i.path.s);
attrs.alloc("prefix").mkString(i.prefix.s);
(v.listElems()[n++] = state.mem.allocValue())->mkAttrs(attrs);
(v.listElems()[n++] = mem.allocValue())->mkAttrs(attrs);
}
return v;
}
void EvalState::createBaseEnv()
void EvalBuiltins::createBaseEnv(const SearchPath & searchPath, const Path & storeDir)
{
baseEnv.up = 0;
env.up = 0;
// constants include the magic `builtins` which must come first
#include "register-builtin-constants.gen.inc"
@ -2838,7 +2840,7 @@ void EvalState::createBaseEnv()
#include "primops/derivation.nix.gen.hh"
;
auto & expr = *state.parse(
code, sizeof(code), Pos::Hidden{}, {CanonPath::root}, state.staticBaseEnv
code, sizeof(code), Pos::Hidden{}, {CanonPath::root}, state.builtins.staticEnv
);
state.eval(expr, v);
},
@ -2857,11 +2859,10 @@ 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]->attrs->sort();
staticBaseEnv->sort();
staticBaseEnv->isRoot = true;
env.values[0]->attrs->sort();
staticEnv->sort();
staticEnv->isRoot = true;
}

View file

@ -265,7 +265,7 @@ static void showHelp(std::vector<std::string> subcommand, NixArgs & toplevel)
vDump->mkString(toplevel.dumpCli());
auto vRes = state.mem.allocValue();
state.callFunction(*vGenerateManpage, state.getBuiltin("false"), *vRes, noPos);
state.callFunction(*vGenerateManpage, state.builtins.get("false"), *vRes, noPos);
state.callFunction(*vRes, *vDump, *vRes, noPos);
auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md"));
@ -414,7 +414,7 @@ void mainWrapped(int argc, char * * argv)
auto res = nlohmann::json::object();
res["builtins"] = ({
auto builtinsJson = nlohmann::json::object();
auto builtins = state.baseEnv.values[0]->attrs;
auto builtins = state.builtins.env.values[0]->attrs;
for (auto & builtin : *builtins) {
auto b = nlohmann::json::object();
if (!builtin.value->isPrimOp()) continue;
@ -430,7 +430,7 @@ void mainWrapped(int argc, char * * argv)
});
res["constants"] = ({
auto constantsJson = nlohmann::json::object();
for (auto & [name, info] : state.constantInfos) {
for (auto & [name, info] : state.builtins.constantInfos) {
auto c = nlohmann::json::object();
if (!info.doc) continue;
c["doc"] = trim(stripIndentation(info.doc));