about summary refs log tree commit diff
path: root/src/libexpr
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/eval.cc28
-rw-r--r--src/libexpr/eval.hh15
-rw-r--r--src/libexpr/get-drvs.cc2
-rw-r--r--src/libexpr/json-to-value.cc9
-rw-r--r--src/libexpr/primops.cc101
-rw-r--r--src/libexpr/value-to-json.cc55
-rw-r--r--src/libexpr/value-to-json.hh71
-rw-r--r--src/libexpr/value.hh17
8 files changed, 153 insertions, 145 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 0833603b2a9e..64f3874db614 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -293,6 +293,8 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
     , sColumn(symbols.create("column"))
     , sFunctor(symbols.create("__functor"))
     , sToString(symbols.create("__toString"))
+    , sRight(symbols.create("right"))
+    , sWrong(symbols.create("wrong"))
     , store(store)
     , baseEnv(allocEnv(128))
     , staticBaseEnv(false, 0)
@@ -379,9 +381,9 @@ void EvalState::addPrimOp(const string & name,
 }
 
 
-void EvalState::getBuiltin(const string & name, Value & v)
+Value & EvalState::getBuiltin(const string & name)
 {
-    v = *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
+    return *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
 }
 
 
@@ -462,7 +464,7 @@ void mkString(Value & v, const char * s)
 }
 
 
-void mkString(Value & v, const string & s, const PathSet & context)
+Value & mkString(Value & v, const string & s, const PathSet & context)
 {
     mkString(v, s.c_str());
     if (!context.empty()) {
@@ -473,6 +475,7 @@ void mkString(Value & v, const string & s, const PathSet & context)
             v.string.context[n++] = dupString(i.c_str());
         v.string.context[n] = 0;
     }
+    return v;
 }
 
 
@@ -993,11 +996,18 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
     if (fun.type == tAttrs) {
       auto found = fun.attrs->find(sFunctor);
       if (found != fun.attrs->end()) {
+        /* fun may be allocated on the stack of the calling function,
+         * but for functors we may keep a reference, so heap-allocate
+         * a copy and use that instead.
+         */
+        auto & fun2 = *allocValue();
+        fun2 = fun;
+        /* !!! Should we use the attr pos here? */
         forceValue(*found->value, pos);
-        Value * v2 = allocValue();
-        callFunction(*found->value, fun, *v2, pos);
-        forceValue(*v2, pos);
-        return callFunction(*v2, arg, v, pos);
+        Value v2;
+        callFunction(*found->value, fun2, v2, pos);
+        forceValue(v2, pos);
+        return callFunction(v2, arg, v, pos);
       }
     }
 
@@ -1368,11 +1378,11 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
 }
 
 
-bool EvalState::forceBool(Value & v)
+bool EvalState::forceBool(Value & v, const Pos & pos)
 {
     forceValue(v);
     if (v.type != tBool)
-        throwTypeError("value is %1% while a Boolean was expected", v);
+        throwTypeError("value is %1% while a Boolean was expected, at %2%", v, pos);
     return v.boolean;
 }
 
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 80e369f2d68f..195cb0db3acc 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -8,10 +8,6 @@
 
 #include <map>
 
-#if HAVE_BOEHMGC
-#include <gc/gc_allocator.h>
-#endif
-
 
 namespace nix {
 
@@ -43,7 +39,7 @@ struct Env
 };
 
 
-void mkString(Value & v, const string & s, const PathSet & context = PathSet());
+Value & mkString(Value & v, const string & s, const PathSet & context = PathSet());
 
 void copyContext(const Value & v, PathSet & context);
 
@@ -71,7 +67,8 @@ public:
 
     const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
         sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
-        sFile, sLine, sColumn, sFunctor, sToString;
+        sFile, sLine, sColumn, sFunctor, sToString,
+        sRight, sWrong;
     Symbol sDerivationNix;
 
     /* If set, force copying files to the Nix store even if they
@@ -108,6 +105,8 @@ public:
 
     void addToSearchPath(const string & s);
 
+    SearchPath getSearchPath() { return searchPath; }
+
     Path checkSourcePath(const Path & path);
 
     /* Parse a Nix expression from the specified file. */
@@ -154,7 +153,7 @@ public:
     /* 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);
+    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);
@@ -204,7 +203,7 @@ private:
 
 public:
 
-    void getBuiltin(const string & name, Value & v);
+    Value & getBuiltin(const string & name);
 
 private:
 
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index b06c539de0fb..dc5def911ca0 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -301,7 +301,7 @@ static void getDerivations(EvalState & state, Value & vIn,
                    `recurseForDerivations = true' attribute. */
                 if (v2.type == tAttrs) {
                     Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
-                    if (j != v2.attrs->end() && state.forceBool(*j->value))
+                    if (j != v2.attrs->end() && state.forceBool(*j->value, *j->pos))
                         getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
                 }
             }
diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc
index 1daf84600dca..f671802bcc24 100644
--- a/src/libexpr/json-to-value.cc
+++ b/src/libexpr/json-to-value.cc
@@ -12,15 +12,6 @@ static void skipWhitespace(const char * & s)
 }
 
 
-#if HAVE_BOEHMGC
-typedef std::vector<Value *, gc_allocator<Value *> > ValueVector;
-typedef std::map<Symbol, Value *, std::less<Symbol>, gc_allocator<Value *> > ValueMap;
-#else
-typedef std::vector<Value *> ValueVector;
-typedef std::map<Symbol, Value *> ValueMap;
-#endif
-
-
 static string parseJSONString(const char * & s)
 {
     string res;
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index c456e9b96a53..3b965f209bb2 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -477,7 +477,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
     bool ignoreNulls = false;
     attr = args[0]->attrs->find(state.sIgnoreNulls);
     if (attr != args[0]->attrs->end())
-        ignoreNulls = state.forceBool(*attr->value);
+        ignoreNulls = state.forceBool(*attr->value, pos);
 
     /* Build the derivation expression by processing the attributes. */
     Derivation drv;
@@ -673,6 +673,19 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
 }
 
 
+/* Return a placeholder string for the specified output that will be
+   substituted by the corresponding output path at build time. For
+   example, ‘placeholder "out"’ returns the string
+   /1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9. At build
+   time, any occurence of this string in an derivation attribute will
+   be replaced with the concrete path in the Nix store of the output
+   ‘out’. */
+static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+    mkString(v, hashPlaceholder(state.forceStringNoCtx(*args[0], pos)));
+}
+
+
 /*************************************************************
  * Paths
  *************************************************************/
@@ -912,9 +925,10 @@ struct FilterFromExpr : PathFilter
 {
     EvalState & state;
     Value & filter;
+    Pos pos;
 
-    FilterFromExpr(EvalState & state, Value & filter)
-        : state(state), filter(filter)
+    FilterFromExpr(EvalState & state, Value & filter, const Pos & pos)
+        : state(state), filter(filter), pos(pos)
     {
     }
 
@@ -942,7 +956,7 @@ struct FilterFromExpr : PathFilter
         Value res;
         state.callFunction(fun2, arg2, res, noPos);
 
-        return state.forceBool(res);
+        return state.forceBool(res, pos);
     }
 };
 
@@ -958,7 +972,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
     if (args[0]->type != tLambda)
         throw TypeError(format("first argument in call to ‘filterSource’ is not a function but %1%, at %2%") % showType(*args[0]) % pos);
 
-    FilterFromExpr filter(state, *args[0]);
+    FilterFromExpr filter(state, *args[0], pos);
 
     path = state.checkSourcePath(path);
 
@@ -1278,7 +1292,7 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu
     for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
         Value res;
         state.callFunction(*args[0], *args[1]->listElems()[n], res, noPos);
-        if (state.forceBool(res))
+        if (state.forceBool(res, pos))
             vs[k++] = args[1]->listElems()[n];
         else
             same = false;
@@ -1354,7 +1368,7 @@ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * arg
     Value vTmp;
     for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
         state.callFunction(*args[0], *args[1]->listElems()[n], vTmp, pos);
-        bool res = state.forceBool(vTmp);
+        bool res = state.forceBool(vTmp, pos);
         if (res == any) {
             mkBool(v, any);
             return;
@@ -1420,7 +1434,7 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value
         Value vTmp1, vTmp2;
         state.callFunction(*args[0], *a, vTmp1, pos);
         state.callFunction(vTmp1, *b, vTmp2, pos);
-        return state.forceBool(vTmp2);
+        return state.forceBool(vTmp2, pos);
     };
 
     /* FIXME: std::sort can segfault if the comparator is not a strict
@@ -1430,6 +1444,40 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value
 }
 
 
+static void prim_partition(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+    state.forceFunction(*args[0], pos);
+    state.forceList(*args[1], pos);
+
+    auto len = args[1]->listSize();
+
+    ValueVector right, wrong;
+
+    for (unsigned int n = 0; n < len; ++n) {
+        auto vElem = args[1]->listElems()[n];
+        state.forceValue(*vElem);
+        Value res;
+        state.callFunction(*args[0], *vElem, res, pos);
+        if (state.forceBool(res, pos))
+            right.push_back(vElem);
+        else
+            wrong.push_back(vElem);
+    }
+
+    state.mkAttrs(v, 2);
+
+    Value * vRight = state.allocAttr(v, state.sRight);
+    state.mkList(*vRight, right.size());
+    memcpy(vRight->listElems(), right.data(), sizeof(Value *) * right.size());
+
+    Value * vWrong = state.allocAttr(v, state.sWrong);
+    state.mkList(*vWrong, wrong.size());
+    memcpy(vWrong->listElems(), wrong.data(), sizeof(Value *) * wrong.size());
+
+    v.attrs->sort();
+}
+
+
 /*************************************************************
  * Integer arithmetic
  *************************************************************/
@@ -1620,13 +1668,18 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
     if (args[0]->listSize() != args[1]->listSize())
         throw EvalError(format("‘from’ and ‘to’ arguments to ‘replaceStrings’ have different lengths, at %1%") % pos);
 
-    Strings from;
+    vector<string> from;
+    from.reserve(args[0]->listSize());
     for (unsigned int n = 0; n < args[0]->listSize(); ++n)
-        from.push_back(state.forceStringNoCtx(*args[0]->listElems()[n], pos));
+        from.push_back(state.forceString(*args[0]->listElems()[n], pos));
 
-    Strings to;
-    for (unsigned int n = 0; n < args[1]->listSize(); ++n)
-        to.push_back(state.forceStringNoCtx(*args[1]->listElems()[n], pos));
+    vector<std::pair<string, PathSet>> to;
+    to.reserve(args[1]->listSize());
+    for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
+        PathSet ctx;
+        auto s = state.forceString(*args[1]->listElems()[n], ctx, pos);
+        to.push_back(std::make_pair(std::move(s), std::move(ctx)));
+    }
 
     PathSet context;
     auto s = state.forceString(*args[2], context, pos);
@@ -1634,11 +1687,16 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
     string res;
     for (size_t p = 0; p < s.size(); ) {
         bool found = false;
-        for (auto i = from.begin(), j = to.begin(); i != from.end(); ++i, ++j)
+        auto i = from.begin();
+        auto j = to.begin();
+        for (; i != from.end(); ++i, ++j)
             if (s.compare(p, i->size(), *i) == 0) {
                 found = true;
                 p += i->size();
-                res += *j;
+                res += j->first;
+                for (auto& path : j->second)
+                    context.insert(path);
+                j->second.clear();
                 break;
             }
         if (!found) res += s[p++];
@@ -1682,6 +1740,7 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
 {
     string url;
     Hash expectedHash;
+    string name;
 
     state.forceValue(*args[0]);
 
@@ -1690,11 +1749,13 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
         state.forceAttrs(*args[0], pos);
 
         for (auto & attr : *args[0]->attrs) {
-            string name(attr.name);
-            if (name == "url")
+            string n(attr.name);
+            if (n == "url")
                 url = state.forceStringNoCtx(*attr.value, *attr.pos);
-            else if (name == "sha256")
+            else if (n == "sha256")
                 expectedHash = parseHash16or32(htSHA256, state.forceStringNoCtx(*attr.value, *attr.pos));
+            else if (n == "name")
+                name = state.forceStringNoCtx(*attr.value, *attr.pos);
             else
                 throw EvalError(format("unsupported argument ‘%1%’ to ‘%2%’, at %3%") % attr.name % who % attr.pos);
         }
@@ -1708,7 +1769,7 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
     if (state.restricted && !expectedHash)
         throw Error(format("‘%1%’ is not allowed in restricted mode") % who);
 
-    Path res = makeDownloader()->downloadCached(state.store, url, unpack, expectedHash);
+    Path res = makeDownloader()->downloadCached(state.store, url, unpack, name, expectedHash);
     mkString(v, res, PathSet({res}));
 }
 
@@ -1855,6 +1916,7 @@ void EvalState::createBaseEnv()
     addPrimOp("__all", 2, prim_all);
     addPrimOp("__genList", 2, prim_genList);
     addPrimOp("__sort", 2, prim_sort);
+    addPrimOp("__partition", 2, prim_partition);
 
     // Integer arithmetic
     addPrimOp("__add", 2, prim_add);
@@ -1880,6 +1942,7 @@ void EvalState::createBaseEnv()
 
     // Derivations
     addPrimOp("derivationStrict", 1, prim_derivationStrict);
+    addPrimOp("placeholder", 1, prim_placeholder);
 
     // Networking
     addPrimOp("__fetchurl", 1, prim_fetchurl);
diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc
index 47ee324a6e4f..72e413e4491e 100644
--- a/src/libexpr/value-to-json.cc
+++ b/src/libexpr/value-to-json.cc
@@ -1,4 +1,5 @@
 #include "value-to-json.hh"
+#include "json.hh"
 #include "eval-inline.hh"
 #include "util.hh"
 
@@ -8,24 +9,8 @@
 
 namespace nix {
 
-
-void escapeJSON(std::ostream & str, const string & s)
-{
-    str << "\"";
-    for (auto & i : s)
-        if (i == '\"' || i == '\\') str << "\\" << i;
-        else if (i == '\n') str << "\\n";
-        else if (i == '\r') str << "\\r";
-        else if (i == '\t') str << "\\t";
-        else if (i >= 0 && i < 32)
-            str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) i << std::dec;
-        else str << i;
-    str << "\"";
-}
-
-
 void printValueAsJSON(EvalState & state, bool strict,
-    Value & v, std::ostream & str, PathSet & context)
+    Value & v, JSONPlaceholder & out, PathSet & context)
 {
     checkInterrupt();
 
@@ -34,58 +19,58 @@ void printValueAsJSON(EvalState & state, bool strict,
     switch (v.type) {
 
         case tInt:
-            str << v.integer;
+            out.write(v.integer);
             break;
 
         case tBool:
-            str << (v.boolean ? "true" : "false");
+            out.write(v.boolean);
             break;
 
         case tString:
             copyContext(v, context);
-            escapeJSON(str, v.string.s);
+            out.write(v.string.s);
             break;
 
         case tPath:
-            escapeJSON(str, state.copyPathToStore(context, v.path));
+            out.write(state.copyPathToStore(context, v.path));
             break;
 
         case tNull:
-            str << "null";
+            out.write(nullptr);
             break;
 
         case tAttrs: {
             Bindings::iterator i = v.attrs->find(state.sOutPath);
             if (i == v.attrs->end()) {
-                JSONObject json(str);
+                auto obj(out.object());
                 StringSet names;
                 for (auto & j : *v.attrs)
                     names.insert(j.name);
                 for (auto & j : names) {
                     Attr & a(*v.attrs->find(state.symbols.create(j)));
-                    json.attr(j);
-                    printValueAsJSON(state, strict, *a.value, str, context);
+                    auto placeholder(obj.placeholder(j));
+                    printValueAsJSON(state, strict, *a.value, placeholder, context);
                 }
             } else
-                printValueAsJSON(state, strict, *i->value, str, context);
+                printValueAsJSON(state, strict, *i->value, out, context);
             break;
         }
 
         case tList1: case tList2: case tListN: {
-            JSONList json(str);
+            auto list(out.list());
             for (unsigned int n = 0; n < v.listSize(); ++n) {
-                json.elem();
-                printValueAsJSON(state, strict, *v.listElems()[n], str, context);
+                auto placeholder(list.placeholder());
+                printValueAsJSON(state, strict, *v.listElems()[n], placeholder, context);
             }
             break;
         }
 
         case tExternal:
-            v.external->printValueAsJSON(state, strict, str, context);
+            v.external->printValueAsJSON(state, strict, out, context);
             break;
 
         case tFloat:
-            str << v.fpoint;
+            out.write(v.fpoint);
             break;
 
         default:
@@ -93,9 +78,15 @@ void printValueAsJSON(EvalState & state, bool strict,
     }
 }
 
+void printValueAsJSON(EvalState & state, bool strict,
+    Value & v, std::ostream & str, PathSet & context)
+{
+    JSONPlaceholder out(str);
+    printValueAsJSON(state, strict, v, out, context);
+}
 
 void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
-      std::ostream & str, PathSet & context) const
+    JSONPlaceholder & out, PathSet & context) const
 {
     throw TypeError(format("cannot convert %1% to JSON") % showType());
 }
diff --git a/src/libexpr/value-to-json.hh b/src/libexpr/value-to-json.hh
index c59caf5641bc..67fed6487dd9 100644
--- a/src/libexpr/value-to-json.hh
+++ b/src/libexpr/value-to-json.hh
@@ -8,73 +8,12 @@
 
 namespace nix {
 
-void printValueAsJSON(EvalState & state, bool strict,
-    Value & v, std::ostream & out, PathSet & context);
-
-void escapeJSON(std::ostream & str, const string & s);
+class JSONPlaceholder;
 
-struct JSONObject
-{
-    std::ostream & str;
-    bool first;
-    JSONObject(std::ostream & str) : str(str), first(true)
-    {
-        str << "{";
-    }
-    ~JSONObject()
-    {
-        str << "}";
-    }
-    void attr(const string & s)
-    {
-        if (!first) str << ","; else first = false;
-        escapeJSON(str, s);
-        str << ":";
-    }
-    void attr(const string & s, const string & t)
-    {
-        attr(s);
-        escapeJSON(str, t);
-    }
-    void attr(const string & s, const char * t)
-    {
-        attr(s);
-        escapeJSON(str, t);
-    }
-    void attr(const string & s, bool b)
-    {
-        attr(s);
-        str << (b ? "true" : "false");
-    }
-    template<typename T>
-    void attr(const string & s, const T & n)
-    {
-        attr(s);
-        str << n;
-    }
-};
+void printValueAsJSON(EvalState & state, bool strict,
+    Value & v, JSONPlaceholder & out, PathSet & context);
 
-struct JSONList
-{
-    std::ostream & str;
-    bool first;
-    JSONList(std::ostream & str) : str(str), first(true)
-    {
-        str << "[";
-    }
-    ~JSONList()
-    {
-        str << "]";
-    }
-    void elem()
-    {
-        if (!first) str << ","; else first = false;
-    }
-    void elem(const string & s)
-    {
-        elem();
-        escapeJSON(str, s);
-    }
-};
+void printValueAsJSON(EvalState & state, bool strict,
+    Value & v, std::ostream & str, PathSet & context);
 
 }
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index 62bdd9281f08..271e6a1b24a2 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -1,7 +1,12 @@
 #pragma once
 
+#include "config.h"
 #include "symbol-table.hh"
 
+#if HAVE_BOEHMGC
+#include <gc/gc_allocator.h>
+#endif
+
 namespace nix {
 
 
@@ -36,6 +41,7 @@ class Symbol;
 struct Pos;
 class EvalState;
 class XMLWriter;
+class JSONPlaceholder;
 
 
 typedef long NixInt;
@@ -73,7 +79,7 @@ class ExternalValueBase
 
     /* Print the value as JSON. Defaults to unconvertable, i.e. throws an error */
     virtual void printValueAsJSON(EvalState & state, bool strict,
-        std::ostream & str, PathSet & context) const;
+        JSONPlaceholder & out, PathSet & context) const;
 
     /* Print the value as XML. Defaults to unevaluated */
     virtual void printValueAsXML(EvalState & state, bool strict, bool location,
@@ -249,4 +255,13 @@ void mkPath(Value & v, const char * s);
 size_t valueSize(Value & v);
 
 
+#if HAVE_BOEHMGC
+typedef std::vector<Value *, gc_allocator<Value *> > ValueVector;
+typedef std::map<Symbol, Value *, std::less<Symbol>, gc_allocator<Value *> > ValueMap;
+#else
+typedef std::vector<Value *> ValueVector;
+typedef std::map<Symbol, Value *> ValueMap;
+#endif
+
+
 }