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,20 @@ class ScopedPrinter { public: - ScopedPrinter(raw_ostream &OS) : OS(OS), IndentLevel(0) {} + enum class ScopedPrinterKind { + Base, + JSON, + }; + + ScopedPrinter(raw_ostream &OS, + ScopedPrinterKind Kind = ScopedPrinterKind::Base) + : OS(OS), IndentLevel(0), Kind(Kind) {} + + ScopedPrinterKind getKind() const { return Kind; } + + static bool classof(const ScopedPrinter *SP) { + return SP->getKind() == ScopedPrinterKind::Base; + } virtual ~ScopedPrinter() {} @@ -479,6 +493,7 @@ raw_ostream &OS; int IndentLevel; StringRef Prefix; + ScopedPrinterKind Kind; }; template <> @@ -488,6 +503,310 @@ startLine() << Label << ": " << hex(Value) << "\n"; } +class JSONScopedPrinter : public ScopedPrinter { +public: + enum class Scope { + Array, + Object, + None, + }; + +private: + 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; + Scope OuterScope; + +public: + JSONScopedPrinter(raw_ostream &OS, int IndentSize = 0, + Scope OuterScopeKind = Scope::None) + : ScopedPrinter(OS, ScopedPrinter::ScopedPrinterKind::JSON), + JOS(OS, IndentSize), OuterScope(OuterScopeKind) { + switch (OuterScope) { + case Scope::Array: + arrayBegin(); + break; + case Scope::Object: + objectBegin(); + break; + case Scope::None: + break; + } + } + + ~JSONScopedPrinter() { + switch (OuterScope) { + case Scope::Array: + arrayEnd(); + break; + case Scope::Object: + objectEnd(); + break; + case Scope::None: + break; + } + } + + static bool classof(const ScopedPrinter *SP) { + return SP->getKind() == ScopedPrinter::ScopedPrinterKind::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); + printAPSInt(Value); + 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) { + printAPSInt(Item); + } + }); + } + + 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 printAPSInt(const APSInt &Value) { + JOS.rawValueBegin() << Value; + JOS.rawValueEnd(); + } + + 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 @@ -18,17 +18,51 @@ std::string StreamBuffer; raw_string_ostream OS; ScopedPrinter Writer; + JSONScopedPrinter JSONWriter; - ScopedPrinterTest() : OS(StreamBuffer), Writer(OS) {} + bool HasPrintedToJSON; + + ScopedPrinterTest() + : OS(StreamBuffer), Writer(OS), JSONWriter(OS, 2), + HasPrintedToJSON(false) {} using PrintFunc = function_ref; void verifyScopedPrinter(StringRef Expected, PrintFunc Func) { Func(Writer); EXPECT_EQ(Expected.str(), OS.str()); + StreamBuffer.clear(); + } + + void verifyJSONScopedPrinter(StringRef Expected, PrintFunc Func) { + { + DictScope D(JSONWriter); + Func(JSONWriter); + } + EXPECT_EQ(Expected.str(), OS.str()); + StreamBuffer.clear(); + HasPrintedToJSON = true; + } + + void TearDown() { + // JSONScopedPrinter fails an assert if nothing's been printed. + if (!HasPrintedToJSON) + JSONWriter.printString(""); } }; +TEST_F(ScopedPrinterTest, GetKind) { + EXPECT_EQ(ScopedPrinter::ScopedPrinterKind::Base, Writer.getKind()); + EXPECT_EQ(ScopedPrinter::ScopedPrinterKind::JSON, JSONWriter.getKind()); +} + +TEST_F(ScopedPrinterTest, ClassOf) { + EXPECT_TRUE(ScopedPrinter::classof(&Writer)); + EXPECT_TRUE(JSONScopedPrinter::classof(&JSONWriter)); + EXPECT_FALSE(ScopedPrinter::classof(&JSONWriter)); + EXPECT_FALSE(JSONScopedPrinter::classof(&Writer)); +} + TEST_F(ScopedPrinterTest, Indent) { auto PrintFunc = [](ScopedPrinter &W) { W.printString("|"); @@ -151,6 +185,15 @@ DoesNotExist: 0x5 )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "Exists": { + "Value": "Name2", + "RawValue": 2 + }, + "DoesNotExist": 5 +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintFlag) { @@ -255,6 +298,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) { @@ -325,6 +531,31 @@ label: value (0) )"; 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) { @@ -337,6 +568,12 @@ False: No )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "True": true, + "False": false +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintVersion) { @@ -406,6 +643,56 @@ APSIntList: [9999999999999999999999, -9999999999999999999999] )"; 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) { @@ -430,6 +717,15 @@ HexLabel: Name (0x10) )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "HexNumber": 16, + "HexLabel": { + "Value": "Name", + "RawValue": 16 + } +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintHexList) { @@ -440,6 +736,15 @@ const char *ExpectedOut = R"(HexList: [0x1, 0x10, 0x100] )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "HexList": [ + 1, + 16, + 256 + ] +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintSymbolOffset) { @@ -451,6 +756,18 @@ NoSymbolOffset: SymbolName+0x0 )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "SymbolOffset": { + "SymName": "SymbolName", + "Offset": 16 + }, + "NoSymbolOffset": { + "SymName": "SymbolName", + "Offset": 0 + } +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintString) { @@ -473,6 +790,16 @@ ] )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "StringRef": "Value", + "String": "Value", + "CharArray": "Value", + "StringList": [ + "Value" + ] +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, PrintBinary) { @@ -520,6 +847,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) { @@ -528,6 +1006,11 @@ const char *ExpectedOut = R"(Object: Value )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "Object": "Value" +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); } TEST_F(ScopedPrinterTest, StartLine) { @@ -578,4 +1061,20 @@ ] )"; verifyScopedPrinter(ExpectedOut, PrintFunc); + + const char *JSONExpectedOut = R"({ + "Object": { + "ObjectInObject": {}, + "ListInObject": [] + }, + "List": [ + { + "ObjectInList": {} + }, + { + "ListInList": [] + } + ] +})"; + verifyJSONScopedPrinter(JSONExpectedOut, PrintFunc); }