lix/src/libexpr/eval.hh

365 lines
11 KiB
C++
Raw Normal View History

#pragma once
#include "attr-set.hh"
#include "value.hh"
#include "nixexpr.hh"
#include "symbol-table.hh"
#include "hash.hh"
#include "config.hh"
#include "function-trace.hh"
#include <map>
2019-10-27 09:15:51 +00:00
#include <optional>
#include <unordered_map>
namespace nix {
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
{
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;
Value * values[0];
};
Value & mkString(Value & v, const string & s, const PathSet & context = PathSet());
2010-03-30 09:22:33 +00:00
void copyContext(const Value & v, PathSet & context);
2010-03-30 09:22:33 +00:00
/* 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;
/* Initialise the Boehm GC, if applicable. */
void initGC();
2013-09-02 14:29:15 +00:00
class EvalState
{
2010-03-31 15:38:03 +00:00
public:
SymbolTable symbols;
2013-10-28 06:34:44 +00:00
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
sFile, sLine, sColumn, sFunctor, sToString,
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
sOutputHash, sOutputHashAlgo, sOutputHashMode;
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. */
2019-02-12 12:43:32 +00:00
std::optional<PathSet> allowedPaths;
Value vEmptySet;
const ref<Store> store;
2010-03-31 15:38:03 +00:00
private:
2013-09-02 14:29:15 +00:00
SrcToStore srcToStore;
/* A cache from path names to parse trees. */
#if HAVE_BOEHMGC
typedef std::map<Path, Expr *, std::less<Path>, traceable_allocator<std::pair<const Path, Expr *> > > FileParseCache;
#else
typedef std::map<Path, Expr *> FileParseCache;
#endif
FileParseCache fileParseCache;
/* A cache from path names to values. */
#if HAVE_BOEHMGC
typedef std::map<Path, Value, std::less<Path>, traceable_allocator<std::pair<const Path, Value> > > FileEvalCache;
#else
typedef std::map<Path, Value> FileEvalCache;
#endif
FileEvalCache fileEvalCache;
SearchPath searchPath;
std::map<std::string, std::pair<bool, std::string>> searchPathResolved;
/* Cache used by checkSourcePath(). */
std::unordered_map<Path, Path> resolvedPaths;
2010-03-30 15:18:20 +00:00
public:
2013-09-02 14:29:15 +00:00
EvalState(const Strings & _searchPath, ref<Store> store);
~EvalState();
void addToSearchPath(const 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);
Add primop ‘scopedImport’ ‘scopedImport’ works like ‘import’, except that it takes a set of attributes to be added to the lexical scope of the expression, essentially extending or overriding the builtin variables. For instance, the expression scopedImport { x = 1; } ./foo.nix where foo.nix contains ‘x’, will evaluate to 1. This has a few applications: * It allows getting rid of function argument specifications in package expressions. For instance, a package expression like: { stdenv, fetchurl, libfoo }: stdenv.mkDerivation { ... buildInputs = [ libfoo ]; } can now we written as just stdenv.mkDerivation { ... buildInputs = [ libfoo ]; } and imported in all-packages.nix as: bar = scopedImport pkgs ./bar.nix; So whereas we once had dependencies listed in three places (buildInputs, the function, and the call site), they now only need to appear in one place. * It allows overriding builtin functions. For instance, to trace all calls to ‘map’: let overrides = { map = f: xs: builtins.trace "map called!" (map f xs); # Ensure that our override gets propagated by calls to # import/scopedImport. import = fn: scopedImport overrides fn; scopedImport = attrs: fn: scopedImport (overrides // attrs) fn; # Also update ‘builtins’. builtins = builtins // overrides; }; in scopedImport overrides ./bla.nix * Similarly, it allows extending the set of builtin functions. For instance, during Nixpkgs/NixOS evaluation, the Nixpkgs library functions could be added to the default scope. There is a downside: calls to scopedImport are not memoized, unlike import. So importing a file multiple times leads to multiple parsings / evaluations. It would be possible to construct the AST only once, but that would require careful handling of variables/environments.
2014-05-26 11:46:11 +00:00
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
/* Parse a Nix expression from the specified string. */
2013-09-02 16:34:04 +00:00
Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv);
Expr * parseExprFromString(const string & s, const Path & basePath);
2013-09-02 14:29:15 +00:00
Expr * parseStdin();
2010-03-30 09:22:33 +00:00
/* Evaluate an expression read from the given file to normal
form. */
void evalFile(const Path & path, Value & v);
2013-09-02 16:34:04 +00:00
void resetFileCache();
/* Look up a file in the search path. */
Path findFile(const string & path);
Path findFile(SearchPath & searchPath, const 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
2010-03-30 13:47:59 +00:00
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. */
2014-04-04 16:58:15 +00:00
NixInt forceInt(Value & v, const Pos & pos);
NixFloat forceFloat(Value & v, const Pos & pos);
2016-08-29 15:56:35 +00:00
bool forceBool(Value & v, const Pos & pos);
inline void forceAttrs(Value & v);
2014-04-04 17:11:40 +00:00
inline void forceAttrs(Value & v, const Pos & pos);
inline void forceList(Value & v);
2014-04-04 17:05:36 +00:00
inline void forceList(Value & v, const Pos & pos);
void forceFunction(Value & v, const Pos & pos); // either lambda or primop
2014-04-04 19:14:11 +00:00
string forceString(Value & v, const Pos & pos = noPos);
2014-11-25 09:23:36 +00:00
string forceString(Value & v, PathSet & context, const Pos & pos = noPos);
2014-04-04 19:14:11 +00:00
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);
2019-10-27 09:15:51 +00:00
std::optional<string> tryAttrsToString(const Pos & pos, Value & v,
PathSet & context, bool coerceMore = false, bool copyToStore = true);
2010-03-30 09:22:33 +00:00
/* 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,
2013-08-14 20:32:49 +00:00
referenced paths are copied to the Nix store as a side effect. */
string coerceToString(const Pos & pos, Value & v, PathSet & context,
2010-03-30 09:22:33 +00:00
bool coerceMore = false, bool copyToStore = true);
2013-11-18 23:03:11 +00:00
string copyPathToStore(PathSet & context, const Path & path);
2010-03-30 09:22:33 +00:00
/* 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);
2010-03-30 09:22:33 +00:00
2013-09-02 16:34:04 +00:00
public:
/* The base environment, containing the builtin functions and
values. */
Env & baseEnv;
2010-04-14 22:59:39 +00:00
/* The same, but used during parsing to resolve variables. */
StaticEnv staticBaseEnv; // !!! should be private
private:
2013-09-02 14:29:15 +00:00
2015-07-23 21:14:07 +00:00
unsigned int baseEnvDispl = 0;
2013-09-02 16:34:04 +00:00
void createBaseEnv();
2013-09-02 14:29:15 +00:00
Value * addConstant(const string & name, Value & v);
2010-03-30 14:39:27 +00:00
Value * addPrimOp(const string & name,
2018-05-02 11:56:34 +00:00
size_t arity, PrimOpFun primOp);
public:
Value & getBuiltin(const string & name);
private:
2013-10-08 12:24:53 +00:00
inline Value * lookupVar(Env * env, const ExprVar & var, bool noEval);
2013-09-02 14:29:15 +00:00
2014-01-21 17:29:55 +00:00
friend struct ExprVar;
friend struct ExprAttrs;
friend struct ExprLet;
2013-09-02 16:34:04 +00:00
Expr * parse(const char * text, const Path & path,
const Path & basePath, StaticEnv & staticEnv);
public:
2013-09-02 14:29:15 +00:00
/* Do a deep equality test between two values. That is, list
elements and attributes are compared recursively. */
bool eqValues(Value & v1, Value & v2);
2015-09-06 23:03:23 +00:00
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);
2013-09-02 14:29:15 +00:00
/* Allocation primitives. */
Value * allocValue();
2018-05-02 11:56:34 +00:00
Env & allocEnv(size_t size);
2010-03-30 14:39:27 +00:00
Value * allocAttr(Value & vAttrs, const Symbol & name);
2018-05-02 11:56:34 +00:00
Bindings * allocBindings(size_t capacity);
2018-05-02 11:56:34 +00:00
void mkList(Value & v, size_t length);
void mkAttrs(Value & v, size_t capacity);
void mkThunk_(Value & v, Expr * expr);
void mkPos(Value & v, Pos * pos);
2018-05-02 11:56:34 +00:00
void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos);
2010-03-30 15:18:20 +00:00
/* Print statistics. */
void printStats();
void realiseContext(const PathSet & context);
private:
2012-08-13 03:41:48 +00:00
2015-07-23 21:14:07 +00:00
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;
2018-05-02 11:56:34 +00:00
typedef std::map<Symbol, size_t> PrimOpCalls;
PrimOpCalls primOpCalls;
2018-05-02 11:56:34 +00:00
typedef std::map<ExprLambda *, size_t> FunctionCalls;
FunctionCalls functionCalls;
2013-11-07 17:04:36 +00:00
void incrFunctionCall(ExprLambda * fun);
2018-05-02 11:56:34 +00:00
typedef std::map<Pos, size_t> AttrSelects;
AttrSelects attrSelects;
2014-01-21 17:29:55 +00:00
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'. */
2010-04-21 15:57:11 +00:00
string showType(const Value & v);
/* Decode a context string !<name>!<path> into a pair <path,
name>. */
std::pair<string, string> decodeContext(const 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);
2014-10-20 16:15:50 +00:00
#ifdef EXCEPTION_NEEDS_THROW_SPEC
~InvalidPathError() throw () { };
#endif
};
struct EvalSettings : Config
{
Setting<bool> enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation",
"Whether builtin functions that allow executing native code should be enabled."};
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;
}