diff options
Diffstat (limited to 'lib/Support/JSON.cpp')
-rw-r--r-- | lib/Support/JSON.cpp | 221 |
1 files changed, 123 insertions, 98 deletions
diff --git a/lib/Support/JSON.cpp b/lib/Support/JSON.cpp index d468013fb94a..95e5ed654277 100644 --- a/lib/Support/JSON.cpp +++ b/lib/Support/JSON.cpp @@ -1,9 +1,8 @@ //=== JSON.cpp - JSON value, parsing and serialization - C++ -----------*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===---------------------------------------------------------------------===// @@ -182,6 +181,12 @@ bool operator==(const Value &L, const Value &R) { case Value::Boolean: return *L.getAsBoolean() == *R.getAsBoolean(); case Value::Number: + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 + // The same integer must convert to the same double, per the standard. + // However we see 64-vs-80-bit precision comparisons with gcc-7 -O3 -m32. + // So we avoid floating point promotion for exact comparisons. + if (L.Type == Value::T_Integer || R.Type == Value::T_Integer) + return L.getAsInteger() == R.getAsInteger(); return *L.getAsNumber() == *R.getAsNumber(); case Value::String: return *L.getAsString() == *R.getAsString(); @@ -555,9 +560,6 @@ std::string fixUTF8(llvm::StringRef S) { return Res; } -} // namespace json -} // namespace llvm - static void quote(llvm::raw_ostream &OS, llvm::StringRef S) { OS << '\"'; for (unsigned char C : S) { @@ -588,106 +590,129 @@ static void quote(llvm::raw_ostream &OS, llvm::StringRef S) { OS << '\"'; } -enum IndenterAction { - Indent, - Outdent, - Newline, - Space, -}; - -// Prints JSON. The indenter can be used to control formatting. -template <typename Indenter> -void llvm::json::Value::print(raw_ostream &OS, const Indenter &I) const { - switch (Type) { - case T_Null: +void llvm::json::OStream::value(const Value &V) { + switch (V.kind()) { + case Value::Null: + valueBegin(); OS << "null"; - break; - case T_Boolean: - OS << (as<bool>() ? "true" : "false"); - break; - case T_Double: - OS << format("%.*g", std::numeric_limits<double>::max_digits10, - as<double>()); - break; - case T_Integer: - OS << as<int64_t>(); - break; - case T_StringRef: - quote(OS, as<StringRef>()); - break; - case T_String: - quote(OS, as<std::string>()); - break; - case T_Object: { - bool Comma = false; - OS << '{'; - I(Indent); - for (const auto *P : sortedElements(as<json::Object>())) { - if (Comma) - OS << ','; - Comma = true; - I(Newline); - quote(OS, P->first); - OS << ':'; - I(Space); - P->second.print(OS, I); - } - I(Outdent); - if (Comma) - I(Newline); - OS << '}'; - break; + return; + case Value::Boolean: + valueBegin(); + OS << (*V.getAsBoolean() ? "true" : "false"); + return; + case Value::Number: + valueBegin(); + if (V.Type == Value::T_Integer) + OS << *V.getAsInteger(); + else + OS << format("%.*g", std::numeric_limits<double>::max_digits10, + *V.getAsNumber()); + return; + case Value::String: + valueBegin(); + quote(OS, *V.getAsString()); + return; + case Value::Array: + return array([&] { + for (const Value &E : *V.getAsArray()) + value(E); + }); + case Value::Object: + return object([&] { + for (const Object::value_type *E : sortedElements(*V.getAsObject())) + attribute(E->first, E->second); + }); } - case T_Array: { - bool Comma = false; - OS << '['; - I(Indent); - for (const auto &E : as<json::Array>()) { - if (Comma) - OS << ','; - Comma = true; - I(Newline); - E.print(OS, I); - } - I(Outdent); - if (Comma) - I(Newline); - OS << ']'; - break; +} + +void llvm::json::OStream::valueBegin() { + assert(Stack.back().Ctx != Object && "Only attributes allowed here"); + if (Stack.back().HasValue) { + assert(Stack.back().Ctx != Singleton && "Only one value allowed here"); + OS << ','; + } + if (Stack.back().Ctx == Array) + newline(); + Stack.back().HasValue = true; +} + +void llvm::json::OStream::newline() { + if (IndentSize) { + OS.write('\n'); + OS.indent(Indent); } +} + +void llvm::json::OStream::arrayBegin() { + valueBegin(); + Stack.emplace_back(); + Stack.back().Ctx = Array; + Indent += IndentSize; + OS << '['; +} + +void llvm::json::OStream::arrayEnd() { + assert(Stack.back().Ctx == Array); + Indent -= IndentSize; + if (Stack.back().HasValue) + newline(); + OS << ']'; + Stack.pop_back(); + assert(!Stack.empty()); +} + +void llvm::json::OStream::objectBegin() { + valueBegin(); + Stack.emplace_back(); + Stack.back().Ctx = Object; + Indent += IndentSize; + OS << '{'; +} + +void llvm::json::OStream::objectEnd() { + assert(Stack.back().Ctx == Object); + Indent -= IndentSize; + if (Stack.back().HasValue) + newline(); + OS << '}'; + Stack.pop_back(); + assert(!Stack.empty()); +} + +void llvm::json::OStream::attributeBegin(llvm::StringRef Key) { + assert(Stack.back().Ctx == Object); + if (Stack.back().HasValue) + OS << ','; + newline(); + Stack.back().HasValue = true; + Stack.emplace_back(); + Stack.back().Ctx = Singleton; + if (LLVM_LIKELY(isUTF8(Key))) { + quote(OS, Key); + } else { + assert(false && "Invalid UTF-8 in attribute key"); + quote(OS, fixUTF8(Key)); } + OS.write(':'); + if (IndentSize) + OS.write(' '); +} + +void llvm::json::OStream::attributeEnd() { + assert(Stack.back().Ctx == Singleton); + assert(Stack.back().HasValue && "Attribute must have a value"); + Stack.pop_back(); + assert(Stack.back().Ctx == Object); } +} // namespace json +} // namespace llvm + void llvm::format_provider<llvm::json::Value>::format( const llvm::json::Value &E, raw_ostream &OS, StringRef Options) { - if (Options.empty()) { - OS << E; - return; - } unsigned IndentAmount = 0; - if (Options.getAsInteger(/*Radix=*/10, IndentAmount)) + if (!Options.empty() && Options.getAsInteger(/*Radix=*/10, IndentAmount)) llvm_unreachable("json::Value format options should be an integer"); - unsigned IndentLevel = 0; - E.print(OS, [&](IndenterAction A) { - switch (A) { - case Newline: - OS << '\n'; - OS.indent(IndentLevel); - break; - case Space: - OS << ' '; - break; - case Indent: - IndentLevel += IndentAmount; - break; - case Outdent: - IndentLevel -= IndentAmount; - break; - }; - }); + json::OStream(OS, IndentAmount).value(E); } -llvm::raw_ostream &llvm::json::operator<<(raw_ostream &OS, const Value &E) { - E.print(OS, [](IndenterAction A) { /*ignore*/ }); - return OS; -} |