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,17 @@ class ScopedPrinter { public: - ScopedPrinter(raw_ostream &OS) : OS(OS), IndentLevel(0) {} + enum ScopedPrinterKind { + Base, + JSON, + }; + + ScopedPrinter(raw_ostream &OS, ScopedPrinterKind Kind = Base) + : OS(OS), IndentLevel(0), Kind(Kind) {} + + ScopedPrinterKind getKind() const { return Kind; } + + static bool classof(const ScopedPrinter *SP) { return SP->getKind() == Base; } virtual ~ScopedPrinter() {} @@ -479,6 +490,7 @@ raw_ostream &OS; int IndentLevel; StringRef Prefix; + ScopedPrinterKind Kind; }; template <> @@ -488,6 +500,277 @@ startLine() << Label << ": " << hex(Value) << "\n"; } +class JSONScopedPrinter : public ScopedPrinter { +private: + enum class Scope { + Array, + Object, + }; + + enum class ScopeKind { + NoAttribute, + Attribute, + NestedAttribute, + }; + + struct ScopeContext { + Scope Context; + ScopeKind Kind; + ScopeContext(Scope Context, ScopeKind Kind = ScopeKind::NoAttribute) + : Context(Context), Kind(Kind) {} + }; + + SmallVector ScopeHistory; + json::OStream JOS; + +public: + JSONScopedPrinter(raw_ostream &OS, int IndentSize = 0) + : ScopedPrinter(OS, ScopedPrinter::ScopedPrinterKind::JSON), + JOS(OS, IndentSize) {} + + 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 printList(StringRef Label, const ArrayRef List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef List) override { + JOS.attributeArray(Label, [&]() { + for (const APSInt &Item : List) { + JOS.rawValueBegin() << Item; + JOS.rawValueEnd(); + } + }); + } + + 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::NoAttribute}); + } + + void objectBegin(StringRef Label) override { + scopedBegin(Label, Scope::Object); + } + + void objectEnd() override { scopedEnd(); } + + void arrayBegin() override { + scopedBegin({Scope::Array, ScopeKind::NoAttribute}); + } + + void arrayBegin(StringRef Label) override { + scopedBegin(Label, Scope::Array); + } + + void arrayEnd() override { scopedEnd(); } + +private: + // Output HexNumbers as decimals 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 printFlagsImpl(StringRef Label, HexNumber Value, + ArrayRef Flags) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("RawFlags", hexNumberToInt(Value)); + JOS.attributeArray("Flags", [&]() { + for (const HexNumber &Flag : Flags) { + JOS.value(Flag.Value); + } + }); + }); + } + + template void printListImpl(StringRef Label, const T &List) { + JOS.attributeArray(Label, [&]() { + for (const auto &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.attribute("Offset", StartOffset); + JOS.attributeArray("Bytes", [&]() { + for (uint8_t Val : Value) + JOS.value(Val); + }); + }); + } + + 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,8 +24,42 @@ ScopedPrinter Writer(OS); EXPECT_EQ(Expected.str(), Func(OS, Writer)); } + + void verifyJSONScopedPrinter(StringRef Expected, PrintFunc Func) { + std::string StreamBuffer; + raw_string_ostream OS(StreamBuffer); + JSONScopedPrinter Writer(OS, 2); + EXPECT_EQ(Expected.str(), Func(OS, Writer)); + } }; +TEST_F(ScopedPrinterTest, GetKind) { + std::string StreamBuffer; + raw_string_ostream OS(StreamBuffer); + + ScopedPrinter Writer(OS); + EXPECT_EQ(ScopedPrinter::ScopedPrinterKind::Base, Writer.getKind()); + + JSONScopedPrinter JSONWriter(OS); + EXPECT_EQ(ScopedPrinter::ScopedPrinterKind::JSON, JSONWriter.getKind()); + // JSONScopedPrinter fails an assert if nothings been printed. + JSONWriter.printString(""); +} + +TEST_F(ScopedPrinterTest, ClassOf) { + std::string StreamBuffer; + raw_string_ostream OS(StreamBuffer); + ScopedPrinter Writer(OS); + JSONScopedPrinter JSONWriter(OS); + + EXPECT_TRUE(ScopedPrinter::classof(&Writer)); + EXPECT_TRUE(JSONScopedPrinter::classof(&JSONWriter)); + EXPECT_FALSE(ScopedPrinter::classof(&JSONWriter)); + EXPECT_FALSE(JSONScopedPrinter::classof(&Writer)); + // JSONScopedPrinter fails an assert if nothings been printed. + JSONWriter.printString(""); +} + TEST_F(ScopedPrinterTest, Indent) { auto PrintFunc = [](raw_string_ostream &OS, ScopedPrinter &W) { W.printString("|"); @@ -162,6 +196,15 @@ } )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "Exists": { + "Value": "Name2", + "RawValue": 2 + }, + "DoesNotExist": 5 +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintFlag) { @@ -273,6 +316,169 @@ } )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "ZeroFlag": { + "RawFlags": 0, + "Flags": [] + }, + "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 + } + ] + }, + "ZeroFlagRaw": { + "RawFlags": 0, + "Flags": [] + }, + "NoFlagRaw": { + "RawFlags": 8, + "Flags": [ + 8 + ] + }, + "Flag1Raw": { + "RawFlags": 1, + "Flags": [ + 1 + ] + }, + "Flag1&3Raw": { + "RawFlags": 5, + "Flags": [ + 1, + 4 + ] + }, + "FlagSorted": { + "RawFlags": 7, + "Flags": [ + { + "Name": "A", + "Value": 4 + }, + { + "Name": "B", + "Value": 2 + }, + { + "Name": "C", + "Value": 1 + } + ] + }, + "NoBitMask": { + "RawFlags": 4095, + "Flags": [ + { + "Name": "FirstByte1", + "Value": 1 + }, + { + "Name": "FirstByte2", + "Value": 2 + }, + { + "Name": "FirstByte3", + "Value": 3 + }, + { + "Name": "SecondByte1", + "Value": 16 + }, + { + "Name": "SecondByte2", + "Value": 32 + }, + { + "Name": "SecondByte3", + "Value": 48 + }, + { + "Name": "ThirdByte1", + "Value": 256 + }, + { + "Name": "ThirdByte2", + "Value": 512 + }, + { + "Name": "ThirdByte3", + "Value": 768 + } + ] + }, + "FirstByteMask": { + "RawFlags": 3, + "Flags": [ + { + "Name": "FirstByte3", + "Value": 3 + } + ] + }, + "SecondByteMask": { + "RawFlags": 48, + "Flags": [ + { + "Name": "SecondByte3", + "Value": 48 + } + ] + }, + "ValueOutsideMask": { + "RawFlags": 1, + "Flags": [ + { + "Name": "FirstByte1", + "Value": 1 + } + ] + }, + "FirstSecondByteMask": { + "RawFlags": 255, + "Flags": [] + }, + "FirstSecondThirdByteMask": { + "RawFlags": 819, + "Flags": [ + { + "Name": "FirstByte3", + "Value": 3 + }, + { + "Name": "SecondByte3", + "Value": 48 + }, + { + "Name": "ThirdByte3", + "Value": 768 + } + ] + } +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintNumber) { @@ -349,6 +555,31 @@ } )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = 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(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintBoolean) { @@ -367,6 +598,12 @@ } )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "True": true, + "False": false +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintVersion) { @@ -449,6 +686,56 @@ } )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "EmptyList": [], + "StringList": [ + "foo", + "bar", + "baz" + ], + "BoolList": [ + true, + false + ], + "uint64List": [ + 18446744073709551615, + 0 + ], + "uint32List": [ + 4294967295, + 0 + ], + "uint16List": [ + 65535, + 0 + ], + "uint8List": [ + 255, + 0 + ], + "int64List": [ + 9223372036854775807, + -9223372036854775808 + ], + "int32List": [ + 2147483647, + -2147483648 + ], + "int16List": [ + 32767, + -32768 + ], + "int8List": [ + 127, + -128 + ], + "APSIntList": [ + 9999999999999999999999, + -9999999999999999999999 + ] +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintListPrinter) { @@ -480,6 +767,15 @@ } )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "HexNumber": 16, + "HexLabel": { + "Value": "Name", + "RawValue": 16 + } +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintHexList) { @@ -496,6 +792,15 @@ } )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "HexList": [ + 1, + 16, + 256 + ] +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintSymbolOffset) { @@ -513,6 +818,18 @@ } )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "SymbolOffset": { + "SymName": "SymbolName", + "Offset": 16 + }, + "NoSymbolOffset": { + "SymName": "SymbolName", + "Offset": 0 + } +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintString) { @@ -541,6 +858,16 @@ } )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "StringRef": "Value", + "String": "Value", + "CharArray": "Value", + "StringList": [ + "Value" + ] +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintBinary) { @@ -594,6 +921,157 @@ } )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "Binary1": { + "Value": "FooBar", + "Offset": 0, + "Bytes": [ + 70, + 111, + 111, + 66, + 97, + 114 + ] + }, + "Binary2": { + "Value": "FooBar", + "Offset": 0, + "Bytes": [ + 70, + 111, + 111, + 66, + 97, + 114 + ] + }, + "Binary3": { + "Offset": 0, + "Bytes": [ + 70, + 111, + 111, + 66, + 97, + 114 + ] + }, + "Binary4": { + "Offset": 0, + "Bytes": [ + 70, + 111, + 111, + 66, + 97, + 114 + ] + }, + "Binary5": { + "Offset": 0, + "Bytes": [ + 70, + 111, + 111, + 66, + 97, + 114 + ] + }, + "Binary6": { + "Offset": 0, + "Bytes": [ + 77, + 117, + 108, + 116, + 105, + 112, + 108, + 101, + 32, + 76, + 105, + 110, + 101, + 32, + 70, + 111, + 111, + 66, + 97, + 114 + ] + }, + "Binary7": { + "Offset": 20, + "Bytes": [ + 70, + 111, + 111, + 66, + 97, + 114 + ] + }, + "Binary8": { + "Offset": 0, + "Bytes": [ + 70, + 111, + 111, + 66, + 97, + 114 + ] + }, + "Binary9": { + "Offset": 0, + "Bytes": [ + 70, + 111, + 111, + 66, + 97, + 114 + ] + }, + "Binary10": { + "Offset": 0, + "Bytes": [ + 77, + 117, + 108, + 116, + 105, + 112, + 108, + 101, + 32, + 76, + 105, + 110, + 101, + 32, + 70, + 111, + 111, + 66, + 97, + 114 + ] + }, + "Binary11": { + "Offset": 0, + "Bytes": [ + 255, + 255 + ] + } +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintObject) { @@ -610,6 +1088,11 @@ } )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "Object": "Value" +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, StartLine) { @@ -670,4 +1153,20 @@ } )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "Object": { + "ObjectInObject": {}, + "ListInObject": [] + }, + "List": [ + { + "ObjectInList": {} + }, + { + "ListInList": [] + } + ] +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); }