about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libexpr/eval.cc23
-rw-r--r--src/libexpr/eval.hh4
-rw-r--r--src/libexpr/value-to-json.cc7
-rw-r--r--tests/lang/eval-okay-tojson.exp2
-rw-r--r--tests/lang/eval-okay-tojson.nix1
5 files changed, 29 insertions, 8 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 1f6dae95ac12..d84d0dd1612f 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -1568,6 +1568,19 @@ bool EvalState::isDerivation(Value & v)
 }
 
 
+std::optional<string> EvalState::tryAttrsToString(const Pos & pos, Value & v,
+    PathSet & context, bool coerceMore, bool copyToStore)
+{
+    auto i = v.attrs->find(sToString);
+    if (i != v.attrs->end()) {
+        Value v1;
+        callFunction(*i->value, v, v1, pos);
+        return coerceToString(pos, v1, context, coerceMore, copyToStore);
+    }
+
+    return {};
+}
+
 string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
     bool coerceMore, bool copyToStore)
 {
@@ -1586,13 +1599,11 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
     }
 
     if (v.type == tAttrs) {
-        auto i = v.attrs->find(sToString);
-        if (i != v.attrs->end()) {
-            Value v1;
-            callFunction(*i->value, v, v1, pos);
-            return coerceToString(pos, v1, context, coerceMore, copyToStore);
+        auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
+        if (maybeString) {
+            return *maybeString;
         }
-        i = v.attrs->find(sOutPath);
+        auto i = v.attrs->find(sOutPath);
         if (i == v.attrs->end()) throwTypeError("cannot coerce a set to a string, at %1%", pos);
         return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
     }
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 22472fd726b2..61ee4a73b563 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -9,6 +9,7 @@
 #include "function-trace.hh"
 
 #include <map>
+#include <optional>
 #include <unordered_map>
 
 
@@ -196,6 +197,9 @@ public:
        set with attribute `type = "derivation"'). */
     bool isDerivation(Value & v);
 
+    std::optional<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,
diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc
index 72e413e4491e..5fe8570adeb4 100644
--- a/src/libexpr/value-to-json.cc
+++ b/src/libexpr/value-to-json.cc
@@ -40,7 +40,12 @@ void printValueAsJSON(EvalState & state, bool strict,
             break;
 
         case tAttrs: {
-            Bindings::iterator i = v.attrs->find(state.sOutPath);
+            auto maybeString = state.tryAttrsToString(noPos, v, context, false, false);
+            if (maybeString) {
+                out.write(*maybeString);
+                break;
+            }
+            auto i = v.attrs->find(state.sOutPath);
             if (i == v.attrs->end()) {
                 auto obj(out.object());
                 StringSet names;
diff --git a/tests/lang/eval-okay-tojson.exp b/tests/lang/eval-okay-tojson.exp
index 33588493f75c..e92aae3235f2 100644
--- a/tests/lang/eval-okay-tojson.exp
+++ b/tests/lang/eval-okay-tojson.exp
@@ -1 +1 @@
-"{\"a\":123,\"b\":-456,\"c\":\"foo\",\"d\":\"foo\\n\\\"bar\\\"\",\"e\":true,\"f\":false,\"g\":[1,2,3],\"h\":[\"a\",[\"b\",{\"foo\\nbar\":{}}]],\"i\":3,\"j\":1.44}"
+"{\"a\":123,\"b\":-456,\"c\":\"foo\",\"d\":\"foo\\n\\\"bar\\\"\",\"e\":true,\"f\":false,\"g\":[1,2,3],\"h\":[\"a\",[\"b\",{\"foo\\nbar\":{}}]],\"i\":3,\"j\":1.44,\"k\":\"foo\"}"
diff --git a/tests/lang/eval-okay-tojson.nix b/tests/lang/eval-okay-tojson.nix
index c046ba4ae59b..ce67943bead5 100644
--- a/tests/lang/eval-okay-tojson.nix
+++ b/tests/lang/eval-okay-tojson.nix
@@ -9,4 +9,5 @@ builtins.toJSON
     h = [ "a" [ "b" { "foo\nbar" = {}; } ] ];
     i = 1 + 2;
     j = 1.44;
+    k = { __toString = self: self.a; a = "foo"; };
   }