Index: llvm/include/llvm/Support/ScopedPrinter.h =================================================================== --- llvm/include/llvm/Support/ScopedPrinter.h +++ llvm/include/llvm/Support/ScopedPrinter.h @@ -16,6 +16,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/raw_ostream.h" #include @@ -92,7 +93,19 @@ class ScopedPrinter { public: - ScopedPrinter(raw_ostream &OS) : OS(OS), IndentLevel(0) {} + enum ScopedPrinterKind { + Standard, + JSON, + }; + + ScopedPrinter(raw_ostream &OS, ScopedPrinterKind Kind = Standard) + : OS(OS), IndentLevel(0), Kind(Kind) {} + + ScopedPrinterKind getKind() const { return Kind; } + + static bool classof(const ScopedPrinter *SP) { + return SP->getKind() == Standard; + } virtual ~ScopedPrinter() {} @@ -368,8 +381,13 @@ virtual void printFlagsImpl(StringRef Label, HexNumber Value, ArrayRef Flags) { startLine() << Label << " [ (" << Value << ")\n"; - for (const auto &Flag : Flags) - startLine() << " " << Flag.Name << " (" << hex(Flag.Value) << ")\n"; + for (const auto &Flag : Flags) { + raw_ostream &OS = startLine(); + if (!Flag.Name.empty()) + OS << " " << Flag.Name << " (" << hex(Flag.Value) << ")\n"; + else + OS << " " << hex(Flag.Value) << '\n'; + } startLine() << "]\n"; } @@ -438,6 +456,7 @@ raw_ostream &OS; int IndentLevel; StringRef Prefix; + ScopedPrinterKind Kind; }; template <> @@ -447,6 +466,254 @@ startLine() << Label << ": " << hex(Value) << "\n"; } +class JSONScopedPrinter : public ScopedPrinter { +private: + enum Scope { + Array, + Object, + }; + + enum ScopeKind { + Standard, + Attribute, + NestedAttribute, + }; + + struct ScopeContext { + Scope Context; + ScopeKind Kind; + ScopeContext(Scope Context, ScopeKind Kind = ScopeKind::Standard) + : Context(Context), Kind(Kind) {} + }; + + SmallVector ScopeHistory; + json::OStream JOS; + +public: + JSONScopedPrinter(raw_ostream &OS) + : ScopedPrinter(OS, ScopedPrinter::ScopedPrinterKind::JSON), JOS(OS, 2) {} + + static bool classof(const ScopedPrinter *SP) { return SP->getKind() == JSON; } + + void printNumber(StringRef Label, uint64_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, uint32_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, uint16_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, uint8_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, int64_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, int32_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, int16_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, int8_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, const APSInt &Value) override { + JOS.attributeBegin(Label); + JOS.rawValueBegin() << Value; + JOS.rawValueEnd(); + JOS.attributeEnd(); + } + + void printBoolean(StringRef Label, bool Value) override { + JOS.attribute(Label, Value); + } + + void printString(StringRef Value) override { JOS.value(Value); } + + void printString(StringRef Label, StringRef Value) override { + JOS.attribute(Label, Value); + } + + void objectBegin() override { + scopedBegin({Scope::Object, ScopeKind::Standard}); + } + + void objectBegin(StringRef Label) override { + scopedBegin(Label, Scope::Object); + } + + void objectEnd() override { scopedEnd(); } + + void arrayBegin() override { + scopedBegin({Scope::Array, ScopeKind::Standard}); + } + + void arrayBegin(StringRef Label) override { + scopedBegin(Label, Scope::Array); + } + + void arrayEnd() override { scopedEnd(); } + + raw_ostream &startLine() override { + raw_ostream &OS = JOS.rawValueBegin(); + JOS.rawValueEnd(); + return OS; + } + + raw_ostream &getOStream() override { + raw_ostream &OS = JOS.rawValueBegin(); + JOS.rawValueEnd(); + return OS; + } + +private: + // Output hex values as JSON numbers so that they're easier to parse. + uint64_t hexNumberToInt(HexNumber Hex) { return Hex.Value; } + + void printFlagsImpl(StringRef Label, HexNumber Value, + ArrayRef Flags) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("RawFlags", hexNumberToInt(Value)); + JOS.attributeArray("Flags", [&]() { + for (const FlagEntry &Flag : Flags) { + JOS.objectBegin(); + JOS.attribute("Name", Flag.Name); + JOS.attribute("Value", Flag.Value); + JOS.objectEnd(); + } + }); + }); + } + + void printRawFlagsImpl(StringRef Label, HexNumber Value, + ArrayRef Flags) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("RawFlags", hexNumberToInt(Value)); + JOS.attributeArray("Flags", [&]() { + for (const FlagEntry &Flag : Flags) { + JOS.value(Flag.Value); + } + }); + }); + } + + void printListImpl(StringRef Label, + const ArrayRef List) override { + JOS.attributeArray(Label, [&]() { + for (StringRef Item : List) + JOS.value(Item); + }); + } + + void printHexListImpl(StringRef Label, + const ArrayRef List) override { + JOS.attributeArray(Label, [&]() { + for (const HexNumber &Item : List) { + JOS.value(hexNumberToInt(Item)); + } + }); + } + + void printHexImpl(StringRef Label, HexNumber Value) override { + JOS.attribute(Label, hexNumberToInt(Value)); + } + + void printHexImpl(StringRef Label, StringRef Str, HexNumber Value) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("Value", Str); + JOS.attribute("RawValue", hexNumberToInt(Value)); + }); + } + + void printSymbolOffsetImpl(StringRef Label, StringRef Symbol, + HexNumber Value) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("SymName", Symbol); + JOS.attribute("Offset", hexNumberToInt(Value)); + }); + } + + void printNumberImpl(StringRef Label, StringRef Str, + StringRef Value) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("Value", Str); + JOS.attributeBegin("RawValue"); + JOS.rawValueBegin() << Value; + JOS.rawValueEnd(); + JOS.attributeEnd(); + }); + } + + void printBinaryImpl(StringRef Label, StringRef Str, ArrayRef Value, + bool Block, uint32_t StartOffset = 0) override { + JOS.attributeObject(Label, [&]() { + if (!Str.empty()) + JOS.attribute("Value", Str); + JOS.attributeArray("Bytes", [&]() { + uint32_t CurrentOffset = StartOffset; + for (uint8_t Val : Value) { + JOS.objectBegin(); + JOS.attribute("Index", CurrentOffset++); + JOS.attribute("Value", Val); + if (Block) { + if (isPrint(Val)) { + JOS.attributeBegin("Character"); + JOS.rawValueBegin() << '"' << static_cast(Val) << '"'; + JOS.rawValueEnd(); + JOS.attributeEnd(); + } else { + JOS.attribute("Character", "."); + } + } + JOS.objectEnd(); + } + }); + }); + } + + void scopedBegin(ScopeContext ScopeCtx) { + if (ScopeCtx.Context == Scope::Object) + JOS.objectBegin(); + else if (ScopeCtx.Context == Scope::Array) + JOS.arrayBegin(); + ScopeHistory.push_back(ScopeCtx); + } + + void scopedBegin(StringRef Label, Scope Ctx) { + ScopeKind Kind = ScopeKind::Attribute; + if (ScopeHistory.empty() || ScopeHistory.back().Context != Scope::Object) { + JOS.objectBegin(); + Kind = ScopeKind::NestedAttribute; + } + JOS.attributeBegin(Label); + scopedBegin({Ctx, Kind}); + } + + void scopedEnd() { + ScopeContext ScopeCtx = ScopeHistory.back(); + if (ScopeCtx.Context == Scope::Object) + JOS.objectEnd(); + else if (ScopeCtx.Context == Scope::Array) + JOS.arrayEnd(); + if (ScopeCtx.Kind == ScopeKind::Attribute || + ScopeCtx.Kind == ScopeKind::NestedAttribute) + JOS.attributeEnd(); + if (ScopeCtx.Kind == ScopeKind::NestedAttribute) + JOS.objectEnd(); + ScopeHistory.pop_back(); + } +}; + struct DelimitedScope { DelimitedScope(ScopedPrinter &W) : W(W) {} virtual ~DelimitedScope(){}; Index: llvm/unittests/Support/ScopedPrinterTest.cpp =================================================================== --- llvm/unittests/Support/ScopedPrinterTest.cpp +++ llvm/unittests/Support/ScopedPrinterTest.cpp @@ -24,6 +24,13 @@ ScopedPrinter Writer(OS); EXPECT_EQ(Expected.str(), Fcn(OS, Writer)); } + + void verifyJSONScopedPrinter(StringRef Expected, PrintFcn Fcn) { + std::string StreamBuffer; + raw_string_ostream OS(StreamBuffer); + JSONScopedPrinter Writer(OS); + EXPECT_EQ(Expected.str(), Fcn(OS, Writer)); + } }; TEST_F(ScopedPrinterTest, PrintIndent) { @@ -119,6 +126,31 @@ } )"; verifyScopedPrinter(Out, NumberFcn); + + const char *JSONOut = R"({ + "uint64_t-max": 18446744073709551615, + "uint64_t-min": 0, + "uint32_t-max": 4294967295, + "uint32_t-min": 0, + "uint16_t-max": 65535, + "uint16_t-min": 0, + "uint8_t-max": 255, + "uint8_t-min": 0, + "int64_t-max": 9223372036854775807, + "int64_t-min": -9223372036854775808, + "int32_t-max": 2147483647, + "int32_t-min": -2147483648, + "int16_t-max": 32767, + "int16_t-min": -32768, + "int8_t-max": 127, + "int8_t-min": -128, + "apsint": 9999999999999999999999, + "label": { + "Value": "value", + "RawValue": 0 + } +})"; + verifyJSONScopedPrinter(JSONOut, NumberFcn); } TEST_F(ScopedPrinterTest, PrintEnum) { @@ -141,6 +173,15 @@ } )"; verifyScopedPrinter(Out, EnumFcn); + + const char *JSONOut = R"({ + "Exists": { + "Value": "Name1", + "RawValue": 1 + }, + "DoesNotExist": 4 +})"; + verifyJSONScopedPrinter(JSONOut, EnumFcn); } TEST_F(ScopedPrinterTest, PrintFlag) { @@ -183,6 +224,55 @@ } )"; verifyScopedPrinter(Out, FlagFcn); + + const char *JSONOut = R"({ + "NoFlag": { + "RawFlags": 8, + "Flags": [] + }, + "Flag1": { + "RawFlags": 1, + "Flags": [ + { + "Name": "Name1", + "Value": 1 + } + ] + }, + "Flag1&3": { + "RawFlags": 5, + "Flags": [ + { + "Name": "Name1", + "Value": 1 + }, + { + "Name": "Name3", + "Value": 4 + } + ] + }, + "NoFlagRaw": { + "RawFlags": 8, + "Flags": [ + 8 + ] + }, + "Flag1Raw": { + "RawFlags": 1, + "Flags": [ + 1 + ] + }, + "Flag1&3Raw": { + "RawFlags": 5, + "Flags": [ + 1, + 4 + ] + } +})"; + verifyJSONScopedPrinter(JSONOut, FlagFcn); } TEST_F(ScopedPrinterTest, PrintBoolean) { @@ -201,6 +291,12 @@ } )"; verifyScopedPrinter(Out, BooleanFcn); + + const char *JSONOut = R"({ + "True": true, + "False": false +})"; + verifyJSONScopedPrinter(JSONOut, BooleanFcn); } TEST_F(ScopedPrinterTest, PrintVersion) { @@ -211,6 +307,7 @@ } return OS.str(); }; + const char *Out = R"({ Version: 123.456.789 } @@ -239,6 +336,21 @@ } )"; verifyScopedPrinter(Out, ListFcn); + + const char *JSONOut = R"({ + "EmptyList": [], + "NumberList": [ + "1", + "2", + "3" + ], + "StringList": [ + "foo", + "bar", + "baz" + ] +})"; + verifyJSONScopedPrinter(JSONOut, ListFcn); } TEST_F(ScopedPrinterTest, PrintHex) { @@ -262,6 +374,24 @@ } )"; verifyScopedPrinter(Out, HexFcn); + + const char *JSONOut = R"({ + "HexList": [ + 1, + 16, + 256 + ], + "HexNumber": 16, + "HexLabel": { + "Value": "Name", + "RawValue": 16 + }, + "SymbolOffset": { + "SymName": "SymbolName", + "Offset": 16 + } +})"; + verifyJSONScopedPrinter(JSONOut, HexFcn); } TEST_F(ScopedPrinterTest, PrintString) { @@ -292,6 +422,17 @@ } )"; verifyScopedPrinter(Out, StringFcn); + + const char *JSONOut = R"({ + "StringRef": "Value", + "String": "Value", + "CharArray": "Value", + "StringList": [ + "Value", + "Value" + ] +})"; + verifyJSONScopedPrinter(JSONOut, StringFcn); } TEST_F(ScopedPrinterTest, PrintBinary) { @@ -328,6 +469,226 @@ } )"; verifyScopedPrinter(Out, BinaryFcn); + + const char *JSONOut = R"({ + "Binary1": { + "Value": "FooBar", + "Bytes": [ + { + "Index": 0, + "Value": 70 + }, + { + "Index": 1, + "Value": 111 + }, + { + "Index": 2, + "Value": 111 + }, + { + "Index": 3, + "Value": 66 + }, + { + "Index": 4, + "Value": 97 + }, + { + "Index": 5, + "Value": 114 + } + ] + }, + "Binary2": { + "Value": "FooBar", + "Bytes": [ + { + "Index": 0, + "Value": 70 + }, + { + "Index": 1, + "Value": 111 + }, + { + "Index": 2, + "Value": 111 + }, + { + "Index": 3, + "Value": 66 + }, + { + "Index": 4, + "Value": 97 + }, + { + "Index": 5, + "Value": 114 + } + ] + }, + "Binary3": { + "Bytes": [ + { + "Index": 0, + "Value": 70 + }, + { + "Index": 1, + "Value": 111 + }, + { + "Index": 2, + "Value": 111 + }, + { + "Index": 3, + "Value": 66 + }, + { + "Index": 4, + "Value": 97 + }, + { + "Index": 5, + "Value": 114 + } + ] + }, + "Binary4": { + "Bytes": [ + { + "Index": 0, + "Value": 70 + }, + { + "Index": 1, + "Value": 111 + }, + { + "Index": 2, + "Value": 111 + }, + { + "Index": 3, + "Value": 66 + }, + { + "Index": 4, + "Value": 97 + }, + { + "Index": 5, + "Value": 114 + } + ] + }, + "Binary5": { + "Bytes": [ + { + "Index": 20, + "Value": 70, + "Character": "F" + }, + { + "Index": 21, + "Value": 111, + "Character": "o" + }, + { + "Index": 22, + "Value": 111, + "Character": "o" + }, + { + "Index": 23, + "Value": 66, + "Character": "B" + }, + { + "Index": 24, + "Value": 97, + "Character": "a" + }, + { + "Index": 25, + "Value": 114, + "Character": "r" + } + ] + }, + "Binary6": { + "Bytes": [ + { + "Index": 0, + "Value": 70, + "Character": "F" + }, + { + "Index": 1, + "Value": 111, + "Character": "o" + }, + { + "Index": 2, + "Value": 111, + "Character": "o" + }, + { + "Index": 3, + "Value": 66, + "Character": "B" + }, + { + "Index": 4, + "Value": 97, + "Character": "a" + }, + { + "Index": 5, + "Value": 114, + "Character": "r" + } + ] + }, + "Binary7": { + "Bytes": [ + { + "Index": 0, + "Value": 70, + "Character": "F" + }, + { + "Index": 1, + "Value": 111, + "Character": "o" + }, + { + "Index": 2, + "Value": 111, + "Character": "o" + }, + { + "Index": 3, + "Value": 66, + "Character": "B" + }, + { + "Index": 4, + "Value": 97, + "Character": "a" + }, + { + "Index": 5, + "Value": 114, + "Character": "r" + } + ] + } +})"; + verifyJSONScopedPrinter(JSONOut, BinaryFcn); } TEST_F(ScopedPrinterTest, PrintObject) { @@ -344,6 +705,11 @@ } )"; verifyScopedPrinter(Out, ObjectFcn); + + const char *JSONOut = R"({ + "Object": "Value" +})"; + verifyJSONScopedPrinter(JSONOut, ObjectFcn); } TEST_F(ScopedPrinterTest, PrintScope) { @@ -380,4 +746,20 @@ } )"; verifyScopedPrinter(Out, ScopeFcn); + + const char *JSONOut = R"({ + "Object": { + "ObjectInObject": {}, + "ListInObject": [] + }, + "List": [ + { + "ObjectInList": {} + }, + { + "ListInList": [] + } + ] +})"; + verifyJSONScopedPrinter(JSONOut, ScopeFcn); }