about summary refs log tree commit diff
path: root/third_party/nix/src/libexpr/eval.hh
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/nix/src/libexpr/eval.hh')
-rw-r--r--third_party/nix/src/libexpr/eval.hh365
1 files changed, 365 insertions, 0 deletions
diff --git a/third_party/nix/src/libexpr/eval.hh b/third_party/nix/src/libexpr/eval.hh
new file mode 100644
index 000000000000..0352a89a2ab8
--- /dev/null
+++ b/third_party/nix/src/libexpr/eval.hh
@@ -0,0 +1,365 @@
+#pragma once
+
+#include <map>
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
+#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 {
+  Env(unsigned short size) : size(size) { values = std::vector<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*> 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. */
+using SrcToStore = std::map<Path, Path>;
+
+std::ostream& operator<<(std::ostream& str, const Value& v);
+
+using SearchPathElem = std::pair<std::string, std::string>;
+using SearchPath = std::list<SearchPathElem>;
+
+using FileParseCache = std::map<Path, Expr*>;
+
+class EvalState {
+ 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. */
+  using FileEvalCache = std::map<Path, Value>;
+  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.  'args' need
+     not live past the end of the call. */
+  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, std::shared_ptr<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);
+
+  /* File access tracing. */
+  void TraceFileAccess(const Path& path);
+  void EnableFileAccessTracing(std::function<void(const Path&)> fn);
+
+ 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;
+
+  std::function<void(const Path&)> file_access_trace_fn = nullptr;
+  Path last_traced_file = "";
+
+  using PrimOpCalls = std::map<Symbol, size_t>;
+  PrimOpCalls primOpCalls;
+
+  using FunctionCalls = std::map<ExprLambda*, size_t>;
+  FunctionCalls functionCalls;
+
+  void incrFunctionCall(ExprLambda* fun);
+
+  using AttrSelects = std::map<Pos, size_t>;
+  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