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() {} @@ -167,16 +180,16 @@ } template void printFlags(StringRef Label, T Value) { - startLine() << Label << " [ (" << hex(Value) << ")\n"; + SmallVector SetFlags; uint64_t Flag = 1; uint64_t Curr = Value; while (Curr > 0) { if (Curr & 1) - startLine() << " " << hex(Flag) << "\n"; + SetFlags.emplace_back("", Flag); Curr >>= 1; Flag <<= 1; } - startLine() << "]\n"; + printFlagsImpl(Label, hex(Value), SetFlags); } virtual void printNumber(StringRef Label, uint64_t Value) { @@ -324,7 +337,7 @@ } template void printObject(StringRef Label, const T &Value) { - startLine() << Label << ": " << Value << "\n"; + printString(Label, to_string(Value)); } virtual void objectBegin() { scopedBegin('{'); } @@ -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"; } @@ -430,6 +448,7 @@ raw_ostream &OS; int IndentLevel; StringRef Prefix; + ScopedPrinterKind Kind; }; template <> @@ -439,6 +458,251 @@ 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: + void printFlagsImpl(StringRef Label, HexNumber Value, + ArrayRef Flags) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("RawFlags", Value.Value); + JOS.attributeArray("Flags", [&]() { + for (const auto &Flag : Flags) { + if (!Flag.Name.empty()) + JOS.value(Flag.Name); + else + JOS.value(Flag.Value); + } + }); + }); + } + + void printListImpl(StringRef Label, + const ArrayRef List) override { + bool IsNumList = true; + for (const auto &Item : List) { + if (!(!Item.empty() && std::all_of(Item.begin(), Item.end(), ::isdigit))) + IsNumList = false; + } + JOS.attributeArray(Label, [&]() { + for (const auto &Item : List) + if (IsNumList) + JOS.value(std::stoi(Item)); + else + JOS.value(Item); + }); + } + + // Output hex values as JSON number so it's easier to parse + void printHexListImpl(StringRef Label, + const ArrayRef List) override { + JOS.attributeArray(Label, [&]() { + for (const auto &Item : List) { + JOS.value(Item.Value); + } + }); + } + + void printHexImpl(StringRef Label, HexNumber Value) override { + JOS.attribute(Label, Value.Value); + } + + void printHexImpl(StringRef Label, StringRef Str, HexNumber Value) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("Value", Str); + JOS.attribute("RawValue", Value.Value); + }); + } + + void printSymbolOffsetImpl(StringRef Label, StringRef Symbol, + HexNumber Value) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("SymName", Symbol); + JOS.attribute("Offset", Value.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.attributeObject("Bytes", [&]() { + uint32_t CurrentOffset = StartOffset; + for (uint8_t Val : Value) { + JOS.attribute(to_string(CurrentOffset++), Val); + } + }); + if (Block) { + JOS.attributeObject("Characters", [&]() { + uint32_t CurrentOffset = StartOffset; + for (uint8_t Val : Value) { + if (isPrint(Val)) { + JOS.attributeBegin(to_string(CurrentOffset++)); + JOS.rawValueBegin() << '"' << static_cast(Val) << '"'; + JOS.rawValueEnd(); + JOS.attributeEnd(); + } else { + JOS.attribute(to_string(CurrentOffset++), "."); + } + } + }); + } + }); + } + + 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/CMakeLists.txt =================================================================== --- llvm/unittests/Support/CMakeLists.txt +++ llvm/unittests/Support/CMakeLists.txt @@ -68,6 +68,7 @@ ReplaceFileTest.cpp RISCVAttributeParserTest.cpp ScaledNumberTest.cpp + ScopedPrinterTest.cpp SHA256.cpp SourceMgrTest.cpp SpecialCaseListTest.cpp Index: llvm/unittests/Support/ScopedPrinterTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/Support/ScopedPrinterTest.cpp @@ -0,0 +1,685 @@ +//===- llvm/unittest/Support/ScopedPrinterTest.cpp - ScopedPrinter tests --===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/ADT/APSInt.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +namespace { + +TEST(ScopedPrinterTest, PrintIndent) { + auto IndentOutput = [](raw_string_ostream &OS, ScopedPrinter &W) { + W.indent(); + W.printString("|"); + W.indent(); + W.printString("|"); + W.unindent(); + W.printString("|"); + W.flush(); + return OS.str(); + }; + const char *Out = R"( | + | + | +)"; + + std::string ScopedString; + llvm::raw_string_ostream OS(ScopedString); + ScopedPrinter Writer(OS); + EXPECT_EQ(Out, IndentOutput(OS, Writer)); +} + +TEST(ScopedPrinterTest, PrintNumber) { + auto NumberOutput = [](raw_string_ostream &OS, ScopedPrinter &W) { + { + DictScope D(W); + uint64_t Unsigned64Max = std::numeric_limits::max(); + uint64_t Unsigned64Min = std::numeric_limits::min(); + W.printNumber("uint64_t-max", Unsigned64Max); + W.printNumber("uint64_t-min", Unsigned64Min); + + uint32_t Unsigned32Max = std::numeric_limits::max(); + uint32_t Unsigned32Min = std::numeric_limits::min(); + W.printNumber("uint32_t-max", Unsigned32Max); + W.printNumber("uint32_t-min", Unsigned32Min); + + uint16_t Unsigned16Max = std::numeric_limits::max(); + uint16_t Unsigned16Min = std::numeric_limits::min(); + W.printNumber("uint16_t-max", Unsigned16Max); + W.printNumber("uint16_t-min", Unsigned16Min); + + uint8_t Unsigned8Max = std::numeric_limits::max(); + uint8_t Unsigned8Min = std::numeric_limits::min(); + W.printNumber("uint8_t-max", Unsigned8Max); + W.printNumber("uint8_t-min", Unsigned8Min); + + int64_t Signed64Max = std::numeric_limits::max(); + int64_t Signed64Min = std::numeric_limits::min(); + W.printNumber("int64_t-max", Signed64Max); + W.printNumber("int64_t-min", Signed64Min); + + int32_t Signed32Max = std::numeric_limits::max(); + int32_t Signed32Min = std::numeric_limits::min(); + W.printNumber("int32_t-max", Signed32Max); + W.printNumber("int32_t-min", Signed32Min); + + int16_t Signed16Max = std::numeric_limits::max(); + int16_t Signed16Min = std::numeric_limits::min(); + W.printNumber("int16_t-max", Signed16Max); + W.printNumber("int16_t-min", Signed16Min); + + int8_t Signed8Max = std::numeric_limits::max(); + int8_t Signed8Min = std::numeric_limits::min(); + W.printNumber("int8_t-max", Signed8Max); + W.printNumber("int8_t-min", Signed8Min); + + APSInt LargeNum("9999999999999999999999"); + W.printNumber("apsint", LargeNum); + + W.printNumber("label", "value", 0); + } + return OS.str(); + }; + + std::string ScopedString; + llvm::raw_string_ostream OS(ScopedString); + ScopedPrinter Writer(OS); + const char *Out = 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 (0) +} +)"; + EXPECT_EQ(Out, NumberOutput(OS, Writer)); + + std::string JSONScopedString; + llvm::raw_string_ostream JOS(JSONScopedString); + JSONScopedPrinter JSONWriter(JOS); + 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 + } +})"; + EXPECT_EQ(JSONOut, NumberOutput(JOS, JSONWriter)); +} + +TEST(ScopedPrinterTest, PrintEnum) { + auto EnumOutput = [](raw_string_ostream &OS, ScopedPrinter &W) { + const EnumEntry EnumList[] = {{"Name1", "AltName1", 1}, + {"Name2", "AltName2", 2}, + {"Name3", "AltName3", 3}}; + EnumEntry OtherEnum{"Name4", "AltName4", 4}; + { + DictScope D(W); + W.printEnum("Exists", EnumList[0].Value, makeArrayRef(EnumList)); + W.printEnum("DoesNotExist", OtherEnum.Value, makeArrayRef(EnumList)); + } + return OS.str(); + }; + + std::string ScopedString; + llvm::raw_string_ostream OS(ScopedString); + ScopedPrinter Writer(OS); + const char *Out = R"({ + Exists: Name1 (0x1) + DoesNotExist: 0x4 +} +)"; + EXPECT_EQ(Out, EnumOutput(OS, Writer)); + + std::string JSONScopedString; + llvm::raw_string_ostream JOS(JSONScopedString); + JSONScopedPrinter JSONWriter(JOS); + const char *JSONOut = R"({ + "Exists": { + "Value": "Name1", + "RawValue": 1 + }, + "DoesNotExist": 4 +})"; + EXPECT_EQ(JSONOut, EnumOutput(JOS, JSONWriter)); +} + +TEST(ScopedPrinterTest, PrintFlag) { + auto FlagOutput = [](raw_string_ostream &OS, ScopedPrinter &W) { + const EnumEntry FlagList[] = {{"Name1", "AltName1", 1}, + {"Name2", "AltName2", 1 << 1}, + {"Name3", "AltName3", 1 << 2}}; + { + DictScope D(W); + W.printFlags("NoFlag", 1 << 3, makeArrayRef(FlagList)); + W.printFlags("Flag1", FlagList[0].Value, makeArrayRef(FlagList)); + W.printFlags("Flag1&3", (1 << 2) + 1, makeArrayRef(FlagList)); + W.printFlags("NoFlagRaw", 1 << 3); + W.printFlags("Flag1Raw", FlagList[0].Value); + W.printFlags("Flag1&3Raw", (1 << 2) + 1); + } + return OS.str(); + }; + + std::string ScopedString; + llvm::raw_string_ostream OS(ScopedString); + ScopedPrinter Writer(OS); + const char *Out = R"({ + NoFlag [ (0x8) + ] + Flag1 [ (0x1) + Name1 (0x1) + ] + Flag1&3 [ (0x5) + Name1 (0x1) + Name3 (0x4) + ] + NoFlagRaw [ (0x8) + 0x8 + ] + Flag1Raw [ (0x1) + 0x1 + ] + Flag1&3Raw [ (0x5) + 0x1 + 0x4 + ] +} +)"; + EXPECT_EQ(Out, FlagOutput(OS, Writer)); + + std::string JSONScopedString; + llvm::raw_string_ostream JOS(JSONScopedString); + JSONScopedPrinter JSONWriter(JOS); + const char *JSONOut = R"({ + "NoFlag": { + "RawFlags": 8, + "Flags": [] + }, + "Flag1": { + "RawFlags": 1, + "Flags": [ + "Name1" + ] + }, + "Flag1&3": { + "RawFlags": 5, + "Flags": [ + "Name1", + "Name3" + ] + }, + "NoFlagRaw": { + "RawFlags": 8, + "Flags": [ + 8 + ] + }, + "Flag1Raw": { + "RawFlags": 1, + "Flags": [ + 1 + ] + }, + "Flag1&3Raw": { + "RawFlags": 5, + "Flags": [ + 1, + 4 + ] + } +})"; + EXPECT_EQ(JSONOut, FlagOutput(JOS, JSONWriter)); +} + +TEST(ScopedPrinterTest, PrintBoolean) { + auto BooleanOutput = [](raw_string_ostream &OS, ScopedPrinter &W) { + { + DictScope D(W); + W.printBoolean("True", true); + W.printBoolean("False", false); + } + return OS.str(); + }; + + std::string ScopedString; + llvm::raw_string_ostream OS(ScopedString); + ScopedPrinter Writer(OS); + const char *Out = R"({ + True: Yes + False: No +} +)"; + EXPECT_EQ(Out, BooleanOutput(OS, Writer)); + + std::string JSONScopedString; + llvm::raw_string_ostream JOS(JSONScopedString); + JSONScopedPrinter JSONWriter(JOS); + const char *JSONOut = R"({ + "True": true, + "False": false +})"; + EXPECT_EQ(JSONOut, BooleanOutput(JOS, JSONWriter)); +} + +TEST(ScopedPrinterTest, PrintVersion) { + auto VersionOutput = [](raw_string_ostream &OS, ScopedPrinter &W) { + { + DictScope D(W); + W.printVersion("Version", "123", "456", "789"); + } + return OS.str(); + }; + std::string ScopedString; + llvm::raw_string_ostream OS(ScopedString); + ScopedPrinter Writer(OS); + const char *Out = R"({ + Version: 123.456.789 +} +)"; + EXPECT_EQ(Out, VersionOutput(OS, Writer)); +} + +TEST(ScopedPrinterTest, PrintList) { + auto ListOutput = [](raw_string_ostream &OS, ScopedPrinter &W) { + { + const std::vector EmptyList; + const int NumberList[] = {1, 2, 3}; + const std::string StringList[] = {"foo", "bar", "baz"}; + DictScope D(W); + W.printList("EmptyList", EmptyList); + W.printList("NumberList", NumberList); + W.printList("StringList", StringList); + } + return OS.str(); + }; + + std::string ScopedString; + llvm::raw_string_ostream OS(ScopedString); + ScopedPrinter Writer(OS); + const char *Out = R"({ + EmptyList: [] + NumberList: [1, 2, 3] + StringList: [foo, bar, baz] +} +)"; + EXPECT_EQ(Out, ListOutput(OS, Writer)); + + std::string JSONScopedString; + llvm::raw_string_ostream JOS(JSONScopedString); + JSONScopedPrinter JSONWriter(JOS); + const char *JSONOut = R"({ + "EmptyList": [], + "NumberList": [ + 1, + 2, + 3 + ], + "StringList": [ + "foo", + "bar", + "baz" + ] +})"; + EXPECT_EQ(JSONOut, ListOutput(JOS, JSONWriter)); +} + +TEST(ScopedPrinterTest, PrintHex) { + auto HexOutput = [](raw_string_ostream &OS, ScopedPrinter &W) { + { + DictScope D(W); + const uint64_t HexList[] = {0x1, 0x10, 0x100}; + W.printHexList("HexList", HexList); + W.printHex("HexNumber", 0x10); + W.printHex("HexLabel", "Name", 0x10); + W.printSymbolOffset("SymbolOffset", "SymbolName", 0x10); + } + return OS.str(); + }; + + std::string ScopedString; + llvm::raw_string_ostream OS(ScopedString); + ScopedPrinter Writer(OS); + const char *Out = R"({ + HexList: [0x1, 0x10, 0x100] + HexNumber: 0x10 + HexLabel: Name (0x10) + SymbolOffset: SymbolName+0x10 +} +)"; + EXPECT_EQ(Out, HexOutput(OS, Writer)); + + std::string JSONScopedString; + llvm::raw_string_ostream JOS(JSONScopedString); + JSONScopedPrinter JSONWriter(JOS); + const char *JSONOut = R"({ + "HexList": [ + 1, + 16, + 256 + ], + "HexNumber": 16, + "HexLabel": { + "Value": "Name", + "RawValue": 16 + }, + "SymbolOffset": { + "SymName": "SymbolName", + "Offset": 16 + } +})"; + EXPECT_EQ(JSONOut, HexOutput(JOS, JSONWriter)); +} + +TEST(ScopedPrinterTest, PrintString) { + auto StringOutput = [](raw_string_ostream &OS, ScopedPrinter &W) { + { + DictScope D(W); + const StringRef StringRefValue("Value"); + const std::string StringValue = "Value"; + const char *CharArrayValue = "Value"; + W.printString("StringRef", StringRefValue); + W.printString("String", StringValue); + W.printString("CharArray", CharArrayValue); + ListScope L(W, "StringList"); + W.printString(StringRefValue); + W.printString(StringRefValue); + } + return OS.str(); + }; + + std::string ScopedString; + llvm::raw_string_ostream OS(ScopedString); + ScopedPrinter Writer(OS); + const char *Out = R"({ + StringRef: Value + String: Value + CharArray: Value + StringList [ + Value + Value + ] +} +)"; + EXPECT_EQ(Out, StringOutput(OS, Writer)); + + std::string JSONScopedString; + llvm::raw_string_ostream JOS(JSONScopedString); + JSONScopedPrinter JSONWriter(JOS); + const char *JSONOut = R"({ + "StringRef": "Value", + "String": "Value", + "CharArray": "Value", + "StringList": [ + "Value", + "Value" + ] +})"; + EXPECT_EQ(JSONOut, StringOutput(JOS, JSONWriter)); +} + +TEST(ScopedPrinterTest, PrintBinary) { + auto BinaryOutput = [](raw_string_ostream &OS, ScopedPrinter &W) { + { + DictScope D(W); + const uint8_t IntArray[] = {70, 111, 111, 66, 97, 114}; + const char CharArray[] = {'F', 'o', 'o', 'B', 'a', 'r'}; + W.printBinary("Binary1", "FooBar", makeArrayRef(IntArray)); + W.printBinary("Binary2", "FooBar", makeArrayRef(CharArray)); + W.printBinary("Binary3", makeArrayRef(IntArray)); + W.printBinary("Binary4", makeArrayRef(CharArray)); + W.printBinaryBlock("Binary5", makeArrayRef(IntArray), 20); + W.printBinaryBlock("Binary6", makeArrayRef(IntArray)); + W.printBinaryBlock("Binary7", "FooBar"); + } + return OS.str(); + }; + + std::string ScopedString; + llvm::raw_string_ostream OS(ScopedString); + ScopedPrinter Writer(OS); + const char *Out = R"({ + Binary1: FooBar (46 6F 6F 42 61 72) + Binary2: FooBar (46 6F 6F 42 61 72) + Binary3: (46 6F 6F 42 61 72) + Binary4: (46 6F 6F 42 61 72) + Binary5 ( + 0014: 466F6F42 6172 |FooBar| + ) + Binary6 ( + 0000: 466F6F42 6172 |FooBar| + ) + Binary7 ( + 0000: 466F6F42 6172 |FooBar| + ) +} +)"; + EXPECT_EQ(Out, BinaryOutput(OS, Writer)); + + std::string JSONScopedString; + llvm::raw_string_ostream JOS(JSONScopedString); + JSONScopedPrinter JSONWriter(JOS); + const char *JSONOut = R"({ + "Binary1": { + "Value": "FooBar", + "Bytes": { + "0": 70, + "1": 111, + "2": 111, + "3": 66, + "4": 97, + "5": 114 + } + }, + "Binary2": { + "Value": "FooBar", + "Bytes": { + "0": 70, + "1": 111, + "2": 111, + "3": 66, + "4": 97, + "5": 114 + } + }, + "Binary3": { + "Bytes": { + "0": 70, + "1": 111, + "2": 111, + "3": 66, + "4": 97, + "5": 114 + } + }, + "Binary4": { + "Bytes": { + "0": 70, + "1": 111, + "2": 111, + "3": 66, + "4": 97, + "5": 114 + } + }, + "Binary5": { + "Bytes": { + "20": 70, + "21": 111, + "22": 111, + "23": 66, + "24": 97, + "25": 114 + }, + "Characters": { + "20": "F", + "21": "o", + "22": "o", + "23": "B", + "24": "a", + "25": "r" + } + }, + "Binary6": { + "Bytes": { + "0": 70, + "1": 111, + "2": 111, + "3": 66, + "4": 97, + "5": 114 + }, + "Characters": { + "0": "F", + "1": "o", + "2": "o", + "3": "B", + "4": "a", + "5": "r" + } + }, + "Binary7": { + "Bytes": { + "0": 70, + "1": 111, + "2": 111, + "3": 66, + "4": 97, + "5": 114 + }, + "Characters": { + "0": "F", + "1": "o", + "2": "o", + "3": "B", + "4": "a", + "5": "r" + } + } +})"; + EXPECT_EQ(JSONOut, BinaryOutput(JOS, JSONWriter)); +} + +TEST(ScopedPrinterTest, PrintObject) { + auto ObjectOutput = [](raw_string_ostream &OS, ScopedPrinter &W) { + { + DictScope D(W); + W.printObject("Object", "Value"); + } + return OS.str(); + }; + + std::string ScopedString; + llvm::raw_string_ostream OS(ScopedString); + ScopedPrinter Writer(OS); + const char *Out = R"({ + Object: Value +} +)"; + EXPECT_EQ(Out, ObjectOutput(OS, Writer)); + + std::string JSONScopedString; + llvm::raw_string_ostream JOS(JSONScopedString); + JSONScopedPrinter JSONWriter(JOS); + const char *JSONOut = R"({ + "Object": "Value" +})"; + EXPECT_EQ(JSONOut, ObjectOutput(JOS, JSONWriter)); +} + +TEST(ScopedPrinterTest, PrintScope) { + auto ScopeOutput = [](raw_string_ostream &OS, ScopedPrinter &W) { + { + DictScope D(W); + { + DictScope O(W, "Object"); + { DictScope OO(W, "ObjectInObject"); } + { ListScope LO(W, "ListInObject"); } + } + { + ListScope L(W, "List"); + { DictScope OL(W, "ObjectInList"); } + { ListScope LL(W, "ListInList"); } + } + } + return OS.str(); + }; + + std::string ScopedString; + llvm::raw_string_ostream OS(ScopedString); + ScopedPrinter Writer(OS); + const char *Out = R"({ + Object { + ObjectInObject { + } + ListInObject [ + ] + } + List [ + ObjectInList { + } + ListInList [ + ] + ] +} +)"; + EXPECT_EQ(Out, ScopeOutput(OS, Writer)); + + std::string JSONScopedString; + llvm::raw_string_ostream JOS(JSONScopedString); + JSONScopedPrinter JSONWriter(JOS); + const char *JSONOut = R"({ + "Object": { + "ObjectInObject": {}, + "ListInObject": [] + }, + "List": [ + { + "ObjectInList": {} + }, + { + "ListInList": [] + } + ] +})"; + EXPECT_EQ(JSONOut, ScopeOutput(JOS, JSONWriter)); +} + +} // namespace