diff --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h --- a/llvm/include/llvm/Support/JSON.h +++ b/llvm/include/llvm/Support/JSON.h @@ -909,6 +909,17 @@ 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 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 @@ 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 @@ 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 --- a/llvm/lib/Support/JSON.cpp +++ b/llvm/lib/Support/JSON.cpp @@ -251,20 +251,13 @@ // 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 @@ // 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 @@ 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 @@ 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 @@ if (Current++ == S.index()) Recurse(V, Path.drop_back(), Recurse); else - abbreviate(V, JOS, OS); + abbreviate(V, JOS); } }); } @@ -893,6 +886,18 @@ 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 --- a/llvm/unittests/Support/JSONTest.cpp +++ b/llvm/unittests/Support/JSONTest.cpp @@ -479,6 +479,7 @@ 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 @@ }; 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 @@ 42.5, [ 43 - ] + ], + 'unverified +raw value' ], /* attribute */ "bar": /* attribute value */ {},