[llvm] 91a98ec - [json] Provide a means to delegate writing a value to another API

Sam McCall via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 7 09:35:57 PDT 2020


Author: Daniel Sanders
Date: 2020-10-07T18:31:45+02:00
New Revision: 91a98ec11e2e001d01c47286bc1721046beeae62

URL: https://github.com/llvm/llvm-project/commit/91a98ec11e2e001d01c47286bc1721046beeae62
DIFF: https://github.com/llvm/llvm-project/commit/91a98ec11e2e001d01c47286bc1721046beeae62.diff

LOG: [json] Provide a means to delegate writing a value to another API

(Based on D87170 by dsanders)

I recently had need to call out to an external API to emit a JSON object as part
of one an LLVM tool was emitting. However, our JSON support didn't provide a way
to delegate part of the JSON output to that API.

Add rawValueBegin() and rawValueEnd() to maintain and check the internal state
while something else is writing to the stream. It's the users responsibility to
ensure that the resulting JSON output is still valid.

Differential Revision: https://reviews.llvm.org/D88902

Added: 
    

Modified: 
    llvm/include/llvm/Support/JSON.h
    llvm/lib/Support/JSON.cpp
    llvm/unittests/Support/JSONTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h
index 7a45dff6342e..455673e42e97 100644
--- a/llvm/include/llvm/Support/JSON.h
+++ b/llvm/include/llvm/Support/JSON.h
@@ -909,6 +909,17 @@ class OStream {
     Contents();
     objectEnd();
   }
+  /// Emit an externally-serialized value.
+  /// The caller must write exactly one valid JSON value to the provided stream.
+  /// No validation or formatting of this value occurs.
+  void rawValue(llvm::function_ref<void(raw_ostream &)> Contents) {
+    rawValueBegin();
+    Contents(OS);
+    rawValueEnd();
+  }
+  void rawValue(llvm::StringRef Contents) {
+    rawValue([&](raw_ostream &OS) { OS << Contents; });
+  }
   /// Emit a JavaScript comment associated with the next printed value.
   /// The string must be valid until the next attribute or value is emitted.
   /// Comments are not part of standard JSON, and many parsers reject them!
@@ -939,8 +950,10 @@ class OStream {
   void objectEnd();
   void attributeBegin(llvm::StringRef Key);
   void attributeEnd();
+  raw_ostream &rawValueBegin();
+  void rawValueEnd();
 
- private:
+private:
   void attributeImpl(llvm::StringRef Key, Block Contents) {
     attributeBegin(Key);
     Contents();
@@ -955,6 +968,7 @@ class OStream {
     Singleton, // Top level, or object attribute.
     Array,
     Object,
+    RawValue, // External code writing a value to OS directly.
   };
   struct State {
     Context Ctx = Singleton;

diff  --git a/llvm/lib/Support/JSON.cpp b/llvm/lib/Support/JSON.cpp
index d44961b43b89..8471e5818cbb 100644
--- a/llvm/lib/Support/JSON.cpp
+++ b/llvm/lib/Support/JSON.cpp
@@ -251,20 +251,13 @@ std::vector<const Object::value_type *> sortedElements(const Object &O) {
 // Prints a one-line version of a value that isn't our main focus.
 // We interleave writes to OS and JOS, exploiting the lack of extra buffering.
 // This is OK as we own the implementation.
-// FIXME: once we have a "write custom serialized value" API, use it here.
-void abbreviate(const Value &V, OStream &JOS, raw_ostream &OS) {
+void abbreviate(const Value &V, OStream &JOS) {
   switch (V.kind()) {
   case Value::Array:
-    JOS.array([&] {
-      if (!V.getAsArray()->empty())
-        OS << " ... ";
-    });
+    JOS.rawValue(V.getAsArray()->empty() ? "[]" : "[ ... ]");
     break;
   case Value::Object:
-    JOS.object([&] {
-      if (!V.getAsObject()->empty())
-        OS << " ... ";
-    });
+    JOS.rawValue(V.getAsObject()->empty() ? "{}" : "{ ... }");
     break;
   case Value::String: {
     llvm::StringRef S = *V.getAsString();
@@ -284,19 +277,19 @@ void abbreviate(const Value &V, OStream &JOS, raw_ostream &OS) {
 
 // Prints a semi-expanded version of a value that is our main focus.
 // Array/Object entries are printed, but not recursively as they may be huge.
-void abbreviateChildren(const Value &V, OStream &JOS, raw_ostream &OS) {
+void abbreviateChildren(const Value &V, OStream &JOS) {
   switch (V.kind()) {
   case Value::Array:
     JOS.array([&] {
       for (const auto &I : *V.getAsArray())
-        abbreviate(I, JOS, OS);
+        abbreviate(I, JOS);
     });
     break;
   case Value::Object:
     JOS.object([&] {
       for (const auto *KV : sortedElements(*V.getAsObject())) {
         JOS.attributeBegin(KV->first);
-        abbreviate(KV->second, JOS, OS);
+        abbreviate(KV->second, JOS);
         JOS.attributeEnd();
       }
     });
@@ -322,7 +315,7 @@ void Path::Root::printErrorContext(const Value &R, raw_ostream &OS) const {
       std::string Comment = "error: ";
       Comment.append(ErrorMessage.data(), ErrorMessage.size());
       JOS.comment(Comment);
-      abbreviateChildren(V, JOS, OS);
+      abbreviateChildren(V, JOS);
     };
     if (Path.empty()) // We reached our target.
       return HighlightCurrent();
@@ -339,7 +332,7 @@ void Path::Root::printErrorContext(const Value &R, raw_ostream &OS) const {
           if (FieldName.equals(KV->first))
             Recurse(KV->second, Path.drop_back(), Recurse);
           else
-            abbreviate(KV->second, JOS, OS);
+            abbreviate(KV->second, JOS);
           JOS.attributeEnd();
         }
       });
@@ -354,7 +347,7 @@ void Path::Root::printErrorContext(const Value &R, raw_ostream &OS) const {
           if (Current++ == S.index())
             Recurse(V, Path.drop_back(), Recurse);
           else
-            abbreviate(V, JOS, OS);
+            abbreviate(V, JOS);
         }
       });
     }
@@ -893,6 +886,18 @@ void llvm::json::OStream::attributeEnd() {
   assert(Stack.back().Ctx == Object);
 }
 
+raw_ostream &llvm::json::OStream::rawValueBegin() {
+  valueBegin();
+  Stack.emplace_back();
+  Stack.back().Ctx = RawValue;
+  return OS;
+}
+
+void llvm::json::OStream::rawValueEnd() {
+  assert(Stack.back().Ctx == RawValue);
+  Stack.pop_back();
+}
+
 } // namespace json
 } // namespace llvm
 

diff  --git a/llvm/unittests/Support/JSONTest.cpp b/llvm/unittests/Support/JSONTest.cpp
index 6a93f3befc5a..9f17c98b4db4 100644
--- a/llvm/unittests/Support/JSONTest.cpp
+++ b/llvm/unittests/Support/JSONTest.cpp
@@ -479,6 +479,7 @@ TEST(JSONTest, Stream) {
         J.arrayBegin();
         J.value(43);
         J.arrayEnd();
+        J.rawValue([](raw_ostream &OS) { OS << "'unverified\nraw value'"; });
       });
       J.comment("attribute");
       J.attributeBegin("bar");
@@ -492,7 +493,8 @@ TEST(JSONTest, Stream) {
   };
 
   const char *Plain =
-      R"(/*top* /level*/{"foo":[null,/*element*/42.5,[43]],/*attribute*/"bar":/*attribute value*/{},"baz":"xyz"})";
+      R"(/*top* /level*/{"foo":[null,/*element*/42.5,[43],'unverified
+raw value'],/*attribute*/"bar":/*attribute value*/{},"baz":"xyz"})";
   EXPECT_EQ(Plain, StreamStuff(0));
   const char *Pretty = R"(/* top* /level */
 {
@@ -502,7 +504,9 @@ TEST(JSONTest, Stream) {
     42.5,
     [
       43
-    ]
+    ],
+    'unverified
+raw value'
   ],
   /* attribute */
   "bar": /* attribute value */ {},


        


More information about the llvm-commits mailing list