#pragma once
#include <map>
#include <optional>
#include <unordered_map>
#include <vector>
#include <gc/gc_allocator.h>
#include <gc/gc_cpp.h>
#include "libexpr/attr-set.hh"
#include "libexpr/nixexpr.hh"
#include "libexpr/symbol-table.hh"
#include "libexpr/value.hh"
#include "libutil/config.hh"
#include "libutil/hash.hh"
namespace nix {
namespace expr {
// Initialise the Boehm GC once per program instance. This should be
// called in places that require the garbage collector.
void InitGC();
} // namespace expr
class Store;
class EvalState;
enum RepairFlag : bool;
typedef void (*PrimOpFun)(EvalState& state, const Pos& pos, Value** args,
Value& v);
struct PrimOp {
PrimOpFun fun;
size_t arity;
Symbol name;
PrimOp(PrimOpFun fun, size_t arity, Symbol name)
: fun(fun), arity(arity), name(name) {}
};
struct Env : public gc {
Env(unsigned short size) : size(size) {
values = std::vector<Value*, traceable_allocator<Value*>>(size);
}
Env* up;
unsigned short size; // used by ‘valueSize’
unsigned short prevWith : 14; // nr of levels up to next `with' environment
enum { Plain = 0, HasWithExpr, HasWithAttrs } type : 2;
std::vector<Value*, traceable_allocator<Value*>> values;
Expr* withAttrsExpr = nullptr;
};
Value& mkString(Value& v, const std::string& s,
const PathSet& context = PathSet());
void copyContext(const Value& v, PathSet& context);
/* Cache for calls to addToStore(); maps source paths to the store
paths. */
typedef std::map<Path, Path> SrcToStore;
std::ostream& operator<<(std::ostream& str, const Value& v);
typedef std::pair<std::string, std::string> SearchPathElem;
typedef std::list<SearchPathElem> SearchPath;
typedef std::map<Path, Expr*, std::less<Path>,
traceable_allocator<std::pair<const Path, Expr*>>>
FileParseCache;
class EvalState : public gc {
public:
SymbolTable symbols;
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, sSystem,
sOutputs, sOutputName, sIgnoreNulls, sFile, sLine, sColumn, sFunctor,
sToString, sRight, sWrong, sStructuredAttrs, sBuilder, sArgs, sOutputHash,
sOutputHashAlgo, sOutputHashMode;
// Symbol representing the path to the built-in 'derivation.nix'
// file, set during primops initialisation.
std::optional<Symbol> sDerivationNix;
/* If set, force copying files to the Nix store even if they
already exist there. */
RepairFlag repair;
/* The allowed filesystem paths in restricted or pure evaluation
mode. */
std::optional<PathSet> allowedPaths;
const ref<Store> store;
private:
SrcToStore srcToStore;
/* A cache from path names to parse trees. */
FileParseCache fileParseCache;
/* A cache from path names to values. */
typedef std::map<Path, Value, std::less<Path>,
traceable_allocator<std::pair<const Path, Value>>>
FileEvalCache;
FileEvalCache fileEvalCache;
SearchPath searchPath;
std::map<std::string, std::pair<bool, std::string>> searchPathResolved;
/* Cache used by checkSourcePath(). */
std::unordered_map<Path, Path> resolvedPaths;
public:
EvalState(const Strings& _searchPath, const ref<Store>& store);
~EvalState();
void addToSearchPath(const std::string& s);
SearchPath getSearchPath() { return searchPath; }
Path checkSourcePath(const Path& path);
void checkURI(const std::string& uri);
/* When using a diverted store and 'path' is in the Nix store, map
'path' to the diverted location (e.g. /nix/store/foo is mapped
to /home/alice/my-nix/nix/store/foo). However, this is only
done if the context is not empty, since otherwise we're
probably trying to read from the actual /nix/store. This is
intended to distinguish between import-from-derivation and
sources stored in the actual /nix/store. */
Path toRealPath(const Path& path, const PathSet& context);
/* Parse a Nix expression from the specified file. */
Expr* parseExprFromFile(const Path& path);
Expr* parseExprFromFile(const Path& path, StaticEnv& staticEnv);
/* Parse a Nix expression from the specified string. */
Expr* parseExprFromString(const std::string& s, const Path& basePath,
StaticEnv& staticEnv);
Expr* parseExprFromString(const std::string& s, const Path& basePath);
Expr* parseStdin();
/* Evaluate an expression read from the given file to normal
form. */
void evalFile(const Path& path, Value& v);
void resetFileCache();
/* Look up a file in the search path. */
Path findFile(const std::string& path);
Path findFile(SearchPath& searchPath, const std::string& path,
const Pos& pos = noPos);
/* If the specified search path element is a URI, download it. */
std::pair<bool, std::string> resolveSearchPathElem(
const SearchPathElem& elem);
/* Evaluate an expression to normal form, storing the result in
value `v'. */
void eval(Expr* e, Value& v);
/* Evaluation the expression, then verify that it has the expected
type. */
inline bool evalBool(Env& env, Expr* e);
inline bool evalBool(Env& env, Expr* e, const Pos& pos);
inline void evalAttrs(Env& env, Expr* e, Value& v);
/* If `v' is a thunk, enter it and overwrite `v' with the result
of the evaluation of the thunk. If `v' is a delayed function
application, call the function and overwrite `v' with the
result. Otherwise, this is a no-op. */
inline void forceValue(Value& v, const Pos& pos = noPos);
/* Force a value, then recursively force list elements and
attributes. */
void forceValueDeep(Value& v);
/* Force `v', and then verify that it has the expected type. */
NixInt forceInt(Value& v, const Pos& pos);
NixFloat forceFloat(Value& v, const Pos& pos);
bool forceBool(Value& v, const Pos& pos);
inline void forceAttrs(Value& v);
inline void forceAttrs(Value& v, const Pos& pos);
inline void forceList(Value& v);
inline void forceList(Value& v, const Pos& pos);
void forceFunction(Value& v, const Pos& pos); // either lambda or primop
std::string forceString(Value& v, const Pos& pos = noPos);
std::string forceString(Value& v, PathSet& context, const Pos& pos = noPos);
std::string forceStringNoCtx(Value& v, const Pos& pos = noPos);
/* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */
bool isDerivation(Value& v);
std::optional<std::string> tryAttrsToString(const Pos& pos, Value& v,
PathSet& context,
bool coerceMore = false,
bool copyToStore = true);
/* String coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect. */
std::string coerceToString(const Pos& pos, Value& v, PathSet& context,
bool coerceMore = false, bool copyToStore = true);
std::string copyPathToStore(PathSet& context, const Path& path);
/* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */
Path coerceToPath(const Pos& pos, Value& v, PathSet& context);
public:
/* The base environment, containing the builtin functions and
values. */
Env& baseEnv;
/* The same, but used during parsing to resolve variables. */
StaticEnv staticBaseEnv; // !!! should be private
private:
unsigned int baseEnvDispl = 0;
void createBaseEnv();
Value* addConstant(const std::string& name, Value& v);
Value* addPrimOp(const std::string& name, size_t arity, PrimOpFun primOp);
public:
Value& getBuiltin(const std::string& name);
private:
inline Value* lookupVar(Env* env, const ExprVar& var, bool noEval);
friend struct ExprVar;
friend struct ExprAttrs;
friend struct ExprLet;
Expr* parse(const char* text, const Path& path, const Path& basePath,
StaticEnv& staticEnv);
public:
/* Do a deep equality test between two values. That is, list
elements and attributes are compared recursively. */
bool eqValues(Value& v1, Value& v2);
bool isFunctor(Value& fun);
void callFunction(Value& fun, Value& arg, Value& v, const Pos& pos);
void callPrimOp(Value& fun, Value& arg, Value& v, const Pos& pos);
/* Automatically call a function for which each argument has a
default value or has a binding in the `args' map. */
void autoCallFunction(Bindings& args, Value& fun, Value& res);
/* Allocation primitives. */
Value* allocValue();
Env& allocEnv(size_t size);
Value* allocAttr(Value& vAttrs, const Symbol& name);
// Create a list value from the specified vector.
void mkList(Value& v, NixList* list);
// Create a list value, allocating as many elements as specified in
// size. This is used for the many cases in this codebase where
// assignment happens into the preallocated list.
void mkList(Value& v, size_t size = 0);
void mkAttrs(Value& v, size_t capacity);
void mkThunk_(Value& v, Expr* expr);
void mkPos(Value& v, Pos* pos);
void concatLists(Value& v, const NixList& lists, const Pos& pos);
/* Print statistics. */
void printStats();
void realiseContext(const PathSet& context);
private:
unsigned long nrEnvs = 0;
unsigned long nrValuesInEnvs = 0;
unsigned long nrValues = 0;
unsigned long nrListElems = 0;
unsigned long nrAttrsets = 0;
unsigned long nrAttrsInAttrsets = 0;
unsigned long nrOpUpdates = 0;
unsigned long nrOpUpdateValuesCopied = 0;
unsigned long nrListConcats = 0;
unsigned long nrPrimOpCalls = 0;
unsigned long nrFunctionCalls = 0;
bool countCalls;
typedef std::map<Symbol, size_t> PrimOpCalls;
PrimOpCalls primOpCalls;
typedef std::map<ExprLambda*, size_t> FunctionCalls;
FunctionCalls functionCalls;
void incrFunctionCall(ExprLambda* fun);
typedef std::map<Pos, size_t> AttrSelects;
AttrSelects attrSelects;
friend struct ExprOpUpdate;
friend struct ExprOpConcatLists;
friend struct ExprSelect;
friend void prim_getAttr(EvalState& state, const Pos& pos, Value** args,
Value& v);
};
/* Return a string representing the type of the value `v'. */
std::string showType(const Value& v);
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
name>. */
std::pair<std::string, std::string> decodeContext(const std::string& s);
/* If `path' refers to a directory, then append "/default.nix". */
Path resolveExprPath(Path path);
struct InvalidPathError : EvalError {
Path path;
InvalidPathError(const Path& path);
#ifdef EXCEPTION_NEEDS_THROW_SPEC
~InvalidPathError() noexcept {};
#endif
};
struct EvalSettings : Config {
Setting<bool> restrictEval{
this, false, "restrict-eval",
"Whether to restrict file system access to paths in $NIX_PATH, "
"and network access to the URI prefixes listed in 'allowed-uris'."};
Setting<bool> pureEval{this, false, "pure-eval",
"Whether to restrict file system and network access "
"to files specified by cryptographic hash."};
Setting<bool> enableImportFromDerivation{
this, true, "allow-import-from-derivation",
"Whether the evaluator allows importing the result of a derivation."};
Setting<Strings> allowedUris{
this,
{},
"allowed-uris",
"Prefixes of URIs that builtin functions such as fetchurl and fetchGit "
"are allowed to fetch."};
Setting<bool> traceFunctionCalls{this, false, "trace-function-calls",
"Emit log messages for each function entry "
"and exit at the 'vomit' log level (-vvvv)"};
};
extern EvalSettings evalSettings;
} // namespace nix