Import cobalt 25.master.0.1034729
diff --git a/base/json/OWNERS b/base/json/OWNERS index e44d995..3e509a9 100644 --- a/base/json/OWNERS +++ b/base/json/OWNERS
@@ -1 +1,3 @@ file://base/SECURITY_OWNERS + +per-file values_util*=alancutter@chromium.org,jdoerrie@chromium.org
diff --git a/base/json/json_common.h b/base/json/json_common.h new file mode 100644 index 0000000..3eb3a79 --- /dev/null +++ b/base/json/json_common.h
@@ -0,0 +1,44 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_JSON_JSON_COMMON_H_ +#define BASE_JSON_JSON_COMMON_H_ + +#include <stddef.h> + +#include "base/check_op.h" +#include "base/memory/raw_ptr.h" + +namespace base { +namespace internal { + +// Chosen to support 99.9% of documents found in the wild late 2016. +// http://crbug.com/673263 +const size_t kAbsoluteMaxDepth = 200; + +// Simple class that checks for maximum recursion/stack overflow. +class StackMarker { + public: + StackMarker(size_t max_depth, size_t* depth) + : max_depth_(max_depth), depth_(depth) { + ++(*depth_); + DCHECK_LE(*depth_, max_depth_); + } + + StackMarker(const StackMarker&) = delete; + StackMarker& operator=(const StackMarker&) = delete; + + ~StackMarker() { --(*depth_); } + + bool IsTooDeep() const { return *depth_ >= max_depth_; } + + private: + const size_t max_depth_; + const raw_ptr<size_t> depth_; +}; + +} // namespace internal +} // namespace base + +#endif // BASE_JSON_JSON_COMMON_H_
diff --git a/base/json/json_correctness_fuzzer.cc b/base/json/json_correctness_fuzzer.cc index c7d6d6f..eb76ead 100644 --- a/base/json/json_correctness_fuzzer.cc +++ b/base/json/json_correctness_fuzzer.cc
@@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2016 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,6 +6,9 @@ // The fuzzer input is passed through parsing twice, // so that presumably valid json is parsed/written again. +#include <stddef.h> +#include <stdint.h> + #include <string> #include "base/json/json_reader.h" @@ -13,8 +16,6 @@ #include "base/json/string_escape.h" #include "base/logging.h" #include "base/values.h" -#include "starboard/memory.h" -#include "starboard/types.h" // Entry point for libFuzzer. // We will use the last byte of data as parsing options. @@ -23,9 +24,6 @@ if (size < 2) return 0; - int error_code, error_line, error_column; - std::string error_message; - // Create a copy of input buffer, as otherwise we don't catch // overflow that touches the last byte (which is used in options). std::unique_ptr<char[]> input(new char[size - 1]); @@ -34,23 +32,20 @@ base::StringPiece input_string(input.get(), size - 1); const int options = data[size - 1]; - auto parsed_value = base::JSONReader::ReadAndReturnError( - input_string, options, &error_code, &error_message, &error_line, - &error_column); - if (!parsed_value) + auto result = + base::JSONReader::ReadAndReturnValueWithError(input_string, options); + if (!result.has_value()) return 0; std::string parsed_output; - bool b = base::JSONWriter::Write(*parsed_value, &parsed_output); + bool b = base::JSONWriter::Write(*result, &parsed_output); LOG_ASSERT(b); - auto double_parsed_value = base::JSONReader::ReadAndReturnError( - parsed_output, options, &error_code, &error_message, &error_line, - &error_column); - LOG_ASSERT(double_parsed_value); + auto double_result = + base::JSONReader::ReadAndReturnValueWithError(parsed_output, options); + LOG_ASSERT(double_result.has_value()); std::string double_parsed_output; - bool b2 = - base::JSONWriter::Write(*double_parsed_value, &double_parsed_output); + bool b2 = base::JSONWriter::Write(*double_result, &double_parsed_output); LOG_ASSERT(b2); LOG_ASSERT(parsed_output == double_parsed_output)
diff --git a/base/json/json_file_value_serializer.cc b/base/json/json_file_value_serializer.cc index 6ec275c..d7d72c0 100644 --- a/base/json/json_file_value_serializer.cc +++ b/base/json/json_file_value_serializer.cc
@@ -1,12 +1,13 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/json/json_file_value_serializer.h" +#include "base/check.h" #include "base/files/file_util.h" #include "base/json/json_string_value_serializer.h" -#include "base/logging.h" +#include "base/notreached.h" #include "build/build_config.h" using base::FilePath; @@ -23,46 +24,42 @@ JSONFileValueSerializer::~JSONFileValueSerializer() = default; -bool JSONFileValueSerializer::Serialize(const base::Value& root) { +bool JSONFileValueSerializer::Serialize(base::ValueView root) { return SerializeInternal(root, false); } bool JSONFileValueSerializer::SerializeAndOmitBinaryValues( - const base::Value& root) { + base::ValueView root) { return SerializeInternal(root, true); } -bool JSONFileValueSerializer::SerializeInternal(const base::Value& root, +bool JSONFileValueSerializer::SerializeInternal(base::ValueView root, bool omit_binary_values) { std::string json_string; JSONStringValueSerializer serializer(&json_string); serializer.set_pretty_print(true); - bool result = omit_binary_values ? - serializer.SerializeAndOmitBinaryValues(root) : - serializer.Serialize(root); + bool result = omit_binary_values + ? serializer.SerializeAndOmitBinaryValues(root) + : serializer.Serialize(root); if (!result) return false; - int data_size = static_cast<int>(json_string.size()); - if (base::WriteFile(json_file_path_, json_string.data(), data_size) != - data_size) - return false; - - return true; + return base::WriteFile(json_file_path_, json_string); } JSONFileValueDeserializer::JSONFileValueDeserializer( const base::FilePath& json_file_path, int options) - : json_file_path_(json_file_path), options_(options), last_read_size_(0U) {} + : json_file_path_(json_file_path), options_(options) {} JSONFileValueDeserializer::~JSONFileValueDeserializer() = default; int JSONFileValueDeserializer::ReadFileToString(std::string* json_string) { DCHECK(json_string); + last_read_size_ = 0u; if (!base::ReadFileToString(json_file_path_, json_string)) { -#if defined(OS_WIN) - int error = ::GetLastError(); +#if BUILDFLAG(IS_WIN) + DWORD error = ::GetLastError(); if (error == ERROR_SHARING_VIOLATION || error == ERROR_LOCK_VIOLATION) { return JSON_FILE_LOCKED; } else if (error == ERROR_ACCESS_DENIED) {
diff --git a/base/json/json_file_value_serializer.h b/base/json/json_file_value_serializer.h index 6a726d6..16ceff5 100644 --- a/base/json/json_file_value_serializer.h +++ b/base/json/json_file_value_serializer.h
@@ -1,72 +1,90 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_JSON_JSON_FILE_VALUE_SERIALIZER_H_ #define BASE_JSON_JSON_FILE_VALUE_SERIALIZER_H_ +#include <stddef.h> + +#include <memory> #include <string> #include "base/base_export.h" #include "base/files/file_path.h" -#include "base/macros.h" +#include "base/json/json_reader.h" #include "base/values.h" -#include "starboard/types.h" class BASE_EXPORT JSONFileValueSerializer : public base::ValueSerializer { public: + JSONFileValueSerializer() = delete; + // |json_file_path_| is the path of a file that will be destination of the // serialization. The serializer will attempt to create the file at the // specified location. explicit JSONFileValueSerializer(const base::FilePath& json_file_path); + JSONFileValueSerializer(const JSONFileValueSerializer&) = delete; + JSONFileValueSerializer& operator=(const JSONFileValueSerializer&) = delete; + ~JSONFileValueSerializer() override; // DO NOT USE except in unit tests to verify the file was written properly. // We should never serialize directly to a file since this will block the // thread. Instead, serialize to a string and write to the file you want on - // the file thread. + // the thread pool. // // Attempt to serialize the data structure represented by Value into // JSON. If the return value is true, the result will have been written // into the file whose name was passed into the constructor. - bool Serialize(const base::Value& root) override; + bool Serialize(base::ValueView root) override; // Equivalent to Serialize(root) except binary values are omitted from the // output. - bool SerializeAndOmitBinaryValues(const base::Value& root); + bool SerializeAndOmitBinaryValues(base::ValueView root); private: - bool SerializeInternal(const base::Value& root, bool omit_binary_values); + bool SerializeInternal(base::ValueView root, bool omit_binary_values); const base::FilePath json_file_path_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueSerializer); }; class BASE_EXPORT JSONFileValueDeserializer : public base::ValueDeserializer { public: + JSONFileValueDeserializer() = delete; + // |json_file_path_| is the path of a file that will be source of the // deserialization. |options| is a bitmask of JSONParserOptions. - explicit JSONFileValueDeserializer(const base::FilePath& json_file_path, - int options = 0); + explicit JSONFileValueDeserializer( + const base::FilePath& json_file_path, + int options = base::JSON_PARSE_CHROMIUM_EXTENSIONS); + + JSONFileValueDeserializer(const JSONFileValueDeserializer&) = delete; + JSONFileValueDeserializer& operator=(const JSONFileValueDeserializer&) = + delete; ~JSONFileValueDeserializer() override; - // Attempt to deserialize the data structure encoded in the file passed - // in to the constructor into a structure of Value objects. If the return - // value is NULL, and if |error_code| is non-null, |error_code| will - // contain an integer error code (either JsonFileError or JsonParseError). - // If |error_message| is non-null, it will be filled in with a formatted - // error message including the location of the error if appropriate. + // Attempts to deserialize the data structure encoded in the file passed to + // the constructor into a structure of Value objects. If the return value is + // null, then + // (1) |error_code| will be filled with an integer error code (either a + // JsonFileError or base::ValueDeserializer::kErrorCodeInvalidFormat) if a + // non-null |error_code| was given. + // (2) |error_message| will be filled with a formatted error message, + // including the location of the error (if appropriate), if a non-null + // |error_message| was given. // The caller takes ownership of the returned value. std::unique_ptr<base::Value> Deserialize(int* error_code, std::string* error_message) override; - // This enum is designed to safely overlap with JSONReader::JsonParseError. + // This enum is designed to safely overlap with JSONParser::JsonParseError. + // + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. enum JsonFileError { JSON_NO_ERROR = 0, - JSON_ACCESS_DENIED = 1000, + JSON_ACCESS_DENIED = kErrorCodeFirstMetadataError, JSON_CANNOT_READ_FILE, JSON_FILE_LOCKED, JSON_NO_SUCH_FILE @@ -93,9 +111,7 @@ const base::FilePath json_file_path_; const int options_; - size_t last_read_size_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueDeserializer); + size_t last_read_size_ = 0u; }; #endif // BASE_JSON_JSON_FILE_VALUE_SERIALIZER_H_
diff --git a/base/json/json_parser.cc b/base/json/json_parser.cc index 057f769..fbb226e 100644 --- a/base/json/json_parser.cc +++ b/base/json/json_parser.cc
@@ -1,16 +1,20 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/json/json_parser.h" #include <cmath> +#include <iterator> #include <utility> #include <vector> -#include "base/logging.h" -#include "base/macros.h" +#include "base/check_op.h" +#include "base/json/json_reader.h" +#include "base/metrics/histogram_functions.h" +#include "base/notreached.h" #include "base/numerics/safe_conversions.h" +#include "base/ranges/algorithm.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/string_util.h" @@ -18,101 +22,156 @@ #include "base/strings/utf_string_conversion_utils.h" #include "base/strings/utf_string_conversions.h" #include "base/third_party/icu/icu_utf.h" -#include "base/values.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace base { namespace internal { namespace { +// Values 1000 and above are used by JSONFileValueSerializer::JsonFileError. +static_assert(JSONParser::JSON_PARSE_ERROR_COUNT < 1000, + "JSONParser error out of bounds"); + +std::string ErrorCodeToString(JSONParser::JsonParseError error_code) { + switch (error_code) { + case JSONParser::JSON_NO_ERROR: + return std::string(); + case JSONParser::JSON_SYNTAX_ERROR: + return JSONParser::kSyntaxError; + case JSONParser::JSON_INVALID_ESCAPE: + return JSONParser::kInvalidEscape; + case JSONParser::JSON_UNEXPECTED_TOKEN: + return JSONParser::kUnexpectedToken; + case JSONParser::JSON_TRAILING_COMMA: + return JSONParser::kTrailingComma; + case JSONParser::JSON_TOO_MUCH_NESTING: + return JSONParser::kTooMuchNesting; + case JSONParser::JSON_UNEXPECTED_DATA_AFTER_ROOT: + return JSONParser::kUnexpectedDataAfterRoot; + case JSONParser::JSON_UNSUPPORTED_ENCODING: + return JSONParser::kUnsupportedEncoding; + case JSONParser::JSON_UNQUOTED_DICTIONARY_KEY: + return JSONParser::kUnquotedDictionaryKey; + case JSONParser::JSON_UNREPRESENTABLE_NUMBER: + return JSONParser::kUnrepresentableNumber; + case JSONParser::JSON_PARSE_ERROR_COUNT: + break; + } + NOTREACHED(); + return std::string(); +} + const int32_t kExtendedASCIIStart = 0x80; +constexpr base_icu::UChar32 kUnicodeReplacementPoint = 0xFFFD; -// Simple class that checks for maximum recursion/"stack overflow." -class StackMarker { - public: - StackMarker(int max_depth, int* depth) - : max_depth_(max_depth), depth_(depth) { - ++(*depth_); - DCHECK_LE(*depth_, max_depth_); +// UnprefixedHexStringToInt acts like |HexStringToInt|, but enforces that the +// input consists purely of hex digits. I.e. no "0x" nor "OX" prefix is +// permitted. +bool UnprefixedHexStringToInt(StringPiece input, int* output) { + for (size_t i = 0; i < input.size(); i++) { + if (!IsHexDigit(input[i])) { + return false; + } } - ~StackMarker() { - --(*depth_); - } + return HexStringToInt(input, output); +} - bool IsTooDeep() const { return *depth_ >= max_depth_; } - - private: - const int max_depth_; - int* const depth_; - - DISALLOW_COPY_AND_ASSIGN(StackMarker); +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum class ChromiumJsonExtension { + kCComment, + kCppComment, + kXEscape, + kVerticalTabEscape, + kControlCharacter, + kMaxValue = kControlCharacter, }; -constexpr uint32_t kUnicodeReplacementPoint = 0xFFFD; +const char kExtensionHistogramName[] = + "Security.JSONParser.ChromiumExtensionUsage"; } // namespace // This is U+FFFD. const char kUnicodeReplacementString[] = "\xEF\xBF\xBD"; -JSONParser::JSONParser(int options, int max_depth) +const char JSONParser::kSyntaxError[] = "Syntax error."; +const char JSONParser::kInvalidEscape[] = "Invalid escape sequence."; +const char JSONParser::kUnexpectedToken[] = "Unexpected token."; +const char JSONParser::kTrailingComma[] = "Trailing comma not allowed."; +const char JSONParser::kTooMuchNesting[] = "Too much nesting."; +const char JSONParser::kUnexpectedDataAfterRoot[] = + "Unexpected data after root element."; +const char JSONParser::kUnsupportedEncoding[] = + "Unsupported encoding. JSON must be UTF-8."; +const char JSONParser::kUnquotedDictionaryKey[] = + "Dictionary keys must be quoted."; +const char JSONParser::kUnrepresentableNumber[] = + "Number cannot be represented."; + +JSONParser::JSONParser(int options, size_t max_depth) : options_(options), max_depth_(max_depth), index_(0), stack_depth_(0), line_number_(0), index_last_line_(0), - error_code_(JSONReader::JSON_NO_ERROR), + error_code_(JSON_NO_ERROR), error_line_(0), error_column_(0) { - CHECK_LE(max_depth, JSONReader::kStackMaxDepth); + CHECK_LE(max_depth, kAbsoluteMaxDepth); } JSONParser::~JSONParser() = default; -Optional<Value> JSONParser::Parse(StringPiece input) { +absl::optional<Value> JSONParser::Parse(StringPiece input) { input_ = input; index_ = 0; + // Line and column counting is 1-based, but |index_| is 0-based. For example, + // if input is "Aaa\nB" then 'A' and 'B' are both in column 1 (at lines 1 and + // 2) and have indexes of 0 and 4. We track the line number explicitly (the + // |line_number_| field) and the column number implicitly (the difference + // between |index_| and |index_last_line_|). In calculating that difference, + // |index_last_line_| is the index of the '\r' or '\n', not the index of the + // first byte after the '\n'. For the 'B' in "Aaa\nB", its |index_| and + // |index_last_line_| would be 4 and 3: 'B' is in column (4 - 3) = 1. We + // initialize |index_last_line_| to -1, not 0, since -1 is the (out of range) + // index of the imaginary '\n' immediately before the start of the string: + // 'A' is in column (0 - -1) = 1. line_number_ = 1; - index_last_line_ = 0; + index_last_line_ = static_cast<size_t>(-1); - error_code_ = JSONReader::JSON_NO_ERROR; + error_code_ = JSON_NO_ERROR; error_line_ = 0; error_column_ = 0; - // ICU and ReadUnicodeCharacter() use int32_t for lengths, so ensure - // that the index_ will not overflow when parsing. - if (!base::IsValueInRangeForNumericType<int32_t>(input.length())) { - ReportError(JSONReader::JSON_TOO_LARGE, 0); - return nullopt; - } - // When the input JSON string starts with a UTF-8 Byte-Order-Mark, // advance the start position to avoid the ParseNextToken function mis- // treating a Unicode BOM as an invalid character and returning NULL. ConsumeIfMatch("\xEF\xBB\xBF"); // Parse the first and any nested tokens. - Optional<Value> root(ParseNextToken()); + absl::optional<Value> root(ParseNextToken()); if (!root) - return nullopt; + return absl::nullopt; // Make sure the input stream is at an end. if (GetNextToken() != T_END_OF_INPUT) { - ReportError(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, 1); - return nullopt; + ReportError(JSON_UNEXPECTED_DATA_AFTER_ROOT, 0); + return absl::nullopt; } return root; } -JSONReader::JsonParseError JSONParser::error_code() const { +JSONParser::JsonParseError JSONParser::error_code() const { return error_code_; } std::string JSONParser::GetErrorMessage() const { return FormatErrorMessage(error_line_, error_column_, - JSONReader::ErrorCodeToString(error_code_)); + ErrorCodeToString(error_code_)); } int JSONParser::error_line() const { @@ -135,12 +194,16 @@ JSONParser::StringBuilder& JSONParser::StringBuilder::operator=( StringBuilder&& other) = default; -void JSONParser::StringBuilder::Append(uint32_t point) { - DCHECK(IsValidCharacter(point)); +void JSONParser::StringBuilder::Append(base_icu::UChar32 point) { + DCHECK(IsValidCodepoint(point)); - if (point < kExtendedASCIIStart && !string_) { - DCHECK_EQ(static_cast<char>(point), pos_[length_]); - ++length_; + if (point < kExtendedASCIIStart) { + if (!string_) { + DCHECK_EQ(static_cast<char>(point), pos_[length_]); + ++length_; + } else { + string_->push_back(static_cast<char>(point)); + } } else { Convert(); if (UNLIKELY(point == kUnicodeReplacementPoint)) { @@ -165,44 +228,44 @@ // JSONParser private ////////////////////////////////////////////////////////// -Optional<StringPiece> JSONParser::PeekChars(int count) { - if (static_cast<size_t>(index_) + count > input_.length()) - return nullopt; +absl::optional<StringPiece> JSONParser::PeekChars(size_t count) { + if (index_ + count > input_.length()) + return absl::nullopt; // Using StringPiece::substr() is significantly slower (according to // base_perftests) than constructing a substring manually. return StringPiece(input_.data() + index_, count); } -Optional<char> JSONParser::PeekChar() { - Optional<StringPiece> chars = PeekChars(1); +absl::optional<char> JSONParser::PeekChar() { + absl::optional<StringPiece> chars = PeekChars(1); if (chars) return (*chars)[0]; - return nullopt; + return absl::nullopt; } -Optional<StringPiece> JSONParser::ConsumeChars(int count) { - Optional<StringPiece> chars = PeekChars(count); +absl::optional<StringPiece> JSONParser::ConsumeChars(size_t count) { + absl::optional<StringPiece> chars = PeekChars(count); if (chars) index_ += count; return chars; } -Optional<char> JSONParser::ConsumeChar() { - Optional<StringPiece> chars = ConsumeChars(1); +absl::optional<char> JSONParser::ConsumeChar() { + absl::optional<StringPiece> chars = ConsumeChars(1); if (chars) return (*chars)[0]; - return nullopt; + return absl::nullopt; } const char* JSONParser::pos() { - CHECK_LE(static_cast<size_t>(index_), input_.length()); + CHECK_LE(index_, input_.length()); return input_.data() + index_; } JSONParser::Token JSONParser::GetNextToken() { EatWhitespaceAndComments(); - Optional<char> c = PeekChar(); + absl::optional<char> c = PeekChar(); if (!c) return T_END_OF_INPUT; @@ -245,7 +308,7 @@ } void JSONParser::EatWhitespaceAndComments() { - while (Optional<char> c = PeekChar()) { + while (absl::optional<char> c = PeekChar()) { switch (*c) { case '\r': case '\n': @@ -254,7 +317,7 @@ if (!(c == '\n' && index_ > 0 && input_[index_ - 1] == '\r')) { ++line_number_; } - FALLTHROUGH; + [[fallthrough]]; case ' ': case '\t': ConsumeChar(); @@ -270,21 +333,39 @@ } bool JSONParser::EatComment() { - Optional<StringPiece> comment_start = ConsumeChars(2); + absl::optional<StringPiece> comment_start = PeekChars(2); if (!comment_start) return false; + const bool comments_allowed = options_ & JSON_ALLOW_COMMENTS; + if (comment_start == "//") { + UmaHistogramEnumeration(kExtensionHistogramName, + ChromiumJsonExtension::kCppComment); + if (!comments_allowed) { + ReportError(JSON_UNEXPECTED_TOKEN, 0); + return false; + } + + ConsumeChars(2); // Single line comment, read to newline. - while (Optional<char> c = PeekChar()) { + while (absl::optional<char> c = PeekChar()) { if (c == '\n' || c == '\r') return true; ConsumeChar(); } } else if (comment_start == "/*") { + UmaHistogramEnumeration(kExtensionHistogramName, + ChromiumJsonExtension::kCComment); + if (!comments_allowed) { + ReportError(JSON_UNEXPECTED_TOKEN, 0); + return false; + } + + ConsumeChars(2); char previous_char = '\0'; // Block comment, read until end marker. - while (Optional<char> c = PeekChar()) { + while (absl::optional<char> c = PeekChar()) { if (previous_char == '*' && c == '/') { // EatWhitespaceAndComments will inspect pos(), which will still be on // the last / of the comment, so advance once more (which may also be @@ -301,11 +382,11 @@ return false; } -Optional<Value> JSONParser::ParseNextToken() { +absl::optional<Value> JSONParser::ParseNextToken() { return ParseToken(GetNextToken()); } -Optional<Value> JSONParser::ParseToken(Token token) { +absl::optional<Value> JSONParser::ParseToken(Token token) { switch (token) { case T_OBJECT_BEGIN: return ConsumeDictionary(); @@ -320,129 +401,130 @@ case T_NULL: return ConsumeLiteral(); default: - ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); - return nullopt; + ReportError(JSON_UNEXPECTED_TOKEN, 0); + return absl::nullopt; } } -Optional<Value> JSONParser::ConsumeDictionary() { +absl::optional<Value> JSONParser::ConsumeDictionary() { if (ConsumeChar() != '{') { - ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); - return nullopt; + ReportError(JSON_UNEXPECTED_TOKEN, 0); + return absl::nullopt; } StackMarker depth_check(max_depth_, &stack_depth_); if (depth_check.IsTooDeep()) { - ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 0); - return nullopt; + ReportError(JSON_TOO_MUCH_NESTING, -1); + return absl::nullopt; } - std::vector<Value::DictStorage::value_type> dict_storage; + std::vector<std::pair<std::string, Value>> values; Token token = GetNextToken(); while (token != T_OBJECT_END) { if (token != T_STRING) { - ReportError(JSONReader::JSON_UNQUOTED_DICTIONARY_KEY, 1); - return nullopt; + ReportError(JSON_UNQUOTED_DICTIONARY_KEY, 0); + return absl::nullopt; } // First consume the key. StringBuilder key; if (!ConsumeStringRaw(&key)) { - return nullopt; + return absl::nullopt; } // Read the separator. token = GetNextToken(); if (token != T_OBJECT_PAIR_SEPARATOR) { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; + ReportError(JSON_SYNTAX_ERROR, 0); + return absl::nullopt; } // The next token is the value. Ownership transfers to |dict|. ConsumeChar(); - Optional<Value> value = ParseNextToken(); + absl::optional<Value> value = ParseNextToken(); if (!value) { // ReportError from deeper level. - return nullopt; + return absl::nullopt; } - dict_storage.emplace_back(key.DestructiveAsString(), - std::make_unique<Value>(std::move(*value))); + values.emplace_back(key.DestructiveAsString(), std::move(*value)); token = GetNextToken(); if (token == T_LIST_SEPARATOR) { ConsumeChar(); token = GetNextToken(); if (token == T_OBJECT_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) { - ReportError(JSONReader::JSON_TRAILING_COMMA, 1); - return nullopt; + ReportError(JSON_TRAILING_COMMA, 0); + return absl::nullopt; } } else if (token != T_OBJECT_END) { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 0); - return nullopt; + ReportError(JSON_SYNTAX_ERROR, 0); + return absl::nullopt; } } ConsumeChar(); // Closing '}'. - - return Value(Value::DictStorage(std::move(dict_storage), KEEP_LAST_OF_DUPES)); + // Reverse |dict_storage| to keep the last of elements with the same key in + // the input. + ranges::reverse(values); + return Value(Value::Dict(std::make_move_iterator(values.begin()), + std::make_move_iterator(values.end()))); } -Optional<Value> JSONParser::ConsumeList() { +absl::optional<Value> JSONParser::ConsumeList() { if (ConsumeChar() != '[') { - ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); - return nullopt; + ReportError(JSON_UNEXPECTED_TOKEN, 0); + return absl::nullopt; } StackMarker depth_check(max_depth_, &stack_depth_); if (depth_check.IsTooDeep()) { - ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 0); - return nullopt; + ReportError(JSON_TOO_MUCH_NESTING, -1); + return absl::nullopt; } - Value::ListStorage list_storage; + Value::List list; Token token = GetNextToken(); while (token != T_ARRAY_END) { - Optional<Value> item = ParseToken(token); + absl::optional<Value> item = ParseToken(token); if (!item) { // ReportError from deeper level. - return nullopt; + return absl::nullopt; } - list_storage.push_back(std::move(*item)); + list.Append(std::move(*item)); token = GetNextToken(); if (token == T_LIST_SEPARATOR) { ConsumeChar(); token = GetNextToken(); if (token == T_ARRAY_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) { - ReportError(JSONReader::JSON_TRAILING_COMMA, 1); - return nullopt; + ReportError(JSON_TRAILING_COMMA, 0); + return absl::nullopt; } } else if (token != T_ARRAY_END) { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; + ReportError(JSON_SYNTAX_ERROR, 0); + return absl::nullopt; } } ConsumeChar(); // Closing ']'. - return Value(std::move(list_storage)); + return Value(std::move(list)); } -Optional<Value> JSONParser::ConsumeString() { +absl::optional<Value> JSONParser::ConsumeString() { StringBuilder string; if (!ConsumeStringRaw(&string)) - return nullopt; - + return absl::nullopt; return Value(string.DestructiveAsString()); } bool JSONParser::ConsumeStringRaw(StringBuilder* out) { if (ConsumeChar() != '"') { - ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); + ReportError(JSON_UNEXPECTED_TOKEN, 0); return false; } @@ -451,15 +533,16 @@ // std::string. StringBuilder string(pos()); - while (PeekChar()) { - uint32_t next_char = 0; - if (!ReadUnicodeCharacter(input_.data(), - static_cast<int32_t>(input_.length()), - &index_, - &next_char) || - !IsValidCharacter(next_char)) { + while (absl::optional<char> c = PeekChar()) { + base_icu::UChar32 next_char = 0; + if (static_cast<unsigned char>(*c) < kExtendedASCIIStart) { + // Fast path for ASCII. + next_char = *c; + } else if (!ReadUnicodeCharacter(input_.data(), input_.length(), &index_, + &next_char) || + !IsValidCodepoint(next_char)) { if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) { - ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1); + ReportError(JSON_UNSUPPORTED_ENCODING, 0); return false; } ConsumeChar(); @@ -473,7 +556,32 @@ return true; } if (next_char != '\\') { - // If this character is not an escape sequence... + // Per Section 7, "All Unicode characters may be placed within the + // quotation marks, except for the characters that MUST be escaped: + // quotation mark, reverse solidus, and the control characters (U+0000 + // through U+001F)". + if (next_char <= 0x1F) { + UmaHistogramEnumeration(kExtensionHistogramName, + ChromiumJsonExtension::kControlCharacter); + if (!(options_ & JSON_ALLOW_CONTROL_CHARS)) { + ReportError(JSON_UNSUPPORTED_ENCODING, -1); + return false; + } + } + + // If this character is not an escape sequence, track any line breaks and + // copy next_char to the StringBuilder. The JSON spec forbids unescaped + // ASCII control characters within a string, including '\r' and '\n', but + // this implementation is more lenient. + if ((next_char == '\r') || (next_char == '\n')) { + index_last_line_ = index_; + // Don't increment line_number_ twice for "\r\n". We are guaranteed + // that (index_ > 0) because we are consuming a string, so we must have + // seen an opening '"' quote character. + if ((next_char == '\r') || (input_[index_ - 1] != '\r')) { + ++line_number_; + } + } ConsumeChar(); string.Append(next_char); } else { @@ -484,9 +592,9 @@ string.Convert(); // Read past the escape '\' and ensure there's a character following. - Optional<StringPiece> escape_sequence = ConsumeChars(2); + absl::optional<StringPiece> escape_sequence = ConsumeChars(2); if (!escape_sequence) { - ReportError(JSONReader::JSON_INVALID_ESCAPE, 0); + ReportError(JSON_INVALID_ESCAPE, -1); return false; } @@ -495,16 +603,23 @@ case 'x': { // UTF-8 sequence. // UTF-8 \x escape sequences are not allowed in the spec, but they // are supported here for backwards-compatiblity with the old parser. + UmaHistogramEnumeration(kExtensionHistogramName, + ChromiumJsonExtension::kXEscape); + if (!(options_ & JSON_ALLOW_X_ESCAPES)) { + ReportError(JSON_INVALID_ESCAPE, -1); + return false; + } + escape_sequence = ConsumeChars(2); if (!escape_sequence) { - ReportError(JSONReader::JSON_INVALID_ESCAPE, -2); + ReportError(JSON_INVALID_ESCAPE, -3); return false; } int hex_digit = 0; - if (!HexStringToInt(*escape_sequence, &hex_digit) || + if (!UnprefixedHexStringToInt(*escape_sequence, &hex_digit) || !IsValidCharacter(hex_digit)) { - ReportError(JSONReader::JSON_INVALID_ESCAPE, -2); + ReportError(JSON_INVALID_ESCAPE, -3); return false; } @@ -513,9 +628,9 @@ } case 'u': { // UTF-16 sequence. // UTF units are of the form \uXXXX. - uint32_t code_point; + base_icu::UChar32 code_point; if (!DecodeUTF16(&code_point)) { - ReportError(JSONReader::JSON_INVALID_ESCAPE, 0); + ReportError(JSON_INVALID_ESCAPE, -1); return false; } string.Append(code_point); @@ -546,71 +661,79 @@ string.Append('\t'); break; case 'v': // Not listed as valid escape sequence in the RFC. + UmaHistogramEnumeration(kExtensionHistogramName, + ChromiumJsonExtension::kVerticalTabEscape); + if (!(options_ & JSON_ALLOW_VERT_TAB)) { + ReportError(JSON_INVALID_ESCAPE, -1); + return false; + } string.Append('\v'); break; // All other escape squences are illegal. default: - ReportError(JSONReader::JSON_INVALID_ESCAPE, 0); + ReportError(JSON_INVALID_ESCAPE, -1); return false; } } } - ReportError(JSONReader::JSON_SYNTAX_ERROR, 0); + ReportError(JSON_SYNTAX_ERROR, -1); return false; } // Entry is at the first X in \uXXXX. -bool JSONParser::DecodeUTF16(uint32_t* out_code_point) { - Optional<StringPiece> escape_sequence = ConsumeChars(4); +bool JSONParser::DecodeUTF16(base_icu::UChar32* out_code_point) { + absl::optional<StringPiece> escape_sequence = ConsumeChars(4); if (!escape_sequence) return false; // Consume the UTF-16 code unit, which may be a high surrogate. int code_unit16_high = 0; - if (!HexStringToInt(*escape_sequence, &code_unit16_high)) + if (!UnprefixedHexStringToInt(*escape_sequence, &code_unit16_high)) return false; // If this is a high surrogate, consume the next code unit to get the // low surrogate. if (CBU16_IS_SURROGATE(code_unit16_high)) { - // Make sure this is the high surrogate. If not, it's an encoding - // error. - if (!CBU16_IS_SURROGATE_LEAD(code_unit16_high)) - return false; + // Make sure this is the high surrogate. + if (!CBU16_IS_SURROGATE_LEAD(code_unit16_high)) { + if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) + return false; + *out_code_point = kUnicodeReplacementPoint; + return true; + } // Make sure that the token has more characters to consume the // lower surrogate. - if (!ConsumeIfMatch("\\u")) - return false; + if (!ConsumeIfMatch("\\u")) { + if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) + return false; + *out_code_point = kUnicodeReplacementPoint; + return true; + } escape_sequence = ConsumeChars(4); if (!escape_sequence) return false; int code_unit16_low = 0; - if (!HexStringToInt(*escape_sequence, &code_unit16_low)) + if (!UnprefixedHexStringToInt(*escape_sequence, &code_unit16_low)) return false; - if (!CBU16_IS_TRAIL(code_unit16_low)) - return false; + if (!CBU16_IS_TRAIL(code_unit16_low)) { + if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) + return false; + *out_code_point = kUnicodeReplacementPoint; + return true; + } - uint32_t code_point = + base_icu::UChar32 code_point = CBU16_GET_SUPPLEMENTARY(code_unit16_high, code_unit16_low); - if (!IsValidCharacter(code_point)) - return false; *out_code_point = code_point; } else { // Not a surrogate. DCHECK(CBU16_IS_SINGLE(code_unit16_high)); - if (!IsValidCharacter(code_unit16_high)) { - if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) { - return false; - } - *out_code_point = kUnicodeReplacementPoint; - return true; - } *out_code_point = code_unit16_high; } @@ -618,17 +741,17 @@ return true; } -Optional<Value> JSONParser::ConsumeNumber() { +absl::optional<Value> JSONParser::ConsumeNumber() { const char* num_start = pos(); - const int start_index = index_; - int end_index = start_index; + const size_t start_index = index_; + size_t end_index = start_index; if (PeekChar() == '-') ConsumeChar(); if (!ReadInt(false)) { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; + ReportError(JSON_SYNTAX_ERROR, 0); + return absl::nullopt; } end_index = index_; @@ -636,22 +759,22 @@ if (PeekChar() == '.') { ConsumeChar(); if (!ReadInt(true)) { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; + ReportError(JSON_SYNTAX_ERROR, 0); + return absl::nullopt; } end_index = index_; } // Optional exponent part. - Optional<char> c = PeekChar(); + absl::optional<char> c = PeekChar(); if (c == 'e' || c == 'E') { ConsumeChar(); if (PeekChar() == '-' || PeekChar() == '+') { ConsumeChar(); } if (!ReadInt(true)) { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; + ReportError(JSON_SYNTAX_ERROR, 0); + return absl::nullopt; } end_index = index_; } @@ -660,7 +783,7 @@ // so save off where the parser should be on exit (see Consume invariant at // the top of the header), then make sure the next token is one which is // valid. - int exit_index = index_; + size_t exit_index = index_; switch (GetNextToken()) { case T_OBJECT_END: @@ -669,8 +792,8 @@ case T_END_OF_INPUT: break; default: - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; + ReportError(JSON_SYNTAX_ERROR, 0); + return absl::nullopt; } index_ = exit_index; @@ -682,19 +805,19 @@ return Value(num_int); double num_double; - if (StringToDouble(num_string.as_string(), &num_double) && - std::isfinite(num_double)) { + if (StringToDouble(num_string, &num_double) && std::isfinite(num_double)) { return Value(num_double); } - return nullopt; + ReportError(JSON_UNREPRESENTABLE_NUMBER, 0); + return absl::nullopt; } bool JSONParser::ReadInt(bool allow_leading_zeros) { size_t len = 0; char first = 0; - while (Optional<char> c = PeekChar()) { + while (absl::optional<char> c = PeekChar()) { if (!IsAsciiDigit(c)) break; @@ -714,15 +837,15 @@ return true; } -Optional<Value> JSONParser::ConsumeLiteral() { +absl::optional<Value> JSONParser::ConsumeLiteral() { if (ConsumeIfMatch("true")) return Value(true); if (ConsumeIfMatch("false")) return Value(false); if (ConsumeIfMatch("null")) return Value(Value::Type::NONE); - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; + ReportError(JSON_SYNTAX_ERROR, 0); + return absl::nullopt; } bool JSONParser::ConsumeIfMatch(StringPiece match) { @@ -733,11 +856,16 @@ return false; } -void JSONParser::ReportError(JSONReader::JsonParseError code, - int column_adjust) { +void JSONParser::ReportError(JsonParseError code, int column_adjust) { error_code_ = code; error_line_ = line_number_; - error_column_ = index_ - index_last_line_ + column_adjust; + error_column_ = static_cast<int>(index_ - index_last_line_) + column_adjust; + + // For a final blank line ('\n' and then EOF), a negative column_adjust may + // put us below 1, which doesn't really make sense for 1-based columns. + if (error_column_ < 1) { + error_column_ = 1; + } } // static
diff --git a/base/json/json_parser.h b/base/json/json_parser.h index 9e6be55..0eb4fac 100644 --- a/base/json/json_parser.h +++ b/base/json/json_parser.h
@@ -1,21 +1,24 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_JSON_JSON_PARSER_H_ #define BASE_JSON_JSON_PARSER_H_ +#include <stddef.h> +#include <stdint.h> + #include <memory> #include <string> #include "base/base_export.h" #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" -#include "base/json/json_reader.h" -#include "base/macros.h" -#include "base/optional.h" +#include "base/json/json_common.h" #include "base/strings/string_piece.h" -#include "starboard/types.h" +#include "base/third_party/icu/icu_utf.h" +#include "base/values.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace base { @@ -41,17 +44,47 @@ // of the next token. class BASE_EXPORT JSONParser { public: - JSONParser(int options, int max_depth = JSONReader::kStackMaxDepth); + // Error codes during parsing. + enum JsonParseError { + JSON_NO_ERROR = base::ValueDeserializer::kErrorCodeNoError, + JSON_SYNTAX_ERROR = base::ValueDeserializer::kErrorCodeInvalidFormat, + JSON_INVALID_ESCAPE, + JSON_UNEXPECTED_TOKEN, + JSON_TRAILING_COMMA, + JSON_TOO_MUCH_NESTING, + JSON_UNEXPECTED_DATA_AFTER_ROOT, + JSON_UNSUPPORTED_ENCODING, + JSON_UNQUOTED_DICTIONARY_KEY, + JSON_UNREPRESENTABLE_NUMBER, + JSON_PARSE_ERROR_COUNT + }; + + // String versions of parse error codes. + static const char kSyntaxError[]; + static const char kInvalidEscape[]; + static const char kUnexpectedToken[]; + static const char kTrailingComma[]; + static const char kTooMuchNesting[]; + static const char kUnexpectedDataAfterRoot[]; + static const char kUnsupportedEncoding[]; + static const char kUnquotedDictionaryKey[]; + static const char kUnrepresentableNumber[]; + + explicit JSONParser(int options, size_t max_depth = kAbsoluteMaxDepth); + + JSONParser(const JSONParser&) = delete; + JSONParser& operator=(const JSONParser&) = delete; + ~JSONParser(); // Parses the input string according to the set options and returns the // result as a Value. // Wrap this in base::FooValue::From() to check the Value is of type Foo and // convert to a FooValue at the same time. - Optional<Value> Parse(StringPiece input); + absl::optional<Value> Parse(StringPiece input); // Returns the error code. - JSONReader::JsonParseError error_code() const; + JsonParseError error_code() const; // Returns the human-friendly error message. std::string GetErrorMessage() const; @@ -101,7 +134,7 @@ // Appends the Unicode code point |point| to the string, either by // increasing the |length_| of the string if the string has not been // converted, or by appending the UTF8 bytes for the code point. - void Append(uint32_t point); + void Append(base_icu::UChar32 point); // Converts the builder from its default StringPiece to a full std::string, // performing a copy. Once a builder is converted, it cannot be made a @@ -122,22 +155,22 @@ // The copied string representation. Will be unset until Convert() is // called. - base::Optional<std::string> string_; + absl::optional<std::string> string_; }; // Returns the next |count| bytes of the input stream, or nullopt if fewer // than |count| bytes remain. - Optional<StringPiece> PeekChars(int count); + absl::optional<StringPiece> PeekChars(size_t count); // Calls PeekChars() with a |count| of 1. - Optional<char> PeekChar(); + absl::optional<char> PeekChar(); // Returns the next |count| bytes of the input stream, or nullopt if fewer // than |count| bytes remain, and advances the parser position by |count|. - Optional<StringPiece> ConsumeChars(int count); + absl::optional<StringPiece> ConsumeChars(size_t count); // Calls ConsumeChars() with a |count| of 1. - Optional<char> ConsumeChar(); + absl::optional<char> ConsumeChar(); // Returns a pointer to the current character position. const char* pos(); @@ -154,22 +187,22 @@ bool EatComment(); // Calls GetNextToken() and then ParseToken(). - Optional<Value> ParseNextToken(); + absl::optional<Value> ParseNextToken(); // Takes a token that represents the start of a Value ("a structural token" // in RFC terms) and consumes it, returning the result as a Value. - Optional<Value> ParseToken(Token token); + absl::optional<Value> ParseToken(Token token); // Assuming that the parser is currently wound to '{', this parses a JSON // object into a Value. - Optional<Value> ConsumeDictionary(); + absl::optional<Value> ConsumeDictionary(); // Assuming that the parser is wound to '[', this parses a JSON list into a // Value. - Optional<Value> ConsumeList(); + absl::optional<Value> ConsumeList(); // Calls through ConsumeStringRaw and wraps it in a value. - Optional<Value> ConsumeString(); + absl::optional<Value> ConsumeString(); // Assuming that the parser is wound to a double quote, this parses a string, // decoding any escape sequences and converts UTF-16 to UTF-8. Returns true on @@ -180,18 +213,18 @@ // bytes (parser is wound to the first character of a HEX sequence, with the // potential for consuming another \uXXXX for a surrogate). Returns true on // success and places the code point |out_code_point|, and false on failure. - bool DecodeUTF16(uint32_t* out_code_point); + bool DecodeUTF16(base_icu::UChar32* out_code_point); // Assuming that the parser is wound to the start of a valid JSON number, // this parses and converts it to either an int or double value. - Optional<Value> ConsumeNumber(); + absl::optional<Value> ConsumeNumber(); // Helper that reads characters that are ints. Returns true if a number was // read and false on error. bool ReadInt(bool allow_leading_zeros); // Consumes the literal values of |true|, |false|, and |null|, assuming the // parser is wound to the first character of any of those. - Optional<Value> ConsumeLiteral(); + absl::optional<Value> ConsumeLiteral(); // Helper function that returns true if the byte squence |match| can be // consumed at the current parser position. Returns false if there are fewer @@ -202,7 +235,7 @@ // Sets the error information to |code| at the current column, based on // |index_| and |index_last_line_|, with an optional positive/negative // adjustment by |column_adjust|. - void ReportError(JSONReader::JsonParseError code, int column_adjust); + void ReportError(JsonParseError code, int column_adjust); // Given the line and column number of an error, formats one of the error // message contants from json_reader.h for human display. @@ -213,25 +246,25 @@ const int options_; // Maximum depth to parse. - const int max_depth_; + const size_t max_depth_; // The input stream being parsed. Note: Not guaranteed to NUL-terminated. StringPiece input_; // The index in the input stream to which the parser is wound. - int index_; + size_t index_; // The number of times the parser has recursed (current stack depth). - int stack_depth_; + size_t stack_depth_; // The line number that the parser is at currently. int line_number_; // The last value of |index_| on the previous line. - int index_last_line_; + size_t index_last_line_; // Error information. - JSONReader::JsonParseError error_code_; + JsonParseError error_code_; int error_line_; int error_column_; @@ -243,10 +276,6 @@ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeLiterals); FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeNumbers); FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ErrorMessages); - FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ReplaceInvalidCharacters); - FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ReplaceInvalidUTF16EscapeSequence); - - DISALLOW_COPY_AND_ASSIGN(JSONParser); }; // Used when decoding and an invalid utf-8 sequence is encountered.
diff --git a/base/json/json_parser_unittest.cc b/base/json/json_parser_unittest.cc index 0e5e10c..e8de7d5 100644 --- a/base/json/json_parser_unittest.cc +++ b/base/json/json_parser_unittest.cc
@@ -1,20 +1,18 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/json/json_parser.h" +#include <stddef.h> + #include <memory> #include "base/json/json_reader.h" #include "base/memory/ptr_util.h" -#include "base/optional.h" -#include "base/strings/stringprintf.h" #include "base/values.h" -#include "starboard/common/string.h" -#include "starboard/memory.h" -#include "starboard/types.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace base { namespace internal { @@ -29,17 +27,6 @@ return parser; } - // MSan will do a better job detecting over-read errors if the input is - // not nul-terminated on the heap. This will copy |input| to a new buffer - // owned by |owner|, returning a StringPiece to |owner|. - StringPiece MakeNotNullTerminatedInput(const char* input, - std::unique_ptr<char[]>* owner) { - size_t str_len = strlen(input); - owner->reset(new char[str_len]); - memcpy(owner->get(), input, str_len); - return StringPiece(owner->get(), str_len); - } - void TestLastThree(JSONParser* parser) { EXPECT_EQ(',', *parser->PeekChar()); parser->ConsumeChar(); @@ -67,60 +54,58 @@ TEST_F(JSONParserTest, ConsumeString) { std::string input("\"test\",|"); std::unique_ptr<JSONParser> parser(NewTestParser(input)); - Optional<Value> value(parser->ConsumeString()); + absl::optional<Value> value(parser->ConsumeString()); EXPECT_EQ(',', *parser->pos()); TestLastThree(parser.get()); ASSERT_TRUE(value); - std::string str; - EXPECT_TRUE(value->GetAsString(&str)); - EXPECT_EQ("test", str); + ASSERT_TRUE(value->is_string()); + EXPECT_EQ("test", value->GetString()); } TEST_F(JSONParserTest, ConsumeList) { std::string input("[true, false],|"); std::unique_ptr<JSONParser> parser(NewTestParser(input)); - Optional<Value> value(parser->ConsumeList()); + absl::optional<Value> value(parser->ConsumeList()); EXPECT_EQ(',', *parser->pos()); TestLastThree(parser.get()); ASSERT_TRUE(value); - base::ListValue* list; - EXPECT_TRUE(value->GetAsList(&list)); - EXPECT_EQ(2u, list->GetSize()); + Value::List* list = value->GetIfList(); + ASSERT_TRUE(list); + EXPECT_EQ(2u, list->size()); } TEST_F(JSONParserTest, ConsumeDictionary) { std::string input("{\"abc\":\"def\"},|"); std::unique_ptr<JSONParser> parser(NewTestParser(input)); - Optional<Value> value(parser->ConsumeDictionary()); + absl::optional<Value> value(parser->ConsumeDictionary()); EXPECT_EQ(',', *parser->pos()); TestLastThree(parser.get()); ASSERT_TRUE(value); - base::DictionaryValue* dict; - EXPECT_TRUE(value->GetAsDictionary(&dict)); - std::string str; - EXPECT_TRUE(dict->GetString("abc", &str)); - EXPECT_EQ("def", str); + const Value::Dict* value_dict = value->GetIfDict(); + ASSERT_TRUE(value_dict); + const std::string* str = value_dict->FindString("abc"); + ASSERT_TRUE(str); + EXPECT_EQ("def", *str); } TEST_F(JSONParserTest, ConsumeLiterals) { // Literal |true|. std::string input("true,|"); std::unique_ptr<JSONParser> parser(NewTestParser(input)); - Optional<Value> value(parser->ConsumeLiteral()); + absl::optional<Value> value(parser->ConsumeLiteral()); EXPECT_EQ(',', *parser->pos()); TestLastThree(parser.get()); ASSERT_TRUE(value); - bool bool_value = false; - EXPECT_TRUE(value->GetAsBoolean(&bool_value)); - EXPECT_TRUE(bool_value); + ASSERT_TRUE(value->is_bool()); + EXPECT_TRUE(value->GetBool()); // Literal |false|. input = "false,|"; @@ -131,8 +116,8 @@ TestLastThree(parser.get()); ASSERT_TRUE(value); - EXPECT_TRUE(value->GetAsBoolean(&bool_value)); - EXPECT_FALSE(bool_value); + ASSERT_TRUE(value->is_bool()); + EXPECT_FALSE(value->GetBool()); // Literal |null|. input = "null,|"; @@ -150,15 +135,14 @@ // Integer. std::string input("1234,|"); std::unique_ptr<JSONParser> parser(NewTestParser(input)); - Optional<Value> value(parser->ConsumeNumber()); + absl::optional<Value> value(parser->ConsumeNumber()); EXPECT_EQ(',', *parser->pos()); TestLastThree(parser.get()); ASSERT_TRUE(value); - int number_i; - EXPECT_TRUE(value->GetAsInteger(&number_i)); - EXPECT_EQ(1234, number_i); + ASSERT_TRUE(value->is_int()); + EXPECT_EQ(1234, value->GetInt()); // Negative integer. input = "-1234,|"; @@ -169,8 +153,8 @@ TestLastThree(parser.get()); ASSERT_TRUE(value); - EXPECT_TRUE(value->GetAsInteger(&number_i)); - EXPECT_EQ(-1234, number_i); + ASSERT_TRUE(value->is_int()); + EXPECT_EQ(-1234, value->GetInt()); // Double. input = "12.34,|"; @@ -181,9 +165,8 @@ TestLastThree(parser.get()); ASSERT_TRUE(value); - double number_d; - EXPECT_TRUE(value->GetAsDouble(&number_d)); - EXPECT_EQ(12.34, number_d); + ASSERT_TRUE(value->is_double()); + EXPECT_EQ(12.34, value->GetDouble()); // Scientific. input = "42e3,|"; @@ -194,8 +177,8 @@ TestLastThree(parser.get()); ASSERT_TRUE(value); - EXPECT_TRUE(value->GetAsDouble(&number_d)); - EXPECT_EQ(42000, number_d); + ASSERT_TRUE(value->is_double()); + EXPECT_EQ(42000, value->GetDouble()); // Negative scientific. input = "314159e-5,|"; @@ -206,8 +189,8 @@ TestLastThree(parser.get()); ASSERT_TRUE(value); - EXPECT_TRUE(value->GetAsDouble(&number_d)); - EXPECT_EQ(3.14159, number_d); + ASSERT_TRUE(value->is_double()); + EXPECT_EQ(3.14159, value->GetDouble()); // Positive scientific. input = "0.42e+3,|"; @@ -218,244 +201,107 @@ TestLastThree(parser.get()); ASSERT_TRUE(value); - EXPECT_TRUE(value->GetAsDouble(&number_d)); - EXPECT_EQ(420, number_d); + ASSERT_TRUE(value->is_double()); + EXPECT_EQ(420, value->GetDouble()); } TEST_F(JSONParserTest, ErrorMessages) { - // Error strings should not be modified in case of success. - std::string error_message; - int error_code = 0; - std::unique_ptr<Value> root = JSONReader::ReadAndReturnError( - "[42]", JSON_PARSE_RFC, &error_code, &error_message); - EXPECT_TRUE(error_message.empty()); - EXPECT_EQ(0, error_code); - - // Test line and column counting - const char big_json[] = "[\n0,\n1,\n2,\n3,4,5,6 7,\n8,\n9\n]"; - // error here ----------------------------------^ - root = JSONReader::ReadAndReturnError(big_json, JSON_PARSE_RFC, &error_code, - &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(5, 10, JSONReader::kSyntaxError), - error_message); - EXPECT_EQ(JSONReader::JSON_SYNTAX_ERROR, error_code); - - error_code = 0; - error_message = ""; - // Test line and column counting with "\r\n" line ending - const char big_json_crlf[] = - "[\r\n0,\r\n1,\r\n2,\r\n3,4,5,6 7,\r\n8,\r\n9\r\n]"; - // error here ----------------------^ - root = JSONReader::ReadAndReturnError(big_json_crlf, JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(5, 10, JSONReader::kSyntaxError), - error_message); - EXPECT_EQ(JSONReader::JSON_SYNTAX_ERROR, error_code); + { + JSONParser parser(JSON_PARSE_RFC); + absl::optional<Value> value = parser.Parse("[42]"); + EXPECT_TRUE(value); + EXPECT_TRUE(parser.GetErrorMessage().empty()); + EXPECT_EQ(0, parser.error_code()); + } // Test each of the error conditions - root = JSONReader::ReadAndReturnError("{},{}", JSON_PARSE_RFC, &error_code, - &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 3, - JSONReader::kUnexpectedDataAfterRoot), error_message); - EXPECT_EQ(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, error_code); - - std::string nested_json; - for (int i = 0; i < 201; ++i) { - nested_json.insert(nested_json.begin(), '['); - nested_json.append(1, ']'); + { + JSONParser parser(JSON_PARSE_RFC); + absl::optional<Value> value = parser.Parse("{},{}"); + EXPECT_FALSE(value); + EXPECT_EQ(JSONParser::FormatErrorMessage( + 1, 3, JSONParser::kUnexpectedDataAfterRoot), + parser.GetErrorMessage()); + EXPECT_EQ(JSONParser::JSON_UNEXPECTED_DATA_AFTER_ROOT, parser.error_code()); } - root = JSONReader::ReadAndReturnError(nested_json, JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 200, JSONReader::kTooMuchNesting), - error_message); - EXPECT_EQ(JSONReader::JSON_TOO_MUCH_NESTING, error_code); - root = JSONReader::ReadAndReturnError("[1,]", JSON_PARSE_RFC, &error_code, - &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 4, JSONReader::kTrailingComma), - error_message); - EXPECT_EQ(JSONReader::JSON_TRAILING_COMMA, error_code); - - root = JSONReader::ReadAndReturnError("{foo:\"bar\"}", JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 2, - JSONReader::kUnquotedDictionaryKey), error_message); - EXPECT_EQ(JSONReader::JSON_UNQUOTED_DICTIONARY_KEY, error_code); - - root = JSONReader::ReadAndReturnError("{\"foo\":\"bar\",}", JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 14, JSONReader::kTrailingComma), - error_message); - - root = JSONReader::ReadAndReturnError("[nu]", JSON_PARSE_RFC, &error_code, - &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 2, JSONReader::kSyntaxError), - error_message); - EXPECT_EQ(JSONReader::JSON_SYNTAX_ERROR, error_code); - - root = JSONReader::ReadAndReturnError("[\"xxx\\xq\"]", JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape), - error_message); - EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code); - - root = JSONReader::ReadAndReturnError("[\"xxx\\uq\"]", JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape), - error_message); - EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code); - - root = JSONReader::ReadAndReturnError("[\"xxx\\q\"]", JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape), - error_message); - EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code); - - root = JSONReader::ReadAndReturnError(("[\"\\ufffe\"]"), JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 8, JSONReader::kInvalidEscape), - error_message); - EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code); -} - -TEST_F(JSONParserTest, Decode4ByteUtf8Char) { - // This test strings contains a 4 byte unicode character (a smiley!) that the - // reader should be able to handle (the character is \xf0\x9f\x98\x87). - const char kUtf8Data[] = - "[\"😇\",[],[],[],{\"google:suggesttype\":[]}]"; - std::string error_message; - int error_code = 0; - std::unique_ptr<Value> root = JSONReader::ReadAndReturnError( - kUtf8Data, JSON_PARSE_RFC, &error_code, &error_message); - EXPECT_TRUE(root.get()) << error_message; -} - -TEST_F(JSONParserTest, DecodeUnicodeNonCharacter) { - // Tests Unicode code points (encoded as escaped UTF-16) that are not valid - // characters. - EXPECT_FALSE(JSONReader::Read("[\"\\ufdd0\"]")); - EXPECT_FALSE(JSONReader::Read("[\"\\ufffe\"]")); - EXPECT_FALSE(JSONReader::Read("[\"\\ud83f\\udffe\"]")); - - EXPECT_TRUE( - JSONReader::Read("[\"\\ufdd0\"]", JSON_REPLACE_INVALID_CHARACTERS)); - EXPECT_TRUE( - JSONReader::Read("[\"\\ufffe\"]", JSON_REPLACE_INVALID_CHARACTERS)); -} - -TEST_F(JSONParserTest, DecodeNegativeEscapeSequence) { - EXPECT_FALSE(JSONReader::Read("[\"\\x-A\"]")); - EXPECT_FALSE(JSONReader::Read("[\"\\u-00A\"]")); -} - -// Verifies invalid utf-8 characters are replaced. -TEST_F(JSONParserTest, ReplaceInvalidCharacters) { - const std::string bogus_char = "󿿿"; - const std::string quoted_bogus_char = "\"" + bogus_char + "\""; - std::unique_ptr<JSONParser> parser( - NewTestParser(quoted_bogus_char, JSON_REPLACE_INVALID_CHARACTERS)); - Optional<Value> value(parser->ConsumeString()); - ASSERT_TRUE(value); - std::string str; - EXPECT_TRUE(value->GetAsString(&str)); - EXPECT_EQ(kUnicodeReplacementString, str); -} - -TEST_F(JSONParserTest, ReplaceInvalidUTF16EscapeSequence) { - const std::string invalid = "\"\\ufffe\""; - std::unique_ptr<JSONParser> parser( - NewTestParser(invalid, JSON_REPLACE_INVALID_CHARACTERS)); - Optional<Value> value(parser->ConsumeString()); - ASSERT_TRUE(value); - std::string str; - EXPECT_TRUE(value->GetAsString(&str)); - EXPECT_EQ(kUnicodeReplacementString, str); -} - -TEST_F(JSONParserTest, ParseNumberErrors) { - const struct { - const char* input; - bool parse_success; - double value; - } kCases[] = { - // clang-format off - {"1", true, 1}, - {"2.", false, 0}, - {"42", true, 42}, - {"6e", false, 0}, - {"43e2", true, 4300}, - {"43e-", false, 0}, - {"9e-3", true, 0.009}, - {"2e+", false, 0}, - {"2e+2", true, 200}, - // clang-format on - }; - - for (unsigned int i = 0; i < arraysize(kCases); ++i) { - auto test_case = kCases[i]; - SCOPED_TRACE(StringPrintf("case %u: \"%s\"", i, test_case.input)); - - std::unique_ptr<char[]> input_owner; - StringPiece input = - MakeNotNullTerminatedInput(test_case.input, &input_owner); - - std::unique_ptr<Value> result = JSONReader::Read(input); - if (test_case.parse_success) { - EXPECT_TRUE(result); - } else { - EXPECT_FALSE(result); + { + std::string nested_json; + for (int i = 0; i < 201; ++i) { + nested_json.insert(nested_json.begin(), '['); + nested_json.append(1, ']'); } - - if (!result) - continue; - - double double_value = 0; - EXPECT_TRUE(result->GetAsDouble(&double_value)); - EXPECT_EQ(test_case.value, double_value); + JSONParser parser(JSON_PARSE_RFC); + absl::optional<Value> value = parser.Parse(nested_json); + EXPECT_FALSE(value); + EXPECT_EQ( + JSONParser::FormatErrorMessage(1, 200, JSONParser::kTooMuchNesting), + parser.GetErrorMessage()); + EXPECT_EQ(JSONParser::JSON_TOO_MUCH_NESTING, parser.error_code()); } -} -TEST_F(JSONParserTest, UnterminatedInputs) { - const char* kCases[] = { - // clang-format off - "/", - "//", - "/*", - "\"xxxxxx", - "\"", - "{ ", - "[\t", - "tru", - "fals", - "nul", - "\"\\x", - "\"\\x2", - "\"\\u123", - "\"\\uD803\\u", - "\"\\", - "\"\\/", - // clang-format on - }; + { + JSONParser parser(JSON_PARSE_RFC); + absl::optional<Value> value = parser.Parse("[1,]"); + EXPECT_FALSE(value); + EXPECT_EQ(JSONParser::FormatErrorMessage(1, 4, JSONParser::kTrailingComma), + parser.GetErrorMessage()); + EXPECT_EQ(JSONParser::JSON_TRAILING_COMMA, parser.error_code()); + } - for (unsigned int i = 0; i < arraysize(kCases); ++i) { - auto* test_case = kCases[i]; - SCOPED_TRACE(StringPrintf("case %u: \"%s\"", i, test_case)); + { + JSONParser parser(JSON_PARSE_RFC); + absl::optional<Value> value = parser.Parse("{foo:\"bar\"}"); + EXPECT_FALSE(value); + EXPECT_EQ(JSONParser::FormatErrorMessage( + 1, 2, JSONParser::kUnquotedDictionaryKey), + parser.GetErrorMessage()); + EXPECT_EQ(JSONParser::JSON_UNQUOTED_DICTIONARY_KEY, parser.error_code()); + } - std::unique_ptr<char[]> input_owner; - StringPiece input = MakeNotNullTerminatedInput(test_case, &input_owner); + { + JSONParser parser(JSON_PARSE_RFC); + absl::optional<Value> value = parser.Parse("{\"foo\":\"bar\",}"); + EXPECT_FALSE(value); + EXPECT_EQ(JSONParser::FormatErrorMessage(1, 14, JSONParser::kTrailingComma), + parser.GetErrorMessage()); + EXPECT_EQ(JSONParser::JSON_TRAILING_COMMA, parser.error_code()); + } - EXPECT_FALSE(JSONReader::Read(input)); + { + JSONParser parser(JSON_PARSE_RFC); + absl::optional<Value> value = parser.Parse("[nu]"); + EXPECT_FALSE(value); + EXPECT_EQ(JSONParser::FormatErrorMessage(1, 2, JSONParser::kSyntaxError), + parser.GetErrorMessage()); + EXPECT_EQ(JSONParser::JSON_SYNTAX_ERROR, parser.error_code()); + } + + { + JSONParser parser(JSON_PARSE_RFC | JSON_ALLOW_X_ESCAPES); + absl::optional<Value> value = parser.Parse("[\"xxx\\xq\"]"); + EXPECT_FALSE(value); + EXPECT_EQ(JSONParser::FormatErrorMessage(1, 7, JSONParser::kInvalidEscape), + parser.GetErrorMessage()); + EXPECT_EQ(JSONParser::JSON_INVALID_ESCAPE, parser.error_code()); + } + + { + JSONParser parser(JSON_PARSE_RFC); + absl::optional<Value> value = parser.Parse("[\"xxx\\uq\"]"); + EXPECT_FALSE(value); + EXPECT_EQ(JSONParser::FormatErrorMessage(1, 7, JSONParser::kInvalidEscape), + parser.GetErrorMessage()); + EXPECT_EQ(JSONParser::JSON_INVALID_ESCAPE, parser.error_code()); + } + + { + JSONParser parser(JSON_PARSE_RFC); + absl::optional<Value> value = parser.Parse("[\"xxx\\q\"]"); + EXPECT_FALSE(value); + EXPECT_EQ(JSONParser::FormatErrorMessage(1, 7, JSONParser::kInvalidEscape), + parser.GetErrorMessage()); + EXPECT_EQ(JSONParser::JSON_INVALID_ESCAPE, parser.error_code()); } }
diff --git a/base/json/json_perftest.cc b/base/json/json_perftest.cc index fc05bdc..11f5290 100644 --- a/base/json/json_perftest.cc +++ b/base/json/json_perftest.cc
@@ -1,47 +1,61 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2017 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/memory/ptr_util.h" +#include "base/strings/string_number_conversions.h" #include "base/time/time.h" #include "base/values.h" +#include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" -#include "testing/perf/perf_test.h" +#include "testing/perf/perf_result_reporter.h" namespace base { namespace { + +constexpr char kMetricPrefixJSON[] = "JSON."; +constexpr char kMetricReadTime[] = "read_time"; +constexpr char kMetricWriteTime[] = "write_time"; + +perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) { + perf_test::PerfResultReporter reporter(kMetricPrefixJSON, story_name); + reporter.RegisterImportantMetric(kMetricReadTime, "ms"); + reporter.RegisterImportantMetric(kMetricWriteTime, "ms"); + return reporter; +} + // Generates a simple dictionary value with simple data types, a string and a // list. -std::unique_ptr<DictionaryValue> GenerateDict() { - auto root = std::make_unique<DictionaryValue>(); - root->SetDouble("Double", 3.141); - root->SetBoolean("Bool", true); - root->SetInteger("Int", 42); - root->SetString("String", "Foo"); +Value::Dict GenerateDict() { + Value::Dict root; + root.Set("Double", 3.141); + root.Set("Bool", true); + root.Set("Int", 42); + root.Set("String", "Foo"); - auto list = std::make_unique<ListValue>(); - list->Set(0, std::make_unique<Value>(2.718)); - list->Set(1, std::make_unique<Value>(false)); - list->Set(2, std::make_unique<Value>(123)); - list->Set(3, std::make_unique<Value>("Bar")); - root->Set("List", std::move(list)); + Value::List list; + list.Append(2.718); + list.Append(false); + list.Append(123); + list.Append("Bar"); + root.Set("List", std::move(list)); return root; } // Generates a tree-like dictionary value with a size of O(breadth ** depth). -std::unique_ptr<DictionaryValue> GenerateLayeredDict(int breadth, int depth) { +Value::Dict GenerateLayeredDict(int breadth, int depth) { if (depth == 1) return GenerateDict(); - auto root = GenerateDict(); - auto next = GenerateLayeredDict(breadth, depth - 1); + Value::Dict root = GenerateDict(); + Value::Dict next = GenerateLayeredDict(breadth, depth - 1); for (int i = 0; i < breadth; ++i) { - root->Set("Dict" + std::to_string(i), next->CreateDeepCopy()); + root.Set("Dict" + base::NumberToString(i), next.Clone()); } return root; @@ -52,30 +66,32 @@ class JSONPerfTest : public testing::Test { public: void TestWriteAndRead(int breadth, int depth) { - std::string description = "Breadth: " + std::to_string(breadth) + - ", Depth: " + std::to_string(depth); - auto dict = GenerateLayeredDict(breadth, depth); + std::string description = "Breadth: " + base::NumberToString(breadth) + + ", Depth: " + base::NumberToString(depth); + Value::Dict dict = GenerateLayeredDict(breadth, depth); std::string json; TimeTicks start_write = TimeTicks::Now(); - JSONWriter::Write(*dict, &json); + JSONWriter::Write(dict, &json); TimeTicks end_write = TimeTicks::Now(); - perf_test::PrintResult("Write", "", description, - (end_write - start_write).InMillisecondsF(), "ms", - true); + auto reporter = SetUpReporter("breadth_" + base::NumberToString(breadth) + + "_depth_" + base::NumberToString(depth)); + reporter.AddResult(kMetricWriteTime, end_write - start_write); TimeTicks start_read = TimeTicks::Now(); JSONReader::Read(json); TimeTicks end_read = TimeTicks::Now(); - perf_test::PrintResult("Read", "", description, - (end_read - start_read).InMillisecondsF(), "ms", - true); + reporter.AddResult(kMetricReadTime, end_read - start_read); } }; TEST_F(JSONPerfTest, StressTest) { + // These loop ranges are chosen such that this test will complete in a + // reasonable amount of time and will work on a 32-bit build without hitting + // an out-of-memory failure. Having j go to 10 uses over 2 GiB of memory and + // might hit Android timeouts so be wary of going that high. for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 12; ++j) { + for (int j = 0; j < 10; ++j) { TestWriteAndRead(i + 1, j + 1); } }
diff --git a/base/json/json_perftest_decodebench.cc b/base/json/json_perftest_decodebench.cc new file mode 100644 index 0000000..9972de8 --- /dev/null +++ b/base/json/json_perftest_decodebench.cc
@@ -0,0 +1,100 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This program measures the time taken to decode the given JSON files (the +// command line arguments). It is for manual benchmarking. +// +// Usage: +// $ ninja -C out/foobar json_perftest_decodebench +// $ out/foobar/json_perftest_decodebench -a -n=10 the/path/to/your/*.json +// +// The -n=10 switch controls the number of iterations. It defaults to 1. +// +// The -a switch means to print 1 non-comment line per input file (the average +// iteration time). Without this switch (the default), it prints n non-comment +// lines per input file (individual iteration times). For a single input file, +// building and running this program before and after a particular commit can +// work well with the 'ministat' tool: https://github.com/thorduri/ministat + +#include <inttypes.h> +#include <iomanip> +#include <iostream> + +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/logging.h" +#include "base/time/time.h" + +int main(int argc, char* argv[]) { + if (!base::ThreadTicks::IsSupported()) { + std::cout << "# base::ThreadTicks is not supported\n"; + return EXIT_FAILURE; + } + base::ThreadTicks::WaitUntilInitialized(); + + base::CommandLine::Init(argc, argv); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + bool average = command_line->HasSwitch("a"); + int iterations = 1; + std::string iterations_str = command_line->GetSwitchValueASCII("n"); + if (!iterations_str.empty()) { + iterations = atoi(iterations_str.c_str()); + if (iterations < 1) { + std::cout << "# invalid -n command line switch\n"; + return EXIT_FAILURE; + } + } + + if (average) { + std::cout << "# Microseconds (μs), n=" << iterations << ", averaged" + << std::endl; + } else { + std::cout << "# Microseconds (μs), n=" << iterations << std::endl; + } + for (const auto& filename : command_line->GetArgs()) { + std::string src; + if (!base::ReadFileToString(base::FilePath(filename), &src)) { + std::cout << "# could not read " << filename << std::endl; + return EXIT_FAILURE; + } + + int64_t total_time = 0; + std::string error_message; + for (int i = 0; i < iterations; ++i) { + auto start = base::ThreadTicks::Now(); + auto v = base::JSONReader::ReadAndReturnValueWithError(src); + auto end = base::ThreadTicks::Now(); + int64_t iteration_time = (end - start).InMicroseconds(); + total_time += iteration_time; + + if (i == 0) { + if (average) { + error_message = + !v.has_value() ? std::move(v.error().message) : std::string(); + } else { + std::cout << "# " << filename; + if (!v.has_value() && !v.error().message.empty()) { + std::cout << ": " << v.error().message; + } + std::cout << std::endl; + } + } + + if (!average) { + std::cout << iteration_time << std::endl; + } + } + + if (average) { + int64_t average_time = total_time / iterations; + std::cout << std::setw(12) << average_time << "\t# " << filename; + if (!error_message.empty()) { + std::cout << ": " << error_message; + } + std::cout << std::endl; + } + } + return EXIT_SUCCESS; +}
diff --git a/base/json/json_reader.cc b/base/json/json_reader.cc index bf2a18a..8faa713 100644 --- a/base/json/json_reader.cc +++ b/base/json/json_reader.cc
@@ -1,126 +1,170 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/json/json_reader.h" #include <utility> -#include <vector> -#include "base/json/json_parser.h" #include "base/logging.h" -#include "base/optional.h" -#include "base/values.h" +#include "base/rust_buildflags.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +#if BUILDFLAG(BUILD_RUST_JSON_READER) +#include "base/strings/string_piece_rust.h" +#include "third_party/rust/serde_json_lenient/v0_1/wrapper/functions.h" +#include "third_party/rust/serde_json_lenient/v0_1/wrapper/lib.rs.h" +#else +#include "base/json/json_parser.h" +#endif namespace base { -// Chosen to support 99.9% of documents found in the wild late 2016. -// http://crbug.com/673263 -const int JSONReader::kStackMaxDepth = 200; +#if BUILDFLAG(BUILD_RUST_JSON_READER) -// Values 1000 and above are used by JSONFileValueSerializer::JsonFileError. -static_assert(JSONReader::JSON_PARSE_ERROR_COUNT < 1000, - "JSONReader error out of bounds"); +namespace { +using serde_json_lenient::ContextPointer; -const char JSONReader::kInvalidEscape[] = - "Invalid escape sequence."; -const char JSONReader::kSyntaxError[] = - "Syntax error."; -const char JSONReader::kUnexpectedToken[] = - "Unexpected token."; -const char JSONReader::kTrailingComma[] = - "Trailing comma not allowed."; -const char JSONReader::kTooMuchNesting[] = - "Too much nesting."; -const char JSONReader::kUnexpectedDataAfterRoot[] = - "Unexpected data after root element."; -const char JSONReader::kUnsupportedEncoding[] = - "Unsupported encoding. JSON must be UTF-8."; -const char JSONReader::kUnquotedDictionaryKey[] = - "Dictionary keys must be quoted."; -const char JSONReader::kInputTooLarge[] = - "Input string is too large (>2GB)."; +ContextPointer& ListAppendList(ContextPointer& ctx, size_t reserve) { + auto& value = reinterpret_cast<base::Value&>(ctx); + value.GetList().reserve(reserve); + value.GetList().Append(base::Value::List()); + return reinterpret_cast<ContextPointer&>(value.GetList().back()); +} -JSONReader::JSONReader(int options, int max_depth) - : parser_(new internal::JSONParser(options, max_depth)) {} +ContextPointer& ListAppendDict(ContextPointer& ctx) { + auto& value = reinterpret_cast<base::Value&>(ctx); + value.GetList().Append(base::Value::Dict()); + return reinterpret_cast<ContextPointer&>(value.GetList().back()); +} -JSONReader::~JSONReader() = default; +void ListAppendNone(ContextPointer& ctx) { + auto& value = reinterpret_cast<base::Value&>(ctx); + value.GetList().Append(base::Value()); +} + +template <class T, class As = T> +void ListAppendValue(ContextPointer& ctx, T v) { + auto& value = reinterpret_cast<base::Value&>(ctx); + value.GetList().Append(As{v}); +} + +ContextPointer& DictSetList(ContextPointer& ctx, + rust::Str key, + size_t reserve) { + auto& value = reinterpret_cast<base::Value&>(ctx); + base::Value::List list; + list.reserve(reserve); + value.SetKey(base::RustStrToStringPiece(key), base::Value(std::move(list))); + return reinterpret_cast<ContextPointer&>( + *value.GetDict().Find(base::RustStrToStringPiece(key))); +} + +ContextPointer& DictSetDict(ContextPointer& ctx, rust::Str key) { + auto& value = reinterpret_cast<base::Value&>(ctx); + value.SetKey(base::RustStrToStringPiece(key), + base::Value(base::Value::Dict())); + return reinterpret_cast<ContextPointer&>( + *value.GetDict().Find(base::RustStrToStringPiece(key))); +} + +void DictSetNone(ContextPointer& ctx, rust::Str key) { + auto& value = reinterpret_cast<base::Value&>(ctx); + value.SetKey(base::RustStrToStringPiece(key), base::Value()); +} + +template <class T, class As = T> +void DictSetValue(ContextPointer& ctx, rust::Str key, T v) { + auto& value = reinterpret_cast<base::Value&>(ctx); + value.SetKey(base::RustStrToStringPiece(key), base::Value(As{v})); +} + +JSONReader::Result DecodeJSONInRust(const base::StringPiece& json, + int options, + size_t max_depth) { + const serde_json_lenient::JsonOptions rust_options = { + .allow_trailing_commas = + (options & base::JSON_ALLOW_TRAILING_COMMAS) != 0, + .replace_invalid_characters = + (options & base::JSON_REPLACE_INVALID_CHARACTERS) != 0, + .allow_comments = (options & base::JSON_ALLOW_COMMENTS) != 0, + .allow_control_chars = (options & base::JSON_ALLOW_CONTROL_CHARS) != 0, + .allow_vert_tab = (options & base::JSON_ALLOW_VERT_TAB) != 0, + .allow_x_escapes = (options & base::JSON_ALLOW_X_ESCAPES) != 0, + .max_depth = max_depth, + }; + const serde_json_lenient::Functions functions = { + .list_append_none_fn = ListAppendNone, + .list_append_bool_fn = ListAppendValue<bool>, + .list_append_i32_fn = ListAppendValue<int32_t>, + .list_append_f64_fn = ListAppendValue<double>, + .list_append_str_fn = ListAppendValue<rust::Str, std::string>, + .list_append_list_fn = ListAppendList, + .list_append_dict_fn = ListAppendDict, + .dict_set_none_fn = DictSetNone, + .dict_set_bool_fn = DictSetValue<bool>, + .dict_set_i32_fn = DictSetValue<int32_t>, + .dict_set_f64_fn = DictSetValue<double>, + .dict_set_str_fn = DictSetValue<rust::Str, std::string>, + .dict_set_list_fn = DictSetList, + .dict_set_dict_fn = DictSetDict, + }; + + base::Value value(base::Value::Type::LIST); + auto& ctx = reinterpret_cast<ContextPointer&>(value); + serde_json_lenient::DecodeError error; + bool ok = serde_json_lenient::decode_json( + base::StringPieceToRustSlice(json), rust_options, functions, ctx, error); + + if (!ok) { + return base::unexpected(base::JSONReader::Error{ + .message = std::string(error.message), + .line = error.line, + .column = error.column, + }); + } + + return std::move(std::move(value.GetList()).back()); +} + +} // anonymous namespace + +#endif // BUILDFLAG(BUILD_RUST_JSON_READER) // static -std::unique_ptr<Value> JSONReader::Read(StringPiece json, - int options, - int max_depth) { +absl::optional<Value> JSONReader::Read(StringPiece json, + int options, + size_t max_depth) { +#if BUILDFLAG(BUILD_RUST_JSON_READER) + JSONReader::Result result = DecodeJSONInRust(json, options, max_depth); + if (!result.has_value()) { + return absl::nullopt; + } + return std::move(*result); +#else // BUILDFLAG(BUILD_RUST_JSON_READER) internal::JSONParser parser(options, max_depth); - Optional<Value> root = parser.Parse(json); - return root ? std::make_unique<Value>(std::move(*root)) : nullptr; + return parser.Parse(json); +#endif // BUILDFLAG(BUILD_RUST_JSON_READER) } - // static -std::unique_ptr<Value> JSONReader::ReadAndReturnError( - StringPiece json, - int options, - int* error_code_out, - std::string* error_msg_out, - int* error_line_out, - int* error_column_out) { +JSONReader::Result JSONReader::ReadAndReturnValueWithError(StringPiece json, + int options) { +#if BUILDFLAG(BUILD_RUST_JSON_READER) + return DecodeJSONInRust(json, options, internal::kAbsoluteMaxDepth); +#else // BUILDFLAG(BUILD_RUST_JSON_READER) internal::JSONParser parser(options); - Optional<Value> root = parser.Parse(json); - if (!root) { - if (error_code_out) - *error_code_out = parser.error_code(); - if (error_msg_out) - *error_msg_out = parser.GetErrorMessage(); - if (error_line_out) - *error_line_out = parser.error_line(); - if (error_column_out) - *error_column_out = parser.error_column(); + auto value = parser.Parse(json); + if (!value) { + Error error; + error.message = parser.GetErrorMessage(); + error.line = parser.error_line(); + error.column = parser.error_column(); + return base::unexpected(std::move(error)); } - return root ? std::make_unique<Value>(std::move(*root)) : nullptr; -} - -// static -std::string JSONReader::ErrorCodeToString(JsonParseError error_code) { - switch (error_code) { - case JSON_NO_ERROR: - return std::string(); - case JSON_INVALID_ESCAPE: - return kInvalidEscape; - case JSON_SYNTAX_ERROR: - return kSyntaxError; - case JSON_UNEXPECTED_TOKEN: - return kUnexpectedToken; - case JSON_TRAILING_COMMA: - return kTrailingComma; - case JSON_TOO_MUCH_NESTING: - return kTooMuchNesting; - case JSON_UNEXPECTED_DATA_AFTER_ROOT: - return kUnexpectedDataAfterRoot; - case JSON_UNSUPPORTED_ENCODING: - return kUnsupportedEncoding; - case JSON_UNQUOTED_DICTIONARY_KEY: - return kUnquotedDictionaryKey; - case JSON_TOO_LARGE: - return kInputTooLarge; - case JSON_PARSE_ERROR_COUNT: - break; - } - NOTREACHED(); - return std::string(); -} - -std::unique_ptr<Value> JSONReader::ReadToValue(StringPiece json) { - Optional<Value> value = parser_->Parse(json); - return value ? std::make_unique<Value>(std::move(*value)) : nullptr; -} - -JSONReader::JsonParseError JSONReader::error_code() const { - return parser_->error_code(); -} - -std::string JSONReader::GetErrorMessage() const { - return parser_->GetErrorMessage(); + return std::move(*value); +#endif // BUILDFLAG(BUILD_RUST_JSON_READER) } } // namespace base
diff --git a/base/json/json_reader.h b/base/json/json_reader.h index 2c6bd3e..00d23cf 100644 --- a/base/json/json_reader.h +++ b/base/json/json_reader.h
@@ -1,133 +1,118 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. + +// A JSON parser, converting from a base::StringPiece to a base::Value. // -// A JSON parser. Converts strings of JSON into a Value object (see -// base/values.h). -// http://www.ietf.org/rfc/rfc4627.txt?number=4627 +// The JSON spec is: +// https://tools.ietf.org/rfc/rfc8259.txt +// which obsoletes the earlier RFCs 4627, 7158 and 7159. // -// Known limitations/deviations from the RFC: -// - Only knows how to parse ints within the range of a signed 32 bit int and -// decimal numbers within a double. -// - Assumes input is encoded as UTF8. The spec says we should allow UTF-16 -// (BE or LE) and UTF-32 (BE or LE) as well. -// - We limit nesting to 100 levels to prevent stack overflow (this is allowed -// by the RFC). -// - A Unicode FAQ ("http://unicode.org/faq/utf_bom.html") writes a data -// stream may start with a Unicode Byte-Order-Mark (U+FEFF), i.e. the input -// UTF-8 string for the JSONReader::JsonToValue() function may start with a -// UTF-8 BOM (0xEF, 0xBB, 0xBF). -// To avoid the function from mis-treating a UTF-8 BOM as an invalid -// character, the function skips a Unicode BOM at the beginning of the -// Unicode string (converted from the input UTF-8 string) before parsing it. +// This RFC should be equivalent to the informal spec: +// https://www.json.org/json-en.html // -// TODO(tc): Add a parsing option to to relax object keys being wrapped in -// double quotes -// TODO(tc): Add an option to disable comment stripping +// Implementation choices permitted by the RFC: +// - Nesting is limited (to a configurable depth, 200 by default). +// - Numbers are limited to those representable by a finite double. The +// conversion from a JSON number (in the base::StringPiece input) to a +// double-flavored base::Value may also be lossy. +// - The input (which must be UTF-8) may begin with a BOM (Byte Order Mark). +// - Duplicate object keys (strings) are silently allowed. Last key-value pair +// wins. Previous pairs are discarded. +// +// Configurable (see the JSONParserOptions type) deviations from the RFC: +// - Allow trailing commas: "[1,2,]". +// - Replace invalid Unicode with U+FFFD REPLACEMENT CHARACTER. +// - Allow "// etc\n" and "/* etc */" C-style comments. +// - Allow ASCII control characters, including literal (not escaped) NUL bytes +// and new lines, within a JSON string. +// - Allow "\\v" escapes within a JSON string, producing a vertical tab. +// - Allow "\\x23" escapes within a JSON string. Subtly, the 2-digit hex value +// is a Unicode code point, not a UTF-8 byte. For example, "\\xFF" in the +// JSON source decodes to a base::Value whose string contains "\xC3\xBF", the +// UTF-8 encoding of U+00FF LATIN SMALL LETTER Y WITH DIAERESIS. Converting +// from UTF-8 to UTF-16, e.g. via UTF8ToWide, will recover a 16-bit 0x00FF. #ifndef BASE_JSON_JSON_READER_H_ #define BASE_JSON_JSON_READER_H_ -#include <memory> #include <string> #include "base/base_export.h" +#include "base/json/json_common.h" #include "base/strings/string_piece.h" +#include "base/types/expected.h" +#include "base/values.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace base { -class Value; - -namespace internal { -class JSONParser; -} - enum JSONParserOptions { - // Parses the input strictly according to RFC 4627, except for where noted - // above. + // Parses the input strictly according to RFC 8259. JSON_PARSE_RFC = 0, // Allows commas to exist after the last element in structures. JSON_ALLOW_TRAILING_COMMAS = 1 << 0, - // If set the parser replaces invalid characters with the Unicode replacement - // character (U+FFFD). If not set, invalid characters trigger a hard error and - // parsing fails. + // If set the parser replaces invalid code points (i.e. lone + // surrogates) with the Unicode replacement character (U+FFFD). If + // not set, invalid code points trigger a hard error and parsing + // fails. JSON_REPLACE_INVALID_CHARACTERS = 1 << 1, + + // Allows both C (/* */) and C++ (//) style comments. + JSON_ALLOW_COMMENTS = 1 << 2, + + // Permits unescaped ASCII control characters (such as unescaped \r and \n) + // in the range [0x00,0x1F]. + JSON_ALLOW_CONTROL_CHARS = 1 << 3, + + // Permits \\v vertical tab escapes. + JSON_ALLOW_VERT_TAB = 1 << 4, + + // Permits \\xNN escapes as described above. + JSON_ALLOW_X_ESCAPES = 1 << 5, + + // This parser historically accepted, without configuration flags, + // non-standard JSON extensions. This flag enables that traditional parsing + // behavior. + // + // This set of options is mirrored in Rust + // base::JsonOptions::with_chromium_extensions(). + JSON_PARSE_CHROMIUM_EXTENSIONS = JSON_ALLOW_COMMENTS | + JSON_ALLOW_CONTROL_CHARS | + JSON_ALLOW_VERT_TAB | JSON_ALLOW_X_ESCAPES, }; class BASE_EXPORT JSONReader { public: - static const int kStackMaxDepth; - - // Error codes during parsing. - enum JsonParseError { - JSON_NO_ERROR = 0, - JSON_INVALID_ESCAPE, - JSON_SYNTAX_ERROR, - JSON_UNEXPECTED_TOKEN, - JSON_TRAILING_COMMA, - JSON_TOO_MUCH_NESTING, - JSON_UNEXPECTED_DATA_AFTER_ROOT, - JSON_UNSUPPORTED_ENCODING, - JSON_UNQUOTED_DICTIONARY_KEY, - JSON_TOO_LARGE, - JSON_PARSE_ERROR_COUNT + struct BASE_EXPORT Error { + std::string message; + int line = 0; + int column = 0; }; - // String versions of parse error codes. - static const char kInvalidEscape[]; - static const char kSyntaxError[]; - static const char kUnexpectedToken[]; - static const char kTrailingComma[]; - static const char kTooMuchNesting[]; - static const char kUnexpectedDataAfterRoot[]; - static const char kUnsupportedEncoding[]; - static const char kUnquotedDictionaryKey[]; - static const char kInputTooLarge[]; + using Result = base::expected<Value, Error>; - // Constructs a reader. - JSONReader(int options = JSON_PARSE_RFC, int max_depth = kStackMaxDepth); - - ~JSONReader(); + // This class contains only static methods. + JSONReader() = delete; + JSONReader(const JSONReader&) = delete; + JSONReader& operator=(const JSONReader&) = delete; // Reads and parses |json|, returning a Value. - // If |json| is not a properly formed JSON string, returns nullptr. - // Wrap this in base::FooValue::From() to check the Value is of type Foo and - // convert to a FooValue at the same time. - static std::unique_ptr<Value> Read(StringPiece json, - int options = JSON_PARSE_RFC, - int max_depth = kStackMaxDepth); - - // Reads and parses |json| like Read(). |error_code_out| and |error_msg_out| - // are optional. If specified and nullptr is returned, they will be populated - // an error code and a formatted error message (including error location if - // appropriate). Otherwise, they will be unmodified. - static std::unique_ptr<Value> ReadAndReturnError( + // If |json| is not a properly formed JSON string, returns absl::nullopt. + static absl::optional<Value> Read( StringPiece json, - int options, // JSONParserOptions - int* error_code_out, - std::string* error_msg_out, - int* error_line_out = nullptr, - int* error_column_out = nullptr); + int options = JSON_PARSE_CHROMIUM_EXTENSIONS, + size_t max_depth = internal::kAbsoluteMaxDepth); - // Converts a JSON parse error code into a human readable message. - // Returns an empty string if error_code is JSON_NO_ERROR. - static std::string ErrorCodeToString(JsonParseError error_code); - - // Non-static version of Read() above. - std::unique_ptr<Value> ReadToValue(StringPiece json); - - // Returns the error code if the last call to ReadToValue() failed. - // Returns JSON_NO_ERROR otherwise. - JsonParseError error_code() const; - - // Converts error_code_ to a human-readable string, including line and column - // numbers if appropriate. - std::string GetErrorMessage() const; - - private: - std::unique_ptr<internal::JSONParser> parser_; + // Reads and parses |json| like Read(). On success returns a Value as the + // expected value. Otherwise, it returns an Error instance, populated with a + // formatted error message, an error code, and the error location if + // appropriate as the error value of the expected type. + static Result ReadAndReturnValueWithError( + StringPiece json, + int options = JSON_PARSE_CHROMIUM_EXTENSIONS); }; } // namespace base
diff --git a/base/json/json_reader_fuzzer.cc b/base/json/json_reader_fuzzer.cc index 2642646..fb82787 100644 --- a/base/json/json_reader_fuzzer.cc +++ b/base/json/json_reader_fuzzer.cc
@@ -1,10 +1,13 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2015 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/json/json_reader.h" +#include "base/json/json_writer.h" #include "base/values.h" -#include "starboard/memory.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace base { // Entry point for LibFuzzer. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { @@ -16,15 +19,26 @@ std::unique_ptr<char[]> input(new char[size - 1]); memcpy(input.get(), data, size - 1); - base::StringPiece input_string(input.get(), size - 1); + StringPiece input_string(input.get(), size - 1); const int options = data[size - 1]; - int error_code, error_line, error_column; - std::string error_message; - base::JSONReader::ReadAndReturnError(input_string, options, &error_code, - &error_message, &error_line, - &error_column); + auto json_val = + JSONReader::ReadAndReturnValueWithError(input_string, options); + if (json_val.has_value()) { + // Check that the value can be serialized and deserialized back to an + // equivalent |Value|. + const Value& value = *json_val; + std::string serialized; + CHECK(JSONWriter::Write(value, &serialized)); + + absl::optional<Value> deserialized = + JSONReader::Read(StringPiece(serialized)); + CHECK(deserialized); + CHECK_EQ(value, deserialized.value()); + } return 0; } + +} // namespace base
diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc index 762092c..9d71caa 100644 --- a/base/json/json_reader_unittest.cc +++ b/base/json/json_reader_unittest.cc
@@ -1,280 +1,341 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/json/json_reader.h" -#include <memory> +#include <stddef.h> + +#include <utility> #include "base/base_paths.h" #include "base/files/file_util.h" #include "base/logging.h" -#include "base/macros.h" #include "base/path_service.h" #include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "build/build_config.h" -#include "starboard/types.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace { + +// MSan will do a better job detecting over-read errors if the input is not +// nul-terminated on the heap. This will copy |input| to a new buffer owned by +// |owner|, returning a base::StringPiece to |owner|. +base::StringPiece MakeNotNullTerminatedInput(const char* input, + std::unique_ptr<char[]>* owner) { + size_t str_len = strlen(input); + owner->reset(new char[str_len]); + memcpy(owner->get(), input, str_len); + return base::StringPiece(owner->get(), str_len); +} + +} // namespace namespace base { TEST(JSONReaderTest, Whitespace) { - std::unique_ptr<Value> root = JSONReader().ReadToValue(" null "); + absl::optional<Value> root = JSONReader::Read(" null "); ASSERT_TRUE(root); EXPECT_TRUE(root->is_none()); } TEST(JSONReaderTest, InvalidString) { - EXPECT_FALSE(JSONReader().ReadToValue("nu")); + // These are invalid because they do not represent a JSON value, + // see https://tools.ietf.org/rfc/rfc8259.txt + EXPECT_FALSE(JSONReader::Read("")); + EXPECT_FALSE(JSONReader::Read("nu")); } TEST(JSONReaderTest, SimpleBool) { - std::unique_ptr<Value> root = JSONReader().ReadToValue("true "); + absl::optional<Value> root = JSONReader::Read("true "); ASSERT_TRUE(root); EXPECT_TRUE(root->is_bool()); } TEST(JSONReaderTest, EmbeddedComments) { - std::unique_ptr<Value> root = JSONReader().ReadToValue("/* comment */null"); + absl::optional<Value> root = JSONReader::Read("/* comment */null"); ASSERT_TRUE(root); EXPECT_TRUE(root->is_none()); - root = JSONReader().ReadToValue("40 /* comment */"); + root = JSONReader::Read("40 /* comment */"); ASSERT_TRUE(root); EXPECT_TRUE(root->is_int()); - root = JSONReader().ReadToValue("true // comment"); + root = JSONReader::Read("true // comment"); ASSERT_TRUE(root); EXPECT_TRUE(root->is_bool()); - root = JSONReader().ReadToValue("/* comment */\"sample string\""); + // Comments in different contexts. + root = JSONReader::Read("{ \"cheese\": 3\n\n // Here's a comment\n}"); ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string value; - EXPECT_TRUE(root->GetAsString(&value)); - EXPECT_EQ("sample string", value); - std::unique_ptr<ListValue> list = - ListValue::From(JSONReader().ReadToValue("[1, /* comment, 2 ] */ \n 3]")); - ASSERT_TRUE(list); - EXPECT_EQ(2u, list->GetSize()); - int int_val = 0; - EXPECT_TRUE(list->GetInteger(0, &int_val)); - EXPECT_EQ(1, int_val); - EXPECT_TRUE(list->GetInteger(1, &int_val)); - EXPECT_EQ(3, int_val); - list = ListValue::From(JSONReader().ReadToValue("[1, /*a*/2, 3]")); - ASSERT_TRUE(list); - EXPECT_EQ(3u, list->GetSize()); - root = JSONReader().ReadToValue("/* comment **/42"); + EXPECT_TRUE(root->is_dict()); + root = JSONReader::Read("{ \"cheese\": 3// Here's a comment\n}"); ASSERT_TRUE(root); - EXPECT_TRUE(root->is_int()); - EXPECT_TRUE(root->GetAsInteger(&int_val)); - EXPECT_EQ(42, int_val); - root = JSONReader().ReadToValue( + EXPECT_TRUE(root->is_dict()); + // Multiple comment markers. + root = JSONReader::Read( + "{ \"cheese\": 3// Here's a comment // and another\n}"); + ASSERT_TRUE(root); + EXPECT_TRUE(root->is_dict()); + root = JSONReader::Read("/* comment */\"sample string\""); + ASSERT_TRUE(root); + ASSERT_TRUE(root->is_string()); + EXPECT_EQ("sample string", root->GetString()); + root = JSONReader::Read("[1, /* comment, 2 ] */ \n 3]"); + ASSERT_TRUE(root); + Value::List* list = root->GetIfList(); + ASSERT_TRUE(list); + ASSERT_EQ(2u, list->size()); + ASSERT_TRUE((*list)[0].is_int()); + EXPECT_EQ(1, (*list)[0].GetInt()); + ASSERT_TRUE((*list)[1].is_int()); + EXPECT_EQ(3, (*list)[1].GetInt()); + root = JSONReader::Read("[1, /*a*/2, 3]"); + ASSERT_TRUE(root); + list = root->GetIfList(); + ASSERT_TRUE(list); + EXPECT_EQ(3u, (*list).size()); + root = JSONReader::Read("/* comment **/42"); + ASSERT_TRUE(root); + ASSERT_TRUE(root->is_int()); + EXPECT_EQ(42, root->GetInt()); + root = JSONReader::Read( "/* comment **/\n" "// */ 43\n" "44"); ASSERT_TRUE(root); EXPECT_TRUE(root->is_int()); - EXPECT_TRUE(root->GetAsInteger(&int_val)); - EXPECT_EQ(44, int_val); + EXPECT_EQ(44, root->GetInt()); + + // At one point, this parsed successfully as the value three. + EXPECT_FALSE(JSONReader::Read("/33")); } TEST(JSONReaderTest, Ints) { - std::unique_ptr<Value> root = JSONReader().ReadToValue("43"); + absl::optional<Value> root = JSONReader::Read("43"); ASSERT_TRUE(root); - EXPECT_TRUE(root->is_int()); - int int_val = 0; - EXPECT_TRUE(root->GetAsInteger(&int_val)); - EXPECT_EQ(43, int_val); + ASSERT_TRUE(root->is_int()); + EXPECT_EQ(43, root->GetInt()); } TEST(JSONReaderTest, NonDecimalNumbers) { - // According to RFC4627, oct, hex, and leading zeros are invalid JSON. - EXPECT_FALSE(JSONReader().ReadToValue("043")); - EXPECT_FALSE(JSONReader().ReadToValue("0x43")); - EXPECT_FALSE(JSONReader().ReadToValue("00")); + // According to RFC 8259, oct, hex, and leading zeros are invalid JSON. + EXPECT_FALSE(JSONReader::Read("043")); + EXPECT_FALSE(JSONReader::Read("0x43")); + EXPECT_FALSE(JSONReader::Read("00")); } TEST(JSONReaderTest, NumberZero) { // Test 0 (which needs to be special cased because of the leading zero // clause). - std::unique_ptr<Value> root = JSONReader().ReadToValue("0"); + absl::optional<Value> root = JSONReader::Read("0"); ASSERT_TRUE(root); - EXPECT_TRUE(root->is_int()); - int int_val = 1; - EXPECT_TRUE(root->GetAsInteger(&int_val)); - EXPECT_EQ(0, int_val); + ASSERT_TRUE(root->is_int()); + EXPECT_EQ(0, root->GetInt()); } TEST(JSONReaderTest, LargeIntPromotion) { // Numbers that overflow ints should succeed, being internally promoted to // storage as doubles - std::unique_ptr<Value> root = JSONReader().ReadToValue("2147483648"); - ASSERT_TRUE(root); - double double_val; - EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(2147483648.0, double_val); - root = JSONReader().ReadToValue("-2147483649"); + absl::optional<Value> root = JSONReader::Read("2147483648"); ASSERT_TRUE(root); EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(-2147483649.0, double_val); + EXPECT_DOUBLE_EQ(2147483648.0, root->GetDouble()); + root = JSONReader::Read("-2147483649"); + ASSERT_TRUE(root); + EXPECT_TRUE(root->is_double()); + EXPECT_DOUBLE_EQ(-2147483649.0, root->GetDouble()); +} + +TEST(JSONReaderTest, LargerIntIsLossy) { + // Parse LONG_MAX as a JSON number (not a JSON string). The result of the + // parse is a base::Value, either a (32-bit) int or a (64-bit) double. + // LONG_MAX would overflow an int and can only be approximated by a double. + // In this case, parsing is lossy. + const char* etc807 = "9223372036854775807"; + const char* etc808 = "9223372036854775808.000000"; + absl::optional<Value> root = JSONReader::Read(etc807); + ASSERT_TRUE(root); + ASSERT_FALSE(root->is_int()); + ASSERT_TRUE(root->is_double()); + // We use StringPrintf instead of NumberToString, because the NumberToString + // function does not let you specify the precision, and its default output, + // "9.223372036854776e+18", isn't precise enough to see the lossiness. + EXPECT_EQ(std::string(etc808), StringPrintf("%f", root->GetDouble())); } TEST(JSONReaderTest, Doubles) { - std::unique_ptr<Value> root = JSONReader().ReadToValue("43.1"); + absl::optional<Value> root = JSONReader::Read("43.1"); ASSERT_TRUE(root); EXPECT_TRUE(root->is_double()); - double double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(43.1, double_val); + EXPECT_DOUBLE_EQ(43.1, root->GetDouble()); - root = JSONReader().ReadToValue("4.3e-1"); + root = JSONReader::Read("4.3e-1"); ASSERT_TRUE(root); EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(.43, double_val); + EXPECT_DOUBLE_EQ(.43, root->GetDouble()); - root = JSONReader().ReadToValue("2.1e0"); + root = JSONReader::Read("2.1e0"); ASSERT_TRUE(root); EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(2.1, double_val); + EXPECT_DOUBLE_EQ(2.1, root->GetDouble()); - root = JSONReader().ReadToValue("2.1e+0001"); + root = JSONReader::Read("2.1e+0001"); ASSERT_TRUE(root); EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(21.0, double_val); + EXPECT_DOUBLE_EQ(21.0, root->GetDouble()); - root = JSONReader().ReadToValue("0.01"); + root = JSONReader::Read("0.01"); ASSERT_TRUE(root); EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(0.01, double_val); + EXPECT_DOUBLE_EQ(0.01, root->GetDouble()); - root = JSONReader().ReadToValue("1.00"); + root = JSONReader::Read("1.00"); ASSERT_TRUE(root); EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(1.0, double_val); + EXPECT_DOUBLE_EQ(1.0, root->GetDouble()); + + // Some "parse to float64" implementations find this one tricky. + // https://github.com/serde-rs/json/issues/707 + root = JSONReader::Read("122.416294033786585"); + ASSERT_TRUE(root); + EXPECT_TRUE(root->is_double()); + EXPECT_DOUBLE_EQ(122.416294033786585, root->GetDouble()); + + // This is syntaxtically valid, but out of range of a double. + auto value = + JSONReader::ReadAndReturnValueWithError("1e1000", JSON_PARSE_RFC); + ASSERT_FALSE(value.has_value()); } TEST(JSONReaderTest, FractionalNumbers) { // Fractional parts must have a digit before and after the decimal point. - EXPECT_FALSE(JSONReader().ReadToValue("1.")); - EXPECT_FALSE(JSONReader().ReadToValue(".1")); - EXPECT_FALSE(JSONReader().ReadToValue("1.e10")); + EXPECT_FALSE(JSONReader::Read("1.")); + EXPECT_FALSE(JSONReader::Read(".1")); + EXPECT_FALSE(JSONReader::Read("1.e10")); } TEST(JSONReaderTest, ExponentialNumbers) { // Exponent must have a digit following the 'e'. - EXPECT_FALSE(JSONReader().ReadToValue("1e")); - EXPECT_FALSE(JSONReader().ReadToValue("1E")); - EXPECT_FALSE(JSONReader().ReadToValue("1e1.")); - EXPECT_FALSE(JSONReader().ReadToValue("1e1.0")); + EXPECT_FALSE(JSONReader::Read("1e")); + EXPECT_FALSE(JSONReader::Read("1E")); + EXPECT_FALSE(JSONReader::Read("1e1.")); + EXPECT_FALSE(JSONReader::Read("1e1.0")); } -TEST(JSONReaderTest, InvalidNAN) { - EXPECT_FALSE(JSONReader().ReadToValue("1e1000")); - EXPECT_FALSE(JSONReader().ReadToValue("-1e1000")); - EXPECT_FALSE(JSONReader().ReadToValue("NaN")); - EXPECT_FALSE(JSONReader().ReadToValue("nan")); - EXPECT_FALSE(JSONReader().ReadToValue("inf")); +TEST(JSONReaderTest, InvalidInfNAN) { + // The largest finite double is roughly 1.8e308. + EXPECT_FALSE(JSONReader::Read("1e1000")); + EXPECT_FALSE(JSONReader::Read("-1e1000")); + EXPECT_FALSE(JSONReader::Read("NaN")); + EXPECT_FALSE(JSONReader::Read("nan")); + EXPECT_FALSE(JSONReader::Read("inf")); } TEST(JSONReaderTest, InvalidNumbers) { - EXPECT_FALSE(JSONReader().ReadToValue("4.3.1")); - EXPECT_FALSE(JSONReader().ReadToValue("4e3.1")); - EXPECT_FALSE(JSONReader().ReadToValue("4.a")); + EXPECT_TRUE(JSONReader::Read("4.3")); + EXPECT_FALSE(JSONReader::Read("4.")); + EXPECT_FALSE(JSONReader::Read("4.3.1")); + EXPECT_FALSE(JSONReader::Read("4e3.1")); + EXPECT_FALSE(JSONReader::Read("4.a")); + EXPECT_FALSE(JSONReader::Read("42a")); } -TEST(JSONReader, SimpleString) { - std::unique_ptr<Value> root = JSONReader().ReadToValue("\"hello world\""); +TEST(JSONReaderTest, SimpleString) { + absl::optional<Value> root = JSONReader::Read("\"hello world\""); ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ("hello world", str_val); + ASSERT_TRUE(root->is_string()); + EXPECT_EQ("hello world", root->GetString()); } TEST(JSONReaderTest, EmptyString) { - std::unique_ptr<Value> root = JSONReader().ReadToValue("\"\""); + absl::optional<Value> root = JSONReader::Read("\"\""); ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ("", str_val); + ASSERT_TRUE(root->is_string()); + EXPECT_EQ("", root->GetString()); } TEST(JSONReaderTest, BasicStringEscapes) { - std::unique_ptr<Value> root = - JSONReader().ReadToValue("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\""); + absl::optional<Value> root = + JSONReader::Read("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\""); ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ(" \"\\/\b\f\n\r\t\v", str_val); + ASSERT_TRUE(root->is_string()); + EXPECT_EQ(" \"\\/\b\f\n\r\t\v", root->GetString()); } TEST(JSONReaderTest, UnicodeEscapes) { // Test hex and unicode escapes including the null character. - std::unique_ptr<Value> root = - JSONReader().ReadToValue("\"\\x41\\x00\\u1234\\u0000\""); + absl::optional<Value> root = + JSONReader::Read("\"\\x41\\xFF\\x00\\u1234\\u0000\""); ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ(std::wstring(L"A\0\x1234\0", 4), UTF8ToWide(str_val)); + ASSERT_TRUE(root->is_string()); + const std::string& str_val = root->GetString(); + EXPECT_EQ(std::wstring(L"A\x00FF\0\x1234\0", 5), UTF8ToWide(str_val)); + + // The contents of a Unicode escape may only be four hex chars. Previously the + // parser accepted things like "0x01" and "0X01". + EXPECT_FALSE(JSONReader::Read("\"\\u0x12\"")); + + // Surrogate pairs are allowed in JSON. + EXPECT_TRUE(JSONReader::Read("\"\\uD834\\uDD1E\"")); // U+1D11E } TEST(JSONReaderTest, InvalidStrings) { - EXPECT_FALSE(JSONReader().ReadToValue("\"no closing quote")); - EXPECT_FALSE(JSONReader().ReadToValue("\"\\z invalid escape char\"")); - EXPECT_FALSE(JSONReader().ReadToValue("\"\\xAQ invalid hex code\"")); - EXPECT_FALSE(JSONReader().ReadToValue("not enough hex chars\\x1\"")); - EXPECT_FALSE(JSONReader().ReadToValue("\"not enough escape chars\\u123\"")); - EXPECT_FALSE( - JSONReader().ReadToValue("\"extra backslash at end of input\\\"")); + EXPECT_FALSE(JSONReader::Read("\"no closing quote")); + EXPECT_FALSE(JSONReader::Read("\"\\z invalid escape char\"")); + EXPECT_FALSE(JSONReader::Read("\"\\xAQ invalid hex code\"")); + EXPECT_FALSE(JSONReader::Read("not enough hex chars\\x1\"")); + EXPECT_FALSE(JSONReader::Read("\"not enough escape chars\\u123\"")); + EXPECT_FALSE(JSONReader::Read("\"extra backslash at end of input\\\"")); } TEST(JSONReaderTest, BasicArray) { - std::unique_ptr<ListValue> list = - ListValue::From(JSONReader::Read("[true, false, null]")); + absl::optional<Value> root = JSONReader::Read("[true, false, null]"); + ASSERT_TRUE(root); + Value::List* list = root->GetIfList(); ASSERT_TRUE(list); - EXPECT_EQ(3U, list->GetSize()); + EXPECT_EQ(3U, list->size()); // Test with trailing comma. Should be parsed the same as above. - std::unique_ptr<Value> root2 = + absl::optional<Value> root2 = JSONReader::Read("[true, false, null, ]", JSON_ALLOW_TRAILING_COMMAS); - EXPECT_TRUE(list->Equals(root2.get())); + ASSERT_TRUE(root2); + EXPECT_EQ(*list, *root2); } TEST(JSONReaderTest, EmptyArray) { - std::unique_ptr<ListValue> list = ListValue::From(JSONReader::Read("[]")); + absl::optional<Value> value = JSONReader::Read("[]"); + ASSERT_TRUE(value); + Value::List* list = value->GetIfList(); ASSERT_TRUE(list); - EXPECT_EQ(0U, list->GetSize()); + EXPECT_TRUE(list->empty()); +} + +TEST(JSONReaderTest, CompleteArray) { + absl::optional<Value> value = JSONReader::Read("[\"a\", 3, 4.56, null]"); + ASSERT_TRUE(value); + Value::List* list = value->GetIfList(); + ASSERT_TRUE(list); + EXPECT_EQ(4U, list->size()); } TEST(JSONReaderTest, NestedArrays) { - std::unique_ptr<ListValue> list = ListValue::From( - JSONReader::Read("[[true], [], [false, [], [null]], null]")); + absl::optional<Value> value = JSONReader::Read( + "[[true], [], {\"smell\": \"nice\",\"taste\": \"yummy\" }, [false, [], " + "[null]], null]"); + ASSERT_TRUE(value); + Value::List* list = value->GetIfList(); ASSERT_TRUE(list); - EXPECT_EQ(4U, list->GetSize()); + EXPECT_EQ(5U, list->size()); // Lots of trailing commas. - std::unique_ptr<Value> root2 = - JSONReader::Read("[[true], [], [false, [], [null, ] , ], null,]", - JSON_ALLOW_TRAILING_COMMAS); - EXPECT_TRUE(list->Equals(root2.get())); + absl::optional<Value> root2 = JSONReader::Read( + "[[true], [], {\"smell\": \"nice\",\"taste\": \"yummy\" }, [false, [], " + "[null, ] , ], null,]", + JSON_ALLOW_TRAILING_COMMAS); + ASSERT_TRUE(root2); + EXPECT_EQ(*list, *root2); } TEST(JSONReaderTest, InvalidArrays) { @@ -294,16 +355,15 @@ TEST(JSONReaderTest, ArrayTrailingComma) { // Valid if we set |allow_trailing_comma| to true. - std::unique_ptr<ListValue> list = - ListValue::From(JSONReader::Read("[true,]", JSON_ALLOW_TRAILING_COMMAS)); + absl::optional<Value> value = + JSONReader::Read("[true,]", JSON_ALLOW_TRAILING_COMMAS); + ASSERT_TRUE(value); + Value::List* list = value->GetIfList(); ASSERT_TRUE(list); - EXPECT_EQ(1U, list->GetSize()); - Value* tmp_value = nullptr; - ASSERT_TRUE(list->Get(0, &tmp_value)); - EXPECT_TRUE(tmp_value->is_bool()); - bool bool_value = false; - EXPECT_TRUE(tmp_value->GetAsBoolean(&bool_value)); - EXPECT_TRUE(bool_value); + ASSERT_EQ(1U, list->size()); + const Value& value1 = (*list)[0]; + ASSERT_TRUE(value1.is_bool()); + EXPECT_TRUE(value1.GetBool()); } TEST(JSONReaderTest, ArrayTrailingCommaNoEmptyElements) { @@ -316,30 +376,39 @@ } TEST(JSONReaderTest, EmptyDictionary) { - std::unique_ptr<DictionaryValue> dict_val = - DictionaryValue::From(JSONReader::Read("{}")); + absl::optional<Value> dict_val = JSONReader::Read("{}"); ASSERT_TRUE(dict_val); + ASSERT_TRUE(dict_val->is_dict()); } TEST(JSONReaderTest, CompleteDictionary) { - auto dict_val = DictionaryValue::From(JSONReader::Read( - "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\" }")); - ASSERT_TRUE(dict_val); - double double_val = 0.0; - EXPECT_TRUE(dict_val->GetDouble("number", &double_val)); - EXPECT_DOUBLE_EQ(9.87654321, double_val); - Value* null_val = nullptr; - ASSERT_TRUE(dict_val->Get("null", &null_val)); + absl::optional<Value> root1 = JSONReader::Read( + "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\", \"bool\": " + "false, \"more\": {} }"); + ASSERT_TRUE(root1); + const Value::Dict* root1_dict = root1->GetIfDict(); + ASSERT_TRUE(root1_dict); + auto double_val = root1_dict->FindDouble("number"); + ASSERT_TRUE(double_val); + EXPECT_DOUBLE_EQ(9.87654321, *double_val); + const Value* null_val = root1_dict->Find("null"); + ASSERT_TRUE(null_val); EXPECT_TRUE(null_val->is_none()); - std::string str_val; - EXPECT_TRUE(dict_val->GetString("S", &str_val)); - EXPECT_EQ("str", str_val); + const std::string* str_val = root1_dict->FindString("S"); + ASSERT_TRUE(str_val); + EXPECT_EQ("str", *str_val); + auto bool_val = root1_dict->FindBool("bool"); + ASSERT_TRUE(bool_val); + ASSERT_FALSE(*bool_val); - std::unique_ptr<Value> root2 = JSONReader::Read( - "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\", }", - JSON_ALLOW_TRAILING_COMMAS); + absl::optional<Value> root2 = JSONReader::Read( + "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\", \"bool\": " + "false, \"more\": {},}", + JSON_PARSE_CHROMIUM_EXTENSIONS | JSON_ALLOW_TRAILING_COMMAS); ASSERT_TRUE(root2); - EXPECT_TRUE(dict_val->Equals(root2.get())); + Value::Dict* root2_dict = root2->GetIfDict(); + ASSERT_TRUE(root2_dict); + EXPECT_EQ(*root1_dict, *root2_dict); // Test newline equivalence. root2 = JSONReader::Read( @@ -347,68 +416,96 @@ " \"number\":9.87654321,\n" " \"null\":null,\n" " \"\\x53\":\"str\",\n" + " \"bool\": false,\n" + " \"more\": {},\n" "}\n", - JSON_ALLOW_TRAILING_COMMAS); + JSON_PARSE_CHROMIUM_EXTENSIONS | JSON_ALLOW_TRAILING_COMMAS); ASSERT_TRUE(root2); - EXPECT_TRUE(dict_val->Equals(root2.get())); + root2_dict = root2->GetIfDict(); + ASSERT_TRUE(root2); + EXPECT_EQ(*root1_dict, *root2_dict); root2 = JSONReader::Read( "{\r\n" " \"number\":9.87654321,\r\n" " \"null\":null,\r\n" " \"\\x53\":\"str\",\r\n" + " \"bool\": false,\r\n" + " \"more\": {},\r\n" "}\r\n", - JSON_ALLOW_TRAILING_COMMAS); + JSON_PARSE_CHROMIUM_EXTENSIONS | JSON_ALLOW_TRAILING_COMMAS); ASSERT_TRUE(root2); - EXPECT_TRUE(dict_val->Equals(root2.get())); + root2_dict = root2->GetIfDict(); + ASSERT_TRUE(root2_dict); + EXPECT_EQ(*root1_dict, *root2_dict); } TEST(JSONReaderTest, NestedDictionaries) { - std::unique_ptr<DictionaryValue> dict_val = - DictionaryValue::From(JSONReader::Read( - "{\"inner\":{\"array\":[true]},\"false\":false,\"d\":{}}")); - ASSERT_TRUE(dict_val); - DictionaryValue* inner_dict = nullptr; - ASSERT_TRUE(dict_val->GetDictionary("inner", &inner_dict)); - ListValue* inner_array = nullptr; - ASSERT_TRUE(inner_dict->GetList("array", &inner_array)); - EXPECT_EQ(1U, inner_array->GetSize()); - bool bool_value = true; - EXPECT_TRUE(dict_val->GetBoolean("false", &bool_value)); - EXPECT_FALSE(bool_value); - inner_dict = nullptr; - EXPECT_TRUE(dict_val->GetDictionary("d", &inner_dict)); + absl::optional<Value> root1 = JSONReader::Read( + "{\"inner\":{\"array\":[true, 3, 4.56, null]},\"false\":false,\"d\":{}}"); + ASSERT_TRUE(root1); + const base::Value::Dict* root1_dict = root1->GetIfDict(); + ASSERT_TRUE(root1_dict); + const Value::Dict* inner_dict = root1_dict->FindDict("inner"); + ASSERT_TRUE(inner_dict); + const Value::List* inner_array = inner_dict->FindList("array"); + ASSERT_TRUE(inner_array); + EXPECT_EQ(4U, inner_array->size()); + auto bool_value = root1_dict->FindBool("false"); + ASSERT_TRUE(bool_value); + EXPECT_FALSE(*bool_value); + inner_dict = root1_dict->FindDict("d"); + EXPECT_TRUE(inner_dict); - std::unique_ptr<Value> root2 = JSONReader::Read( - "{\"inner\": {\"array\":[true] , },\"false\":false,\"d\":{},}", + absl::optional<Value> root2 = JSONReader::Read( + "{\"inner\": {\"array\":[true, 3, 4.56, null] , " + "},\"false\":false,\"d\":{},}", JSON_ALLOW_TRAILING_COMMAS); - EXPECT_TRUE(dict_val->Equals(root2.get())); + ASSERT_TRUE(root2); + EXPECT_EQ(*root1_dict, *root2); } TEST(JSONReaderTest, DictionaryKeysWithPeriods) { - std::unique_ptr<DictionaryValue> dict_val = DictionaryValue::From( - JSONReader::Read("{\"a.b\":3,\"c\":2,\"d.e.f\":{\"g.h.i.j\":1}}")); - ASSERT_TRUE(dict_val); - int integer_value = 0; - EXPECT_TRUE(dict_val->GetIntegerWithoutPathExpansion("a.b", &integer_value)); - EXPECT_EQ(3, integer_value); - EXPECT_TRUE(dict_val->GetIntegerWithoutPathExpansion("c", &integer_value)); - EXPECT_EQ(2, integer_value); - DictionaryValue* inner_dict = nullptr; - ASSERT_TRUE( - dict_val->GetDictionaryWithoutPathExpansion("d.e.f", &inner_dict)); - EXPECT_EQ(1U, inner_dict->size()); - EXPECT_TRUE( - inner_dict->GetIntegerWithoutPathExpansion("g.h.i.j", &integer_value)); - EXPECT_EQ(1, integer_value); + absl::optional<Value> root = + JSONReader::Read("{\"a.b\":3,\"c\":2,\"d.e.f\":{\"g.h.i.j\":1}}"); + ASSERT_TRUE(root); + Value::Dict* root_dict = root->GetIfDict(); + ASSERT_TRUE(root_dict); - dict_val = - DictionaryValue::From(JSONReader::Read("{\"a\":{\"b\":2},\"a.b\":1}")); - ASSERT_TRUE(dict_val); - EXPECT_TRUE(dict_val->GetInteger("a.b", &integer_value)); - EXPECT_EQ(2, integer_value); - EXPECT_TRUE(dict_val->GetIntegerWithoutPathExpansion("a.b", &integer_value)); - EXPECT_EQ(1, integer_value); + auto integer_value = root_dict->FindInt("a.b"); + ASSERT_TRUE(integer_value); + EXPECT_EQ(3, *integer_value); + integer_value = root_dict->FindInt("c"); + ASSERT_TRUE(integer_value); + EXPECT_EQ(2, *integer_value); + const Value::Dict* inner_dict = root_dict->FindDict("d.e.f"); + ASSERT_TRUE(inner_dict); + EXPECT_EQ(1U, inner_dict->size()); + integer_value = inner_dict->FindInt("g.h.i.j"); + ASSERT_TRUE(integer_value); + EXPECT_EQ(1, *integer_value); + + root = JSONReader::Read("{\"a\":{\"b\":2},\"a.b\":1}"); + ASSERT_TRUE(root); + root_dict = root->GetIfDict(); + ASSERT_TRUE(root_dict); + const Value* integer_path_value = root_dict->FindByDottedPath("a.b"); + ASSERT_TRUE(integer_path_value); + EXPECT_EQ(2, integer_path_value->GetInt()); + integer_value = root_dict->FindInt("a.b"); + ASSERT_TRUE(integer_value); + EXPECT_EQ(1, *integer_value); +} + +TEST(JSONReaderTest, DuplicateKeys) { + absl::optional<Value> root = JSONReader::Read("{\"x\":1,\"x\":2,\"y\":3}"); + ASSERT_TRUE(root); + const Value::Dict* root_dict = root->GetIfDict(); + ASSERT_TRUE(root_dict); + + auto integer_value = root_dict->FindInt("x"); + ASSERT_TRUE(integer_value); + EXPECT_EQ(2, *integer_value); } TEST(JSONReaderTest, InvalidDictionaries) { @@ -419,6 +516,7 @@ EXPECT_FALSE(JSONReader::Read("{foo:true}")); EXPECT_FALSE(JSONReader::Read("{1234: false}")); EXPECT_FALSE(JSONReader::Read("{:false}")); + EXPECT_FALSE(JSONReader::Read("{ , }")); // Trailing comma. EXPECT_FALSE(JSONReader::Read("{\"a\":true,}")); @@ -451,51 +549,96 @@ for (int i = 0; i < 5000; ++i) not_evil.append("[],"); not_evil.append("[]]"); - std::unique_ptr<ListValue> list = ListValue::From(JSONReader::Read(not_evil)); + absl::optional<Value> value = JSONReader::Read(not_evil); + ASSERT_TRUE(value); + Value::List* list = value->GetIfList(); ASSERT_TRUE(list); - EXPECT_EQ(5001U, list->GetSize()); + EXPECT_EQ(5001U, list->size()); } TEST(JSONReaderTest, UTF8Input) { - std::unique_ptr<Value> root = - JSONReader().ReadToValue("\"\xe7\xbd\x91\xe9\xa1\xb5\""); + absl::optional<Value> root = JSONReader::Read("\"\xe7\xbd\x91\xe9\xa1\xb5\""); ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); + ASSERT_TRUE(root->is_string()); + const std::string& str_val = root->GetString(); EXPECT_EQ(L"\x7f51\x9875", UTF8ToWide(str_val)); - std::unique_ptr<DictionaryValue> dict_val = - DictionaryValue::From(JSONReader().ReadToValue( - "{\"path\": \"/tmp/\xc3\xa0\xc3\xa8\xc3\xb2.png\"}")); - ASSERT_TRUE(dict_val); - EXPECT_TRUE(dict_val->GetString("path", &str_val)); - EXPECT_EQ("/tmp/\xC3\xA0\xC3\xA8\xC3\xB2.png", str_val); + root = JSONReader::Read("{\"path\": \"/tmp/\xc3\xa0\xc3\xa8\xc3\xb2.png\"}"); + ASSERT_TRUE(root); + const Value::Dict* root_dict = root->GetIfDict(); + ASSERT_TRUE(root_dict); + const std::string* maybe_string = root_dict->FindString("path"); + ASSERT_TRUE(maybe_string); + EXPECT_EQ("/tmp/\xC3\xA0\xC3\xA8\xC3\xB2.png", *maybe_string); + + // JSON can encode non-characters. + const char* const noncharacters[] = { + "\"\xEF\xB7\x90\"", // U+FDD0 + "\"\xEF\xB7\x9F\"", // U+FDDF + "\"\xEF\xB7\xAF\"", // U+FDEF + "\"\xEF\xBF\xBE\"", // U+FFFE + "\"\xEF\xBF\xBF\"", // U+FFFF + "\"\xF0\x9F\xBF\xBE\"", // U+01FFFE + "\"\xF0\x9F\xBF\xBF\"", // U+01FFFF + "\"\xF0\xAF\xBF\xBE\"", // U+02FFFE + "\"\xF0\xAF\xBF\xBF\"", // U+02FFFF + "\"\xF0\xBF\xBF\xBE\"", // U+03FFFE + "\"\xF0\xBF\xBF\xBF\"", // U+03FFFF + "\"\xF1\x8F\xBF\xBE\"", // U+04FFFE + "\"\xF1\x8F\xBF\xBF\"", // U+04FFFF + "\"\xF1\x9F\xBF\xBE\"", // U+05FFFE + "\"\xF1\x9F\xBF\xBF\"", // U+05FFFF + "\"\xF1\xAF\xBF\xBE\"", // U+06FFFE + "\"\xF1\xAF\xBF\xBF\"", // U+06FFFF + "\"\xF1\xBF\xBF\xBE\"", // U+07FFFE + "\"\xF1\xBF\xBF\xBF\"", // U+07FFFF + "\"\xF2\x8F\xBF\xBE\"", // U+08FFFE + "\"\xF2\x8F\xBF\xBF\"", // U+08FFFF + "\"\xF2\x9F\xBF\xBE\"", // U+09FFFE + "\"\xF2\x9F\xBF\xBF\"", // U+09FFFF + "\"\xF2\xAF\xBF\xBE\"", // U+0AFFFE + "\"\xF2\xAF\xBF\xBF\"", // U+0AFFFF + "\"\xF2\xBF\xBF\xBE\"", // U+0BFFFE + "\"\xF2\xBF\xBF\xBF\"", // U+0BFFFF + "\"\xF3\x8F\xBF\xBE\"", // U+0CFFFE + "\"\xF3\x8F\xBF\xBF\"", // U+0CFFFF + "\"\xF3\x9F\xBF\xBE\"", // U+0DFFFE + "\"\xF3\x9F\xBF\xBF\"", // U+0DFFFF + "\"\xF3\xAF\xBF\xBE\"", // U+0EFFFE + "\"\xF3\xAF\xBF\xBF\"", // U+0EFFFF + "\"\xF3\xBF\xBF\xBE\"", // U+0FFFFE + "\"\xF3\xBF\xBF\xBF\"", // U+0FFFFF + "\"\xF4\x8F\xBF\xBE\"", // U+10FFFE + "\"\xF4\x8F\xBF\xBF\"", // U+10FFFF + }; + for (auto* noncharacter : noncharacters) { + root = JSONReader::Read(noncharacter); + ASSERT_TRUE(root); + ASSERT_TRUE(root->is_string()); + EXPECT_EQ(std::string(noncharacter + 1, strlen(noncharacter) - 2), + root->GetString()); + } } TEST(JSONReaderTest, InvalidUTF8Input) { - EXPECT_FALSE(JSONReader().ReadToValue("\"345\xb0\xa1\xb0\xa2\"")); - EXPECT_FALSE(JSONReader().ReadToValue("\"123\xc0\x81\"")); - EXPECT_FALSE(JSONReader().ReadToValue("\"abc\xc0\xae\"")); + EXPECT_FALSE(JSONReader::Read("\"345\xb0\xa1\xb0\xa2\"")); + EXPECT_FALSE(JSONReader::Read("\"123\xc0\x81\"")); + EXPECT_FALSE(JSONReader::Read("\"abc\xc0\xae\"")); } TEST(JSONReaderTest, UTF16Escapes) { - std::unique_ptr<Value> root = JSONReader().ReadToValue("\"\\u20ac3,14\""); + absl::optional<Value> root = JSONReader::Read("\"\\u20ac3,14\""); ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); + ASSERT_TRUE(root->is_string()); EXPECT_EQ( "\xe2\x82\xac" "3,14", - str_val); + root->GetString()); - root = JSONReader().ReadToValue("\"\\ud83d\\udca9\\ud83d\\udc6c\""); + root = JSONReader::Read("\"\\ud83d\\udca9\\ud83d\\udc6c\""); ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - str_val.clear(); - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ("\xf0\x9f\x92\xa9\xf0\x9f\x91\xac", str_val); + ASSERT_TRUE(root->is_string()); + EXPECT_EQ("\xf0\x9f\x92\xa9\xf0\x9f\x91\xac", root->GetString()); } TEST(JSONReaderTest, InvalidUTF16Escapes) { @@ -506,42 +649,39 @@ "\"\\uzz89\"", // Invalid scalar. "\"\\ud83d\\udca\"", // Invalid lower surrogate. "\"\\ud83d\\ud83d\"", // Invalid lower surrogate. - "\"\\ud83d\\uaaaZ\"" // Invalid lower surrogate. + "\"\\ud83d\\uaaaZ\"", // Invalid lower surrogate. "\"\\ud83foo\"", // No lower surrogate. - "\"\\ud83d\\foo\"" // No lower surrogate. - "\"\\ud83\\foo\"" // Invalid upper surrogate. - "\"\\ud83d\\u1\"" // No lower surrogate. - "\"\\ud83\\u1\"" // Invalid upper surrogate. + "\"\\ud83d\\foo\"", // No lower surrogate. + "\"\\ud83\\foo\"", // Invalid upper surrogate. + "\"\\ud83d\\u1\"", // No lower surrogate. + "\"\\ud83\\u1\"", // Invalid upper surrogate. }; - std::unique_ptr<Value> root; - for (size_t i = 0; i < arraysize(cases); ++i) { - root = JSONReader().ReadToValue(cases[i]); - EXPECT_FALSE(root) << cases[i]; + absl::optional<Value> root; + for (auto* i : cases) { + root = JSONReader::Read(i); + EXPECT_FALSE(root) << i; } } TEST(JSONReaderTest, LiteralRoots) { - std::unique_ptr<Value> root = JSONReader::Read("null"); + absl::optional<Value> root = JSONReader::Read("null"); ASSERT_TRUE(root); EXPECT_TRUE(root->is_none()); root = JSONReader::Read("true"); ASSERT_TRUE(root); - bool bool_value; - EXPECT_TRUE(root->GetAsBoolean(&bool_value)); - EXPECT_TRUE(bool_value); + ASSERT_TRUE(root->is_bool()); + EXPECT_TRUE(root->GetBool()); root = JSONReader::Read("10"); ASSERT_TRUE(root); - int integer_value; - EXPECT_TRUE(root->GetAsInteger(&integer_value)); - EXPECT_EQ(10, integer_value); + ASSERT_TRUE(root->is_int()); + EXPECT_EQ(10, root->GetInt()); root = JSONReader::Read("\"root\""); ASSERT_TRUE(root); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ("root", str_val); + ASSERT_TRUE(root->is_string()); + EXPECT_EQ("root", root->GetString()); } TEST(JSONReaderTest, ReadFromFile) { @@ -558,24 +698,23 @@ std::string input; ASSERT_TRUE(ReadFileToString(path.AppendASCII("bom_feff.json"), &input)); - JSONReader reader; - std::unique_ptr<Value> root(reader.ReadToValue(input)); - ASSERT_TRUE(root) << reader.GetErrorMessage(); + auto root = JSONReader::ReadAndReturnValueWithError(input); + ASSERT_TRUE(root.has_value()) << root.error().message; EXPECT_TRUE(root->is_dict()); } // Tests that the root of a JSON object can be deleted safely while its // children outlive it. TEST(JSONReaderTest, StringOptimizations) { - std::unique_ptr<Value> dict_literal_0; - std::unique_ptr<Value> dict_literal_1; - std::unique_ptr<Value> dict_string_0; - std::unique_ptr<Value> dict_string_1; - std::unique_ptr<Value> list_value_0; - std::unique_ptr<Value> list_value_1; + Value dict_literal_0; + Value dict_literal_1; + Value dict_string_0; + Value dict_string_1; + Value list_value_0; + Value list_value_1; { - std::unique_ptr<Value> root = JSONReader::Read( + absl::optional<Value> root = JSONReader::Read( "{" " \"test\": {" " \"foo\": true," @@ -590,46 +729,53 @@ "}", JSON_PARSE_RFC); ASSERT_TRUE(root); + Value::Dict* root_dict = root->GetIfDict(); + ASSERT_TRUE(root_dict); - DictionaryValue* root_dict = nullptr; - ASSERT_TRUE(root->GetAsDictionary(&root_dict)); + Value::Dict* dict = root_dict->FindDict("test"); + ASSERT_TRUE(dict); + Value::List* list = root_dict->FindList("list"); + ASSERT_TRUE(list); - DictionaryValue* dict = nullptr; - ListValue* list = nullptr; + Value* to_move = dict->Find("foo"); + ASSERT_TRUE(to_move); + dict_literal_0 = std::move(*to_move); + to_move = dict->Find("bar"); + ASSERT_TRUE(to_move); + dict_literal_1 = std::move(*to_move); + to_move = dict->Find("baz"); + ASSERT_TRUE(to_move); + dict_string_0 = std::move(*to_move); + to_move = dict->Find("moo"); + ASSERT_TRUE(to_move); + dict_string_1 = std::move(*to_move); + ASSERT_TRUE(dict->Remove("foo")); + ASSERT_TRUE(dict->Remove("bar")); + ASSERT_TRUE(dict->Remove("baz")); + ASSERT_TRUE(dict->Remove("moo")); - ASSERT_TRUE(root_dict->GetDictionary("test", &dict)); - ASSERT_TRUE(root_dict->GetList("list", &list)); - - ASSERT_TRUE(dict->Remove("foo", &dict_literal_0)); - ASSERT_TRUE(dict->Remove("bar", &dict_literal_1)); - ASSERT_TRUE(dict->Remove("baz", &dict_string_0)); - ASSERT_TRUE(dict->Remove("moo", &dict_string_1)); - - ASSERT_EQ(2u, list->GetSize()); - ASSERT_TRUE(list->Remove(0, &list_value_0)); - ASSERT_TRUE(list->Remove(0, &list_value_1)); + ASSERT_EQ(2u, list->size()); + list_value_0 = std::move((*list)[0]); + list_value_1 = std::move((*list)[1]); + list->clear(); } - bool b = false; - double d = 0; - std::string s; + ASSERT_TRUE(dict_literal_0.is_bool()); + EXPECT_TRUE(dict_literal_0.GetBool()); - EXPECT_TRUE(dict_literal_0->GetAsBoolean(&b)); - EXPECT_TRUE(b); + ASSERT_TRUE(dict_literal_1.is_double()); + EXPECT_EQ(3.14, dict_literal_1.GetDouble()); - EXPECT_TRUE(dict_literal_1->GetAsDouble(&d)); - EXPECT_EQ(3.14, d); + ASSERT_TRUE(dict_string_0.is_string()); + EXPECT_EQ("bat", dict_string_0.GetString()); - EXPECT_TRUE(dict_string_0->GetAsString(&s)); - EXPECT_EQ("bat", s); + ASSERT_TRUE(dict_string_1.is_string()); + EXPECT_EQ("cow", dict_string_1.GetString()); - EXPECT_TRUE(dict_string_1->GetAsString(&s)); - EXPECT_EQ("cow", s); - - EXPECT_TRUE(list_value_0->GetAsString(&s)); - EXPECT_EQ("a", s); - EXPECT_TRUE(list_value_1->GetAsString(&s)); - EXPECT_EQ("b", s); + ASSERT_TRUE(list_value_0.is_string()); + EXPECT_EQ("a", list_value_0.GetString()); + ASSERT_TRUE(list_value_1.is_string()); + EXPECT_EQ("b", list_value_1.GetString()); } // A smattering of invalid JSON designed to test specific portions of the @@ -640,30 +786,325 @@ "/* test *", "{\"foo\"", "{\"foo\":", " [", "\"\\u123g\"", "{\n\"eh:\n}", }; - for (size_t i = 0; i < arraysize(kInvalidJson); ++i) { - JSONReader reader; + for (size_t i = 0; i < std::size(kInvalidJson); ++i) { LOG(INFO) << "Sanity test " << i << ": <" << kInvalidJson[i] << ">"; - EXPECT_FALSE(reader.ReadToValue(kInvalidJson[i])); - EXPECT_NE(JSONReader::JSON_NO_ERROR, reader.error_code()); - EXPECT_NE("", reader.GetErrorMessage()); + auto root = JSONReader::ReadAndReturnValueWithError(kInvalidJson[i]); + EXPECT_FALSE(root.has_value()); + EXPECT_NE("", root.error().message); } } TEST(JSONReaderTest, IllegalTrailingNull) { - const char json[] = { '"', 'n', 'u', 'l', 'l', '"', '\0' }; + const char json[] = {'"', 'n', 'u', 'l', 'l', '"', '\0'}; std::string json_string(json, sizeof(json)); - JSONReader reader; - EXPECT_FALSE(reader.ReadToValue(json_string)); - EXPECT_EQ(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, reader.error_code()); + auto root = JSONReader::ReadAndReturnValueWithError(json_string); + EXPECT_FALSE(root.has_value()); + EXPECT_NE("", root.error().message); +} + +TEST(JSONReaderTest, ASCIIControlCodes) { + // A literal NUL byte or a literal new line, in a JSON string, should be + // rejected. RFC 8259 section 7 says "the characters that MUST be escaped + // [include]... the control characters (U+0000 through U+001F)". + // + // Nonetheless, we accept them, for backwards compatibility. + const char json[] = {'"', 'a', '\0', 'b', '\n', 'c', '"'}; + absl::optional<Value> root = + JSONReader::Read(std::string(json, sizeof(json))); + ASSERT_TRUE(root); + ASSERT_TRUE(root->is_string()); + EXPECT_EQ(5u, root->GetString().length()); } TEST(JSONReaderTest, MaxNesting) { std::string json(R"({"outer": { "inner": {"foo": true}}})"); - std::unique_ptr<Value> root; - root = JSONReader::Read(json, JSON_PARSE_RFC, 3); - ASSERT_FALSE(root); - root = JSONReader::Read(json, JSON_PARSE_RFC, 4); + EXPECT_FALSE(JSONReader::Read(json, JSON_PARSE_RFC, 3)); + EXPECT_TRUE(JSONReader::Read(json, JSON_PARSE_RFC, 4)); +} + +TEST(JSONReaderTest, Decode4ByteUtf8Char) { + // kUtf8Data contains a 4 byte unicode character (a smiley!) that JSONReader + // should be able to handle. The UTF-8 encoding of U+1F607 SMILING FACE WITH + // HALO is "\xF0\x9F\x98\x87". + const char kUtf8Data[] = "[\"😇\",[],[],[],{\"google:suggesttype\":[]}]"; + absl::optional<Value> root = JSONReader::Read(kUtf8Data, JSON_PARSE_RFC); ASSERT_TRUE(root); + Value::List* list = root->GetIfList(); + ASSERT_TRUE(list); + ASSERT_EQ(5u, list->size()); + ASSERT_TRUE((*list)[0].is_string()); + EXPECT_EQ("\xF0\x9F\x98\x87", (*list)[0].GetString()); +} + +TEST(JSONReaderTest, DecodeUnicodeNonCharacter) { + // Tests Unicode code points (encoded as escaped UTF-16) that are not valid + // characters. + EXPECT_TRUE(JSONReader::Read("[\"\\uFDD0\"]")); // U+FDD0 + EXPECT_TRUE(JSONReader::Read("[\"\\uFDDF\"]")); // U+FDDF + EXPECT_TRUE(JSONReader::Read("[\"\\uFDEF\"]")); // U+FDEF + EXPECT_TRUE(JSONReader::Read("[\"\\uFFFE\"]")); // U+FFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uFFFF\"]")); // U+FFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uD83F\\uDFFE\"]")); // U+01FFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uD83F\\uDFFF\"]")); // U+01FFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uD87F\\uDFFE\"]")); // U+02FFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uD87F\\uDFFF\"]")); // U+02FFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uD8BF\\uDFFE\"]")); // U+03FFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uD8BF\\uDFFF\"]")); // U+03FFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uD8FF\\uDFFE\"]")); // U+04FFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uD8FF\\uDFFF\"]")); // U+04FFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uD93F\\uDFFE\"]")); // U+05FFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uD93F\\uDFFF\"]")); // U+05FFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uD97F\\uDFFE\"]")); // U+06FFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uD97F\\uDFFF\"]")); // U+06FFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uD9BF\\uDFFE\"]")); // U+07FFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uD9BF\\uDFFF\"]")); // U+07FFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uD9FF\\uDFFE\"]")); // U+08FFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uD9FF\\uDFFF\"]")); // U+08FFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uDA3F\\uDFFE\"]")); // U+09FFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uDA3F\\uDFFF\"]")); // U+09FFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uDA7F\\uDFFE\"]")); // U+0AFFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uDA7F\\uDFFF\"]")); // U+0AFFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uDABF\\uDFFE\"]")); // U+0BFFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uDABF\\uDFFF\"]")); // U+0BFFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uDAFF\\uDFFE\"]")); // U+0CFFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uDAFF\\uDFFF\"]")); // U+0CFFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uDB3F\\uDFFE\"]")); // U+0DFFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uDB3F\\uDFFF\"]")); // U+0DFFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uDB7F\\uDFFE\"]")); // U+0EFFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uDB7F\\uDFFF\"]")); // U+0EFFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uDBBF\\uDFFE\"]")); // U+0FFFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uDBBF\\uDFFF\"]")); // U+0FFFFF + EXPECT_TRUE(JSONReader::Read("[\"\\uDBFF\\uDFFE\"]")); // U+10FFFE + EXPECT_TRUE(JSONReader::Read("[\"\\uDBFF\\uDFFF\"]")); // U+10FFFF +} + +TEST(JSONReaderTest, DecodeNegativeEscapeSequence) { + EXPECT_FALSE(JSONReader::Read("[\"\\x-A\"]")); + EXPECT_FALSE(JSONReader::Read("[\"\\u-00A\"]")); +} + +// Verifies invalid code points are replaced. +TEST(JSONReaderTest, ReplaceInvalidCharacters) { + // U+D800 is a lone high surrogate. + const std::string invalid_high = "\"\xED\xA0\x80\""; + absl::optional<Value> value = + JSONReader::Read(invalid_high, JSON_REPLACE_INVALID_CHARACTERS); + ASSERT_TRUE(value); + ASSERT_TRUE(value->is_string()); + // Expect three U+FFFD (one for each UTF-8 byte in the invalid code point). + EXPECT_EQ("\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBD", value->GetString()); + + // U+DFFF is a lone low surrogate. + const std::string invalid_low = "\"\xED\xBF\xBF\""; + value = JSONReader::Read(invalid_low, JSON_REPLACE_INVALID_CHARACTERS); + ASSERT_TRUE(value); + ASSERT_TRUE(value->is_string()); + // Expect three U+FFFD (one for each UTF-8 byte in the invalid code point). + EXPECT_EQ("\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBD", value->GetString()); +} + +TEST(JSONReaderTest, ReplaceInvalidUTF16EscapeSequence) { + // U+D800 is a lone high surrogate. + const std::string invalid_high = "\"_\\uD800_\""; + absl::optional<Value> value = + JSONReader::Read(invalid_high, JSON_REPLACE_INVALID_CHARACTERS); + ASSERT_TRUE(value); + ASSERT_TRUE(value->is_string()); + EXPECT_EQ("_\xEF\xBF\xBD_", value->GetString()); + + // U+DFFF is a lone low surrogate. + const std::string invalid_low = "\"_\\uDFFF_\""; + value = JSONReader::Read(invalid_low, JSON_REPLACE_INVALID_CHARACTERS); + ASSERT_TRUE(value); + ASSERT_TRUE(value->is_string()); + EXPECT_EQ("_\xEF\xBF\xBD_", value->GetString()); +} + +TEST(JSONReaderTest, ParseNumberErrors) { + const struct { + const char* input; + bool parse_success; + double value; + } kCases[] = { + // clang-format off + {"1", true, 1}, + {"2.", false, 0}, + {"42", true, 42}, + {"6e", false, 0}, + {"43e2", true, 4300}, + {"43e-", false, 0}, + {"9e-3", true, 0.009}, + {"2e+", false, 0}, + {"2e+2", true, 200}, + // clang-format on + }; + + for (unsigned int i = 0; i < std::size(kCases); ++i) { + auto test_case = kCases[i]; + SCOPED_TRACE(StringPrintf("case %u: \"%s\"", i, test_case.input)); + + std::unique_ptr<char[]> input_owner; + StringPiece input = + MakeNotNullTerminatedInput(test_case.input, &input_owner); + + absl::optional<Value> result = JSONReader::Read(input); + EXPECT_EQ(test_case.parse_success, result.has_value()); + + if (!result) + continue; + + ASSERT_TRUE(result->is_double() || result->is_int()); + EXPECT_EQ(test_case.value, result->GetDouble()); + } +} + +TEST(JSONReaderTest, UnterminatedInputs) { + const char* const kCases[] = { + // clang-format off + "/", + "//", + "/*", + "\"xxxxxx", + "\"", + "{ ", + "[\t", + "tru", + "fals", + "nul", + "\"\\x", + "\"\\x2", + "\"\\u123", + "\"\\uD803\\u", + "\"\\", + "\"\\/", + // clang-format on + }; + + for (unsigned int i = 0; i < std::size(kCases); ++i) { + auto* test_case = kCases[i]; + SCOPED_TRACE(StringPrintf("case %u: \"%s\"", i, test_case)); + + std::unique_ptr<char[]> input_owner; + StringPiece input = MakeNotNullTerminatedInput(test_case, &input_owner); + + EXPECT_FALSE(JSONReader::Read(input)); + } +} + +TEST(JSONReaderTest, LineColumnCounting) { + const struct { + const char* input; + int error_line; + int error_column; + } kCases[] = { + // For all but the "q_is_not_etc" case, the error (indicated by ^ in the + // comments) is seeing a digit when expecting ',' or ']'. + { + // Line and column counts are 1-based, not 0-based. + "q_is_not_the_start_of_any_valid_JSON_token", + 1, + 1, + }, + { + "[2,4,6 8", + // -----^ + 1, + 8, + }, + { + "[2,4,6\t8", + // ------^ + 1, + 8, + }, + { + "[2,4,6\n8", + // ------^ + 2, + 1, + }, + { + "[\n0,\n1,\n2,\n3,4,5,6 7,\n8,\n9\n]", + // ---------------------^ + 5, + 9, + }, + { + // Same as the previous example, but with "\r\n"s instead of "\n"s. + "[\r\n0,\r\n1,\r\n2,\r\n3,4,5,6 7,\r\n8,\r\n9\r\n]", + // -----------------------------^ + 5, + 9, + }, + // The JSON spec forbids unescaped ASCII control characters (including + // line breaks) within a string, but our implementation is more lenient. + { + "[\"3\n1\" 4", + // --------^ + 2, + 4, + }, + { + "[\"3\r\n1\" 4", + // ----------^ + 2, + 4, + }, + }; + + for (unsigned int i = 0; i < std::size(kCases); ++i) { + auto test_case = kCases[i]; + SCOPED_TRACE(StringPrintf("case %u: \"%s\"", i, test_case.input)); + + auto root = JSONReader::ReadAndReturnValueWithError( + test_case.input, JSON_PARSE_RFC | JSON_ALLOW_CONTROL_CHARS); + EXPECT_FALSE(root.has_value()); + EXPECT_EQ(test_case.error_line, root.error().line); + EXPECT_EQ(test_case.error_column, root.error().column); + } +} + +TEST(JSONReaderTest, ChromiumExtensions) { + // All of these cases should parse with JSON_PARSE_CHROMIUM_EXTENSIONS but + // fail with JSON_PARSE_RFC. + const struct { + // The JSON input. + const char* input; + // What JSON_* option permits this extension. + int option; + } kCases[] = { + {"{ /* comment */ \"foo\": 3 }", JSON_ALLOW_COMMENTS}, + {"{ // comment\n \"foo\": 3 }", JSON_ALLOW_COMMENTS}, + {"[\"\\xAB\"]", JSON_ALLOW_X_ESCAPES}, + {"[\"\b\"]", JSON_ALLOW_CONTROL_CHARS}, + {"[\"\f\"]", JSON_ALLOW_CONTROL_CHARS}, + {"[\"\n\"]", JSON_ALLOW_CONTROL_CHARS}, + {"[\"\r\"]", JSON_ALLOW_CONTROL_CHARS}, + {"[\"\t\"]", JSON_ALLOW_CONTROL_CHARS}, + {"[\"\v\"]", JSON_ALLOW_CONTROL_CHARS}, + {"[\"\\v\"]", JSON_ALLOW_VERT_TAB}, + }; + + for (size_t i = 0; i < std::size(kCases); ++i) { + SCOPED_TRACE(testing::Message() << "case " << i); + const auto& test_case = kCases[i]; + + auto result = JSONReader::ReadAndReturnValueWithError(test_case.input, + JSON_PARSE_RFC); + EXPECT_FALSE(result.has_value()); + + result = JSONReader::ReadAndReturnValueWithError( + test_case.input, JSON_PARSE_RFC | test_case.option); + EXPECT_TRUE(result.has_value()); + + result = JSONReader::ReadAndReturnValueWithError( + test_case.input, JSON_PARSE_CHROMIUM_EXTENSIONS); + EXPECT_TRUE(result.has_value()); + + result = JSONReader::ReadAndReturnValueWithError( + test_case.input, JSON_PARSE_CHROMIUM_EXTENSIONS & ~test_case.option); + EXPECT_FALSE(result.has_value()); + } } } // namespace base
diff --git a/base/json/json_string_value_serializer.cc b/base/json/json_string_value_serializer.cc index f9c45a4..561bfa0 100644 --- a/base/json/json_string_value_serializer.cc +++ b/base/json/json_string_value_serializer.cc
@@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,7 +6,6 @@ #include "base/json/json_reader.h" #include "base/json/json_writer.h" -#include "base/logging.h" using base::Value; @@ -17,16 +16,16 @@ JSONStringValueSerializer::~JSONStringValueSerializer() = default; -bool JSONStringValueSerializer::Serialize(const Value& root) { +bool JSONStringValueSerializer::Serialize(base::ValueView root) { return SerializeInternal(root, false); } bool JSONStringValueSerializer::SerializeAndOmitBinaryValues( - const Value& root) { + base::ValueView root) { return SerializeInternal(root, true); } -bool JSONStringValueSerializer::SerializeInternal(const Value& root, +bool JSONStringValueSerializer::SerializeInternal(base::ValueView root, bool omit_binary_values) { if (!json_string_) return false; @@ -50,6 +49,14 @@ std::unique_ptr<Value> JSONStringValueDeserializer::Deserialize( int* error_code, std::string* error_str) { - return base::JSONReader::ReadAndReturnError(json_string_, options_, - error_code, error_str); + auto ret = + base::JSONReader::ReadAndReturnValueWithError(json_string_, options_); + if (ret.has_value()) + return base::Value::ToUniquePtrValue(std::move(*ret)); + + if (error_code) + *error_code = base::ValueDeserializer::kErrorCodeInvalidFormat; + if (error_str) + *error_str = std::move(ret.error().message); + return nullptr; }
diff --git a/base/json/json_string_value_serializer.h b/base/json/json_string_value_serializer.h index 55a53e2..5386546 100644 --- a/base/json/json_string_value_serializer.h +++ b/base/json/json_string_value_serializer.h
@@ -1,15 +1,16 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_JSON_JSON_STRING_VALUE_SERIALIZER_H_ #define BASE_JSON_JSON_STRING_VALUE_SERIALIZER_H_ +#include <memory> #include <string> #include "base/base_export.h" -#include "base/files/file_path.h" -#include "base/macros.h" +#include "base/json/json_reader.h" +#include "base/memory/raw_ptr.h" #include "base/strings/string_piece.h" #include "base/values.h" @@ -20,28 +21,30 @@ // string. |json_string| must not be null. explicit JSONStringValueSerializer(std::string* json_string); + JSONStringValueSerializer(const JSONStringValueSerializer&) = delete; + JSONStringValueSerializer& operator=(const JSONStringValueSerializer&) = + delete; + ~JSONStringValueSerializer() override; // Attempt to serialize the data structure represented by Value into // JSON. If the return value is true, the result will have been written // into the string passed into the constructor. - bool Serialize(const base::Value& root) override; + bool Serialize(base::ValueView root) override; // Equivalent to Serialize(root) except binary values are omitted from the // output. - bool SerializeAndOmitBinaryValues(const base::Value& root); + bool SerializeAndOmitBinaryValues(base::ValueView root); void set_pretty_print(bool new_value) { pretty_print_ = new_value; } bool pretty_print() { return pretty_print_; } private: - bool SerializeInternal(const base::Value& root, bool omit_binary_values); + bool SerializeInternal(base::ValueView root, bool omit_binary_values); // Owned by the caller of the constructor. - std::string* json_string_; + raw_ptr<std::string> json_string_; bool pretty_print_; // If true, serialization will span multiple lines. - - DISALLOW_COPY_AND_ASSIGN(JSONStringValueSerializer); }; class BASE_EXPORT JSONStringValueDeserializer : public base::ValueDeserializer { @@ -49,17 +52,24 @@ // This retains a reference to the contents of |json_string|, so the data // must outlive the JSONStringValueDeserializer. |options| is a bitmask of // JSONParserOptions. - explicit JSONStringValueDeserializer(const base::StringPiece& json_string, - int options = 0); + explicit JSONStringValueDeserializer( + const base::StringPiece& json_string, + int options = base::JSON_PARSE_CHROMIUM_EXTENSIONS); + + JSONStringValueDeserializer(const JSONStringValueDeserializer&) = delete; + JSONStringValueDeserializer& operator=(const JSONStringValueDeserializer&) = + delete; ~JSONStringValueDeserializer() override; - // Attempt to deserialize the data structure encoded in the string passed - // in to the constructor into a structure of Value objects. If the return - // value is null, and if |error_code| is non-null, |error_code| will - // contain an integer error code (a JsonParseError in this case). - // If |error_message| is non-null, it will be filled in with a formatted - // error message including the location of the error if appropriate. + // Attempts to deserialize |json_string_| into a structure of Value objects. + // If the return value is null, then + // (1) |error_code| will be filled with an integer error code + // (base::ValueDeserializer::kErrorCodeInvalidFormat) if a non-null + // |error_code| was given. + // (2) |error_message| will be filled with a formatted error message, + // including the location of the error (if appropriate), if a non-null + // |error_message| was given. // The caller takes ownership of the returned value. std::unique_ptr<base::Value> Deserialize(int* error_code, std::string* error_message) override; @@ -68,8 +78,6 @@ // Data is owned by the caller of the constructor. base::StringPiece json_string_; const int options_; - - DISALLOW_COPY_AND_ASSIGN(JSONStringValueDeserializer); }; #endif // BASE_JSON_JSON_STRING_VALUE_SERIALIZER_H_
diff --git a/base/json/json_value_converter.cc b/base/json/json_value_converter.cc index 6f772f3..50a3976 100644 --- a/base/json/json_value_converter.cc +++ b/base/json/json_value_converter.cc
@@ -1,35 +1,57 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2014 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/json/json_value_converter.h" +#include "base/strings/utf_string_conversions.h" + namespace base { namespace internal { bool BasicValueConverter<int>::Convert( const base::Value& value, int* field) const { - return value.GetAsInteger(field); + if (!value.is_int()) + return false; + if (field) + *field = value.GetInt(); + return true; } bool BasicValueConverter<std::string>::Convert( const base::Value& value, std::string* field) const { - return value.GetAsString(field); + if (!value.is_string()) + return false; + if (field) + *field = value.GetString(); + return true; } -bool BasicValueConverter<string16>::Convert( - const base::Value& value, string16* field) const { - return value.GetAsString(field); +bool BasicValueConverter<std::u16string>::Convert(const base::Value& value, + std::u16string* field) const { + if (!value.is_string()) + return false; + if (field) + *field = base::UTF8ToUTF16(value.GetString()); + return true; } bool BasicValueConverter<double>::Convert( const base::Value& value, double* field) const { - return value.GetAsDouble(field); + if (!value.is_double() && !value.is_int()) + return false; + if (field) + *field = value.GetDouble(); + return true; } bool BasicValueConverter<bool>::Convert( const base::Value& value, bool* field) const { - return value.GetAsBoolean(field); + if (!value.is_bool()) + return false; + if (field) + *field = value.GetBool(); + return true; } } // namespace internal
diff --git a/base/json/json_value_converter.h b/base/json/json_value_converter.h index 9a7b999..0d00cf3 100644 --- a/base/json/json_value_converter.h +++ b/base/json/json_value_converter.h
@@ -1,22 +1,22 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_JSON_JSON_VALUE_CONVERTER_H_ #define BASE_JSON_JSON_VALUE_CONVERTER_H_ +#include <stddef.h> + #include <memory> #include <string> +#include <utility> #include <vector> #include "base/base_export.h" #include "base/logging.h" -#include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/strings/string16.h" #include "base/strings/string_piece.h" #include "base/values.h" -#include "starboard/types.h" // JSONValueConverter converts a JSON value into a C++ struct in a // lightweight way. @@ -91,18 +91,21 @@ namespace internal { -template<typename StructType> +template <typename StructType> class FieldConverterBase { public: explicit FieldConverterBase(const std::string& path) : field_path_(path) {} + + FieldConverterBase(const FieldConverterBase&) = delete; + FieldConverterBase& operator=(const FieldConverterBase&) = delete; + virtual ~FieldConverterBase() = default; - virtual bool ConvertField(const base::Value& value, StructType* obj) - const = 0; + virtual bool ConvertField(const base::Value& value, + StructType* obj) const = 0; const std::string& field_path() const { return field_path_; } private: std::string field_path_; - DISALLOW_COPY_AND_ASSIGN(FieldConverterBase); }; template <typename FieldType> @@ -116,21 +119,22 @@ class FieldConverter : public FieldConverterBase<StructType> { public: explicit FieldConverter(const std::string& path, - FieldType StructType::* field, + FieldType StructType::*field, ValueConverter<FieldType>* converter) : FieldConverterBase<StructType>(path), field_pointer_(field), - value_converter_(converter) { - } + value_converter_(converter) {} + + FieldConverter(const FieldConverter&) = delete; + FieldConverter& operator=(const FieldConverter&) = delete; bool ConvertField(const base::Value& value, StructType* dst) const override { return value_converter_->Convert(value, &(dst->*field_pointer_)); } private: - FieldType StructType::* field_pointer_; + FieldType StructType::*field_pointer_; std::unique_ptr<ValueConverter<FieldType>> value_converter_; - DISALLOW_COPY_AND_ASSIGN(FieldConverter); }; template <typename FieldType> @@ -141,10 +145,10 @@ public: BasicValueConverter() = default; - bool Convert(const base::Value& value, int* field) const override; + BasicValueConverter(const BasicValueConverter&) = delete; + BasicValueConverter& operator=(const BasicValueConverter&) = delete; - private: - DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); + bool Convert(const base::Value& value, int* field) const override; }; template <> @@ -153,22 +157,22 @@ public: BasicValueConverter() = default; - bool Convert(const base::Value& value, std::string* field) const override; + BasicValueConverter(const BasicValueConverter&) = delete; + BasicValueConverter& operator=(const BasicValueConverter&) = delete; - private: - DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); + bool Convert(const base::Value& value, std::string* field) const override; }; template <> -class BASE_EXPORT BasicValueConverter<string16> - : public ValueConverter<string16> { +class BASE_EXPORT BasicValueConverter<std::u16string> + : public ValueConverter<std::u16string> { public: BasicValueConverter() = default; - bool Convert(const base::Value& value, string16* field) const override; + BasicValueConverter(const BasicValueConverter&) = delete; + BasicValueConverter& operator=(const BasicValueConverter&) = delete; - private: - DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); + bool Convert(const base::Value& value, std::u16string* field) const override; }; template <> @@ -176,10 +180,10 @@ public: BasicValueConverter() = default; - bool Convert(const base::Value& value, double* field) const override; + BasicValueConverter(const BasicValueConverter&) = delete; + BasicValueConverter& operator=(const BasicValueConverter&) = delete; - private: - DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); + bool Convert(const base::Value& value, double* field) const override; }; template <> @@ -187,28 +191,29 @@ public: BasicValueConverter() = default; - bool Convert(const base::Value& value, bool* field) const override; + BasicValueConverter(const BasicValueConverter&) = delete; + BasicValueConverter& operator=(const BasicValueConverter&) = delete; - private: - DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); + bool Convert(const base::Value& value, bool* field) const override; }; template <typename FieldType> class ValueFieldConverter : public ValueConverter<FieldType> { public: - typedef bool(*ConvertFunc)(const base::Value* value, FieldType* field); + typedef bool (*ConvertFunc)(const base::Value* value, FieldType* field); explicit ValueFieldConverter(ConvertFunc convert_func) : convert_func_(convert_func) {} + ValueFieldConverter(const ValueFieldConverter&) = delete; + ValueFieldConverter& operator=(const ValueFieldConverter&) = delete; + bool Convert(const base::Value& value, FieldType* field) const override { return convert_func_(&value, field); } private: ConvertFunc convert_func_; - - DISALLOW_COPY_AND_ASSIGN(ValueFieldConverter); }; template <typename FieldType> @@ -219,16 +224,15 @@ explicit CustomFieldConverter(ConvertFunc convert_func) : convert_func_(convert_func) {} + CustomFieldConverter(const CustomFieldConverter&) = delete; + CustomFieldConverter& operator=(const CustomFieldConverter&) = delete; + bool Convert(const base::Value& value, FieldType* field) const override { - std::string string_value; - return value.GetAsString(&string_value) && - convert_func_(string_value, field); + return value.is_string() && convert_func_(value.GetString(), field); } private: ConvertFunc convert_func_; - - DISALLOW_COPY_AND_ASSIGN(CustomFieldConverter); }; template <typename NestedType> @@ -236,13 +240,15 @@ public: NestedValueConverter() = default; + NestedValueConverter(const NestedValueConverter&) = delete; + NestedValueConverter& operator=(const NestedValueConverter&) = delete; + bool Convert(const base::Value& value, NestedType* field) const override { return converter_.Convert(value, field); } private: JSONValueConverter<NestedType> converter_; - DISALLOW_COPY_AND_ASSIGN(NestedValueConverter); }; template <typename Element> @@ -251,34 +257,34 @@ public: RepeatedValueConverter() = default; + RepeatedValueConverter(const RepeatedValueConverter&) = delete; + RepeatedValueConverter& operator=(const RepeatedValueConverter&) = delete; + bool Convert(const base::Value& value, std::vector<std::unique_ptr<Element>>* field) const override { - const base::ListValue* list = NULL; - if (!value.GetAsList(&list)) { + const Value::List* list = value.GetIfList(); + if (!list) { // The field is not a list. return false; } - field->reserve(list->GetSize()); - for (size_t i = 0; i < list->GetSize(); ++i) { - const base::Value* element = NULL; - if (!list->Get(i, &element)) - continue; - - std::unique_ptr<Element> e(new Element); - if (basic_converter_.Convert(*element, e.get())) { + field->reserve(list->size()); + size_t i = 0; + for (const Value& element : *list) { + auto e = std::make_unique<Element>(); + if (basic_converter_.Convert(element, e.get())) { field->push_back(std::move(e)); } else { DVLOG(1) << "failure at " << i << "-th element"; return false; } + i++; } return true; } private: BasicValueConverter<Element> basic_converter_; - DISALLOW_COPY_AND_ASSIGN(RepeatedValueConverter); }; template <typename NestedType> @@ -287,119 +293,120 @@ public: RepeatedMessageConverter() = default; + RepeatedMessageConverter(const RepeatedMessageConverter&) = delete; + RepeatedMessageConverter& operator=(const RepeatedMessageConverter&) = delete; + bool Convert(const base::Value& value, std::vector<std::unique_ptr<NestedType>>* field) const override { - const base::ListValue* list = NULL; - if (!value.GetAsList(&list)) + const Value::List* list = value.GetIfList(); + if (!list) return false; - field->reserve(list->GetSize()); - for (size_t i = 0; i < list->GetSize(); ++i) { - const base::Value* element = NULL; - if (!list->Get(i, &element)) - continue; - - std::unique_ptr<NestedType> nested(new NestedType); - if (converter_.Convert(*element, nested.get())) { + field->reserve(list->size()); + size_t i = 0; + for (const Value& element : *list) { + auto nested = std::make_unique<NestedType>(); + if (converter_.Convert(element, nested.get())) { field->push_back(std::move(nested)); } else { DVLOG(1) << "failure at " << i << "-th element"; return false; } + i++; } return true; } private: JSONValueConverter<NestedType> converter_; - DISALLOW_COPY_AND_ASSIGN(RepeatedMessageConverter); }; template <typename NestedType> class RepeatedCustomValueConverter : public ValueConverter<std::vector<std::unique_ptr<NestedType>>> { public: - typedef bool(*ConvertFunc)(const base::Value* value, NestedType* field); + typedef bool (*ConvertFunc)(const base::Value* value, NestedType* field); explicit RepeatedCustomValueConverter(ConvertFunc convert_func) : convert_func_(convert_func) {} + RepeatedCustomValueConverter(const RepeatedCustomValueConverter&) = delete; + RepeatedCustomValueConverter& operator=(const RepeatedCustomValueConverter&) = + delete; + bool Convert(const base::Value& value, std::vector<std::unique_ptr<NestedType>>* field) const override { - const base::ListValue* list = NULL; - if (!value.GetAsList(&list)) + const Value::List* list = value.GetIfList(); + if (!list) return false; - field->reserve(list->GetSize()); - for (size_t i = 0; i < list->GetSize(); ++i) { - const base::Value* element = NULL; - if (!list->Get(i, &element)) - continue; - - std::unique_ptr<NestedType> nested(new NestedType); - if ((*convert_func_)(element, nested.get())) { + field->reserve(list->size()); + size_t i = 0; + for (const Value& element : *list) { + auto nested = std::make_unique<NestedType>(); + if ((*convert_func_)(&element, nested.get())) { field->push_back(std::move(nested)); } else { DVLOG(1) << "failure at " << i << "-th element"; return false; } + i++; } return true; } private: ConvertFunc convert_func_; - DISALLOW_COPY_AND_ASSIGN(RepeatedCustomValueConverter); }; - } // namespace internal template <class StructType> class JSONValueConverter { public: - JSONValueConverter() { - StructType::RegisterJSONConverter(this); - } + JSONValueConverter() { StructType::RegisterJSONConverter(this); } - void RegisterIntField(const std::string& field_name, - int StructType::* field) { + JSONValueConverter(const JSONValueConverter&) = delete; + JSONValueConverter& operator=(const JSONValueConverter&) = delete; + + void RegisterIntField(const std::string& field_name, int StructType::*field) { fields_.push_back( std::make_unique<internal::FieldConverter<StructType, int>>( field_name, field, new internal::BasicValueConverter<int>)); } void RegisterStringField(const std::string& field_name, - std::string StructType::* field) { + std::string StructType::*field) { fields_.push_back( std::make_unique<internal::FieldConverter<StructType, std::string>>( field_name, field, new internal::BasicValueConverter<std::string>)); } void RegisterStringField(const std::string& field_name, - string16 StructType::* field) { + std::u16string StructType::*field) { fields_.push_back( - std::make_unique<internal::FieldConverter<StructType, string16>>( - field_name, field, new internal::BasicValueConverter<string16>)); + std::make_unique<internal::FieldConverter<StructType, std::u16string>>( + field_name, field, + new internal::BasicValueConverter<std::u16string>)); } void RegisterBoolField(const std::string& field_name, - bool StructType::* field) { + bool StructType::*field) { fields_.push_back( std::make_unique<internal::FieldConverter<StructType, bool>>( field_name, field, new internal::BasicValueConverter<bool>)); } void RegisterDoubleField(const std::string& field_name, - double StructType::* field) { + double StructType::*field) { fields_.push_back( std::make_unique<internal::FieldConverter<StructType, double>>( field_name, field, new internal::BasicValueConverter<double>)); } template <class NestedType> - void RegisterNestedField( - const std::string& field_name, NestedType StructType::* field) { + void RegisterNestedField(const std::string& field_name, + NestedType StructType::*field) { fields_.push_back( std::make_unique<internal::FieldConverter<StructType, NestedType>>( field_name, field, new internal::NestedValueConverter<NestedType>)); @@ -416,10 +423,10 @@ } template <typename FieldType> - void RegisterCustomValueField( - const std::string& field_name, - FieldType StructType::* field, - bool (*convert_func)(const base::Value*, FieldType*)) { + void RegisterCustomValueField(const std::string& field_name, + FieldType StructType::*field, + bool (*convert_func)(const base::Value*, + FieldType*)) { fields_.push_back( std::make_unique<internal::FieldConverter<StructType, FieldType>>( field_name, field, @@ -446,10 +453,12 @@ void RegisterRepeatedString( const std::string& field_name, - std::vector<std::unique_ptr<string16>> StructType::*field) { - fields_.push_back(std::make_unique<internal::FieldConverter< - StructType, std::vector<std::unique_ptr<string16>>>>( - field_name, field, new internal::RepeatedValueConverter<string16>)); + std::vector<std::unique_ptr<std::u16string>> StructType::*field) { + fields_.push_back( + std::make_unique<internal::FieldConverter< + StructType, std::vector<std::unique_ptr<std::u16string>>>>( + field_name, field, + new internal::RepeatedValueConverter<std::u16string>)); } void RegisterRepeatedDouble( @@ -493,15 +502,16 @@ } bool Convert(const base::Value& value, StructType* output) const { - const DictionaryValue* dictionary_value = NULL; - if (!value.GetAsDictionary(&dictionary_value)) + const Value::Dict* dict = value.GetIfDict(); + if (!dict) return false; for (size_t i = 0; i < fields_.size(); ++i) { const internal::FieldConverterBase<StructType>* field_converter = fields_[i].get(); - const base::Value* field = NULL; - if (dictionary_value->Get(field_converter->field_path(), &field)) { + const base::Value* field = + dict->FindByDottedPath(field_converter->field_path()); + if (field) { if (!field_converter->ConvertField(*field, output)) { DVLOG(1) << "failure at field " << field_converter->field_path(); return false; @@ -514,8 +524,6 @@ private: std::vector<std::unique_ptr<internal::FieldConverterBase<StructType>>> fields_; - - DISALLOW_COPY_AND_ASSIGN(JSONValueConverter); }; } // namespace base
diff --git a/base/json/json_value_converter_unittest.cc b/base/json/json_value_converter_unittest.cc index 0946820..f385486 100644 --- a/base/json/json_value_converter_unittest.cc +++ b/base/json/json_value_converter_unittest.cc
@@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,6 +12,7 @@ #include "base/strings/string_piece.h" #include "base/values.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace base { namespace { @@ -19,7 +20,8 @@ // Very simple messages. struct SimpleMessage { enum SimpleEnum { - FOO, BAR, + FOO, + BAR, }; int foo; std::string bar; @@ -48,13 +50,14 @@ } static bool GetValueString(const base::Value* value, std::string* result) { - const base::DictionaryValue* dict = nullptr; - if (!value->GetAsDictionary(&dict)) + const Value::Dict* dict = value->GetIfDict(); + if (!dict) return false; - - if (!dict->GetString("val", result)) + const std::string* str = dict->FindString("val"); + if (!str) return false; - + if (result) + *result = *str; return true; } @@ -66,13 +69,10 @@ converter->RegisterCustomField<SimpleEnum>( "simple_enum", &SimpleMessage::simple_enum, &ParseSimpleEnum); converter->RegisterRepeatedInt("ints", &SimpleMessage::ints); - converter->RegisterCustomValueField<bool>("bstruct", - &SimpleMessage::bstruct, - &HasFieldPresent); + converter->RegisterCustomValueField<bool>( + "bstruct", &SimpleMessage::bstruct, &HasFieldPresent); converter->RegisterRepeatedCustomValue<std::string>( - "string_values", - &SimpleMessage::string_values, - &GetValueString); + "string_values", &SimpleMessage::string_values, &GetValueString); } }; @@ -106,10 +106,11 @@ " \"ints\": [1, 2]" "}\n"; - std::unique_ptr<Value> value = base::JSONReader::Read(normal_data); + absl::optional<Value> value = base::JSONReader::Read(normal_data); + ASSERT_TRUE(value); SimpleMessage message; base::JSONValueConverter<SimpleMessage> converter; - EXPECT_TRUE(converter.Convert(*value.get(), &message)); + EXPECT_TRUE(converter.Convert(*value, &message)); EXPECT_EQ(1, message.foo); EXPECT_EQ("bar", message.bar); @@ -148,10 +149,11 @@ " }]\n" "}\n"; - std::unique_ptr<Value> value = base::JSONReader::Read(normal_data); + absl::optional<Value> value = base::JSONReader::Read(normal_data); + ASSERT_TRUE(value); NestedMessage message; base::JSONValueConverter<NestedMessage> converter; - EXPECT_TRUE(converter.Convert(*value.get(), &message)); + EXPECT_TRUE(converter.Convert(*value, &message)); EXPECT_EQ(1.0, message.foo); EXPECT_EQ(1, message.child.foo); @@ -185,15 +187,16 @@ const char normal_data[] = "{\n" " \"foo\": 1,\n" - " \"bar\": 2,\n" // "bar" is an integer here. + " \"bar\": 2,\n" // "bar" is an integer here. " \"baz\": true,\n" " \"ints\": [1, 2]" "}\n"; - std::unique_ptr<Value> value = base::JSONReader::Read(normal_data); + absl::optional<Value> value = base::JSONReader::Read(normal_data); + ASSERT_TRUE(value); SimpleMessage message; base::JSONValueConverter<SimpleMessage> converter; - EXPECT_FALSE(converter.Convert(*value.get(), &message)); + EXPECT_FALSE(converter.Convert(*value, &message)); // Do not check the values below. |message| may be modified during // Convert() even it fails. } @@ -206,11 +209,12 @@ " \"ints\": [1, 2]" "}\n"; - std::unique_ptr<Value> value = base::JSONReader::Read(normal_data); + absl::optional<Value> value = base::JSONReader::Read(normal_data); + ASSERT_TRUE(value); SimpleMessage message; base::JSONValueConverter<SimpleMessage> converter; // Convert() still succeeds even if the input doesn't have "bar" field. - EXPECT_TRUE(converter.Convert(*value.get(), &message)); + EXPECT_TRUE(converter.Convert(*value, &message)); EXPECT_EQ(1, message.foo); EXPECT_TRUE(message.baz); @@ -229,10 +233,11 @@ " \"ints\": [1, 2]" "}\n"; - std::unique_ptr<Value> value = base::JSONReader::Read(normal_data); + absl::optional<Value> value = base::JSONReader::Read(normal_data); + ASSERT_TRUE(value); SimpleMessage message; base::JSONValueConverter<SimpleMessage> converter; - EXPECT_FALSE(converter.Convert(*value.get(), &message)); + EXPECT_FALSE(converter.Convert(*value, &message)); // No check the values as mentioned above. } @@ -246,10 +251,11 @@ " \"ints\": [1, false]" "}\n"; - std::unique_ptr<Value> value = base::JSONReader::Read(normal_data); + absl::optional<Value> value = base::JSONReader::Read(normal_data); + ASSERT_TRUE(value); SimpleMessage message; base::JSONValueConverter<SimpleMessage> converter; - EXPECT_FALSE(converter.Convert(*value.get(), &message)); + EXPECT_FALSE(converter.Convert(*value, &message)); // No check the values as mentioned above. }
diff --git a/base/json/json_value_serializer_unittest.cc b/base/json/json_value_serializer_unittest.cc index b12e5e7..16bf99e 100644 --- a/base/json/json_value_serializer_unittest.cc +++ b/base/json/json_value_serializer_unittest.cc
@@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -17,8 +17,8 @@ #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "build/build_config.h" -#include "starboard/common/string.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace base { @@ -70,21 +70,21 @@ str_serializer.set_pretty_print(true); ASSERT_TRUE(str_serializer.Serialize(value)); // Unify line endings between platforms. - ReplaceSubstringsAfterOffset(&serialized_json, 0, - kWinLineEnds, kLinuxLineEnds); + ReplaceSubstringsAfterOffset(&serialized_json, 0, kWinLineEnds, + kLinuxLineEnds); // Now compare the input with the output. ASSERT_EQ(kProperJSON, serialized_json); } void ValidateJsonList(const std::string& json) { - std::unique_ptr<ListValue> list = ListValue::From(JSONReader::Read(json)); + absl::optional<Value> value = JSONReader::Read(json); + ASSERT_TRUE(value); + Value::List* list = value->GetIfList(); ASSERT_TRUE(list); - ASSERT_EQ(1U, list->GetSize()); - Value* elt = nullptr; - ASSERT_TRUE(list->Get(0, &elt)); - int value = 0; - ASSERT_TRUE(elt && elt->GetAsInteger(&value)); - ASSERT_EQ(1, value); + ASSERT_EQ(1U, list->size()); + const Value& elt = (*list)[0]; + ASSERT_TRUE(elt.is_int()); + ASSERT_EQ(1, elt.GetInt()); } // Test proper JSON deserialization from string is working. @@ -135,12 +135,15 @@ ASSERT_FALSE(value); ASSERT_NE(0, error_code); ASSERT_FALSE(error_message.empty()); - // Repeat with commas allowed. + // Repeat with commas allowed. The Deserialize call shouldn't change the + // value of error_code. To test that, we first set it to a nonsense value + // (-789) and ASSERT_EQ that it remains that nonsense value. + error_code = -789; JSONStringValueDeserializer str_deserializer2(kProperJSONWithCommas, JSON_ALLOW_TRAILING_COMMAS); value = str_deserializer2.Deserialize(&error_code, &error_message); ASSERT_TRUE(value); - ASSERT_EQ(JSONReader::JSON_TRAILING_COMMA, error_code); + ASSERT_EQ(-789, error_code); // Verify if the same JSON is still there. CheckJSONIsStillTheSame(*value); } @@ -151,8 +154,7 @@ ASSERT_TRUE(tempdir.CreateUniqueTempDir()); // Write it down in the file. FilePath temp_file(tempdir.GetPath().AppendASCII("test.json")); - ASSERT_EQ(static_cast<int>(strlen(kProperJSON)), - WriteFile(temp_file, kProperJSON, strlen(kProperJSON))); + ASSERT_TRUE(WriteFile(temp_file, kProperJSON)); // Try to deserialize it through the serializer. JSONFileValueDeserializer file_deserializer(temp_file); @@ -175,9 +177,7 @@ ASSERT_TRUE(tempdir.CreateUniqueTempDir()); // Write it down in the file. FilePath temp_file(tempdir.GetPath().AppendASCII("test.json")); - ASSERT_EQ(static_cast<int>(strlen(kProperJSONWithCommas)), - WriteFile(temp_file, kProperJSONWithCommas, - strlen(kProperJSONWithCommas))); + ASSERT_TRUE(WriteFile(temp_file, kProperJSONWithCommas)); // Try to deserialize it through the serializer. JSONFileValueDeserializer file_deserializer(temp_file); @@ -189,12 +189,15 @@ ASSERT_FALSE(value); ASSERT_NE(0, error_code); ASSERT_FALSE(error_message.empty()); - // Repeat with commas allowed. + // Repeat with commas allowed. The Deserialize call shouldn't change the + // value of error_code. To test that, we first set it to a nonsense value + // (-789) and ASSERT_EQ that it remains that nonsense value. + error_code = -789; JSONFileValueDeserializer file_deserializer2(temp_file, JSON_ALLOW_TRAILING_COMMAS); value = file_deserializer2.Deserialize(&error_code, &error_message); ASSERT_TRUE(value); - ASSERT_EQ(JSONReader::JSON_TRAILING_COMMA, error_code); + ASSERT_EQ(-789, error_code); // Verify if the same JSON is still there. CheckJSONIsStillTheSame(*value); } @@ -211,33 +214,25 @@ std::unique_ptr<Value> root_expected; root_expected = deserializer_expected.Deserialize(nullptr, nullptr); ASSERT_TRUE(root_expected); - ASSERT_TRUE(root->Equals(root_expected.get())); + ASSERT_EQ(*root, *root_expected); } TEST(JSONValueSerializerTest, Roundtrip) { static const char kOriginalSerialization[] = - "{\"bool\":true,\"double\":3.14,\"int\":42,\"list\":[1,2],\"null\":null}"; + "{\"bool\":true,\"double\":3.14,\"int\":42,\"list\":[1,2],\"null\":null}"; JSONStringValueDeserializer deserializer(kOriginalSerialization); - std::unique_ptr<DictionaryValue> root_dict = - DictionaryValue::From(deserializer.Deserialize(nullptr, nullptr)); + std::unique_ptr<Value> root = deserializer.Deserialize(nullptr, nullptr); + ASSERT_TRUE(root); + const Value::Dict* root_dict = root->GetIfDict(); ASSERT_TRUE(root_dict); - Value* null_value = nullptr; - ASSERT_TRUE(root_dict->Get("null", &null_value)); + const Value* null_value = root_dict->Find("null"); ASSERT_TRUE(null_value); ASSERT_TRUE(null_value->is_none()); - bool bool_value = false; - ASSERT_TRUE(root_dict->GetBoolean("bool", &bool_value)); - ASSERT_TRUE(bool_value); - - int int_value = 0; - ASSERT_TRUE(root_dict->GetInteger("int", &int_value)); - ASSERT_EQ(42, int_value); - - double double_value = 0.0; - ASSERT_TRUE(root_dict->GetDouble("double", &double_value)); - ASSERT_DOUBLE_EQ(3.14, double_value); + ASSERT_TRUE(root_dict->FindBool("bool").value()); + ASSERT_EQ(42, root_dict->FindInt("int").value()); + ASSERT_DOUBLE_EQ(3.14, root_dict->FindDouble("double").value()); std::string test_serialization; JSONStringValueSerializer mutable_serializer(&test_serialization); @@ -248,27 +243,24 @@ ASSERT_TRUE(mutable_serializer.Serialize(*root_dict)); // JSON output uses a different newline style on Windows than on other // platforms. -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) #define JSON_NEWLINE "\r\n" #else #define JSON_NEWLINE "\n" #endif const std::string pretty_serialization = - "{" JSON_NEWLINE - " \"bool\": true," JSON_NEWLINE - " \"double\": 3.14," JSON_NEWLINE - " \"int\": 42," JSON_NEWLINE - " \"list\": [ 1, 2 ]," JSON_NEWLINE - " \"null\": null" JSON_NEWLINE - "}" JSON_NEWLINE; + "{" JSON_NEWLINE " \"bool\": true," JSON_NEWLINE + " \"double\": 3.14," JSON_NEWLINE " \"int\": 42," JSON_NEWLINE + " \"list\": [ 1, 2 ]," JSON_NEWLINE " \"null\": null" JSON_NEWLINE + "}" JSON_NEWLINE; #undef JSON_NEWLINE ASSERT_EQ(pretty_serialization, test_serialization); } TEST(JSONValueSerializerTest, StringEscape) { - string16 all_chars; + std::u16string all_chars; for (int i = 1; i < 256; ++i) { - all_chars += static_cast<char16>(i); + all_chars += static_cast<char16_t>(i); } // Generated in in Firefox using the following js (with an extra backslash for // double quote): @@ -296,12 +288,12 @@ "\xC3\xB1\xC3\xB2\xC3\xB3\xC3\xB4\xC3\xB5\xC3\xB6\xC3\xB7\xC3\xB8\xC3\xB9" "\xC3\xBA\xC3\xBB\xC3\xBC\xC3\xBD\xC3\xBE\xC3\xBF"; - std::string expected_output = "{\"all_chars\":\"" + all_chars_expected + - "\"}"; + std::string expected_output = + "{\"all_chars\":\"" + all_chars_expected + "\"}"; // Test JSONWriter interface std::string output_js; - DictionaryValue valueRoot; - valueRoot.SetString("all_chars", all_chars); + Value::Dict valueRoot; + valueRoot.Set("all_chars", all_chars); JSONWriter::Write(valueRoot, &output_js); ASSERT_EQ(expected_output, output_js); @@ -313,9 +305,9 @@ TEST(JSONValueSerializerTest, UnicodeStrings) { // unicode string json -> escaped ascii text - DictionaryValue root; - string16 test(WideToUTF16(L"\x7F51\x9875")); - root.SetString("web", test); + Value::Dict root; + std::u16string test(u"\x7F51\x9875"); + root.Set("web", test); static const char kExpected[] = "{\"web\":\"\xE7\xBD\x91\xE9\xA1\xB5\"}"; @@ -329,18 +321,17 @@ std::unique_ptr<Value> deserial_root = deserializer.Deserialize(nullptr, nullptr); ASSERT_TRUE(deserial_root); - DictionaryValue* dict_root = - static_cast<DictionaryValue*>(deserial_root.get()); - string16 web_value; - ASSERT_TRUE(dict_root->GetString("web", &web_value)); - ASSERT_EQ(test, web_value); + const Value::Dict* deserial_root_dict = deserial_root->GetIfDict(); + const std::string* web_value = deserial_root_dict->FindString("web"); + ASSERT_TRUE(web_value); + ASSERT_EQ("\xE7\xBD\x91\xE9\xA1\xB5", *web_value); } TEST(JSONValueSerializerTest, HexStrings) { // hex string json -> escaped ascii text - DictionaryValue root; - string16 test(WideToUTF16(L"\x01\x02")); - root.SetString("test", test); + Value::Dict root; + std::u16string test(u"\x01\x02"); + root.Set("test", test); static const char kExpected[] = "{\"test\":\"\\u0001\\u0002\"}"; @@ -354,20 +345,20 @@ std::unique_ptr<Value> deserial_root = deserializer.Deserialize(nullptr, nullptr); ASSERT_TRUE(deserial_root); - DictionaryValue* dict_root = - static_cast<DictionaryValue*>(deserial_root.get()); - string16 test_value; - ASSERT_TRUE(dict_root->GetString("test", &test_value)); - ASSERT_EQ(test, test_value); + Value::Dict* deserial_root_dict = deserial_root->GetIfDict(); + const std::string* test_value = deserial_root_dict->FindString("test"); + ASSERT_TRUE(test_value); + ASSERT_EQ("\u0001\u0002", *test_value); // Test converting escaped regular chars static const char kEscapedChars[] = "{\"test\":\"\\u0067\\u006f\"}"; JSONStringValueDeserializer deserializer2(kEscapedChars); deserial_root = deserializer2.Deserialize(nullptr, nullptr); ASSERT_TRUE(deserial_root); - dict_root = static_cast<DictionaryValue*>(deserial_root.get()); - ASSERT_TRUE(dict_root->GetString("test", &test_value)); - ASSERT_EQ(ASCIIToUTF16("go"), test_value); + deserial_root_dict = deserial_root->GetIfDict(); + test_value = deserial_root_dict->FindString("test"); + ASSERT_TRUE(test_value); + ASSERT_EQ("go", *test_value); } TEST(JSONValueSerializerTest, JSONReaderComments) { @@ -379,15 +370,14 @@ ValidateJsonList("[ 1 //// ,2\r\n ]"); // It's ok to have a comment in a string. - std::unique_ptr<ListValue> list = - ListValue::From(JSONReader::Read("[\"// ok\\n /* foo */ \"]")); + absl::optional<Value> value = JSONReader::Read("[\"// ok\\n /* foo */ \"]"); + ASSERT_TRUE(value); + Value::List* list = value->GetIfList(); ASSERT_TRUE(list); - ASSERT_EQ(1U, list->GetSize()); - Value* elt = nullptr; - ASSERT_TRUE(list->Get(0, &elt)); - std::string value; - ASSERT_TRUE(elt && elt->GetAsString(&value)); - ASSERT_EQ("// ok\n /* foo */ ", value); + ASSERT_EQ(1U, list->size()); + const Value& elt = (*list)[0]; + ASSERT_TRUE(elt.is_string()); + ASSERT_EQ("// ok\n /* foo */ ", elt.GetString()); // You can't nest comments. ASSERT_FALSE(JSONReader::Read("/* /* inner */ outer */ [ 1 ]")); @@ -416,26 +406,21 @@ ASSERT_TRUE(PathExists(original_file_path)); JSONFileValueDeserializer deserializer(original_file_path); - std::unique_ptr<DictionaryValue> root_dict = - DictionaryValue::From(deserializer.Deserialize(nullptr, nullptr)); + std::unique_ptr<Value> root = deserializer.Deserialize(nullptr, nullptr); + ASSERT_TRUE(root); + const Value::Dict* root_dict = root->GetIfDict(); ASSERT_TRUE(root_dict); - Value* null_value = nullptr; - ASSERT_TRUE(root_dict->Get("null", &null_value)); + const Value* null_value = root_dict->Find("null"); ASSERT_TRUE(null_value); ASSERT_TRUE(null_value->is_none()); - bool bool_value = false; - ASSERT_TRUE(root_dict->GetBoolean("bool", &bool_value)); - ASSERT_TRUE(bool_value); + ASSERT_TRUE(root_dict->FindBool("bool").value()); + ASSERT_EQ(42, root_dict->FindInt("int").value()); - int int_value = 0; - ASSERT_TRUE(root_dict->GetInteger("int", &int_value)); - ASSERT_EQ(42, int_value); - - std::string string_value; - ASSERT_TRUE(root_dict->GetString("string", &string_value)); - ASSERT_EQ("hello", string_value); + const std::string* string_value = root_dict->FindString("string"); + ASSERT_TRUE(string_value); + ASSERT_EQ("hello", *string_value); // Now try writing. const FilePath written_file_path = @@ -450,7 +435,7 @@ #if !defined(STARBOARD) EXPECT_TRUE(TextContentsEqual(original_file_path, written_file_path)); #endif // !defined(STARBOARD) - EXPECT_TRUE(DeleteFile(written_file_path, false)); + EXPECT_TRUE(DeleteFile(written_file_path)); } TEST_F(JSONFileValueSerializerTest, RoundtripNested) { @@ -483,7 +468,7 @@ #if !defined(STARBOARD) EXPECT_TRUE(TextContentsEqual(original_file_path, written_file_path)); #endif // !defined(STARBOARD) - EXPECT_TRUE(DeleteFile(written_file_path, false)); + EXPECT_TRUE(DeleteFile(written_file_path)); } TEST_F(JSONFileValueSerializerTest, NoWhitespace) {
diff --git a/base/json/json_writer.cc b/base/json/json_writer.cc index 237be4f..9195e64 100644 --- a/base/json/json_writer.cc +++ b/base/json/json_writer.cc
@@ -1,208 +1,237 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/json/json_writer.h" +#include <stdint.h> + #include <cmath> #include <limits> #include "base/json/string_escape.h" #include "base/logging.h" +#include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" -#include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "build/build_config.h" -#include "starboard/types.h" namespace base { -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) const char kPrettyPrintLineEnding[] = "\r\n"; #else const char kPrettyPrintLineEnding[] = "\n"; #endif // static -bool JSONWriter::Write(const Value& node, std::string* json) { - return WriteWithOptions(node, 0, json); +bool JSONWriter::Write(ValueView node, std::string* json, size_t max_depth) { + return WriteWithOptions(node, 0, json, max_depth); } // static -bool JSONWriter::WriteWithOptions(const Value& node, +bool JSONWriter::WriteWithOptions(ValueView node, int options, - std::string* json) { + std::string* json, + size_t max_depth) { json->clear(); // Is there a better way to estimate the size of the output? - json->reserve(1024); + if (json->capacity() < 1024) { + json->reserve(1024); + } - JSONWriter writer(options, json); - bool result = writer.BuildJSONString(node, 0U); + JSONWriter writer(options, json, max_depth); + bool result = node.Visit([&writer](const auto& member) { + return writer.BuildJSONString(member, 0); + }); - if (options & OPTIONS_PRETTY_PRINT) + if (options & OPTIONS_PRETTY_PRINT) { json->append(kPrettyPrintLineEnding); + } return result; } -JSONWriter::JSONWriter(int options, std::string* json) +JSONWriter::JSONWriter(int options, std::string* json, size_t max_depth) : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0), omit_double_type_preservation_( (options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION) != 0), pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0), - json_string_(json) { + json_string_(json), + max_depth_(max_depth), + stack_depth_(0) { DCHECK(json); + CHECK_LE(max_depth, internal::kAbsoluteMaxDepth); } -bool JSONWriter::BuildJSONString(const Value& node, size_t depth) { - switch (node.type()) { - case Value::Type::NONE: { - json_string_->append("null"); - return true; +bool JSONWriter::BuildJSONString(absl::monostate node, size_t depth) { + json_string_->append("null"); + return true; +} + +bool JSONWriter::BuildJSONString(bool node, size_t depth) { + json_string_->append(node ? "true" : "false"); + return true; +} + +bool JSONWriter::BuildJSONString(int node, size_t depth) { + json_string_->append(NumberToString(node)); + return true; +} + +bool JSONWriter::BuildJSONString(double node, size_t depth) { + if (omit_double_type_preservation_ && + IsValueInRangeForNumericType<int64_t>(node) && std::floor(node) == node) { + json_string_->append(NumberToString(static_cast<int64_t>(node))); + return true; + } + + std::string real = NumberToString(node); + // Ensure that the number has a .0 if there's no decimal or 'e'. This + // makes sure that when we read the JSON back, it's interpreted as a + // real rather than an int. + if (real.find_first_of(".eE") == std::string::npos) { + real.append(".0"); + } + + // The JSON spec requires that non-integer values in the range (-1,1) + // have a zero before the decimal point - ".52" is not valid, "0.52" is. + if (real[0] == '.') { + real.insert(0, 1, '0'); + } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') { + // "-.1" bad "-0.1" good + real.insert(1, 1, '0'); + } + json_string_->append(real); + return true; +} + +bool JSONWriter::BuildJSONString(StringPiece node, size_t depth) { + EscapeJSONString(node, true, json_string_); + return true; +} + +bool JSONWriter::BuildJSONString(const Value::BlobStorage& node, size_t depth) { + // Successful only if we're allowed to omit it. + DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value."; + return omit_binary_values_; +} + +bool JSONWriter::BuildJSONString(const Value::Dict& node, size_t depth) { + internal::StackMarker depth_check(max_depth_, &stack_depth_); + + if (depth_check.IsTooDeep()) { + return false; + } + + json_string_->push_back('{'); + if (pretty_print_) { + json_string_->append(kPrettyPrintLineEnding); + } + + bool first_value_has_been_output = false; + bool result = true; + for (const auto [key, value] : node) { + if (omit_binary_values_ && value.type() == Value::Type::BINARY) { + continue; } - case Value::Type::BOOLEAN: { - bool value; - bool result = node.GetAsBoolean(&value); - DCHECK(result); - json_string_->append(value ? "true" : "false"); - return result; - } - - case Value::Type::INTEGER: { - int value; - bool result = node.GetAsInteger(&value); - DCHECK(result); - json_string_->append(IntToString(value)); - return result; - } - - case Value::Type::DOUBLE: { - double value; - bool result = node.GetAsDouble(&value); - DCHECK(result); - if (omit_double_type_preservation_ && - value <= std::numeric_limits<int64_t>::max() && - value >= std::numeric_limits<int64_t>::min() && - std::floor(value) == value) { - json_string_->append(Int64ToString(static_cast<int64_t>(value))); - return result; - } - std::string real = NumberToString(value); - // Ensure that the number has a .0 if there's no decimal or 'e'. This - // makes sure that when we read the JSON back, it's interpreted as a - // real rather than an int. - if (real.find('.') == std::string::npos && - real.find('e') == std::string::npos && - real.find('E') == std::string::npos) { - real.append(".0"); - } - // The JSON spec requires that non-integer values in the range (-1,1) - // have a zero before the decimal point - ".52" is not valid, "0.52" is. - if (real[0] == '.') { - real.insert(static_cast<size_t>(0), static_cast<size_t>(1), '0'); - } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') { - // "-.1" bad "-0.1" good - real.insert(static_cast<size_t>(1), static_cast<size_t>(1), '0'); - } - json_string_->append(real); - return result; - } - - case Value::Type::STRING: { - std::string value; - bool result = node.GetAsString(&value); - DCHECK(result); - EscapeJSONString(value, true, json_string_); - return result; - } - - case Value::Type::LIST: { - json_string_->push_back('['); - if (pretty_print_) - json_string_->push_back(' '); - - const ListValue* list = nullptr; - bool first_value_has_been_output = false; - bool result = node.GetAsList(&list); - DCHECK(result); - for (const auto& value : *list) { - if (omit_binary_values_ && value.type() == Value::Type::BINARY) - continue; - - if (first_value_has_been_output) { - json_string_->push_back(','); - if (pretty_print_) - json_string_->push_back(' '); - } - - if (!BuildJSONString(value, depth)) - result = false; - - first_value_has_been_output = true; - } - - if (pretty_print_) - json_string_->push_back(' '); - json_string_->push_back(']'); - return result; - } - - case Value::Type::DICTIONARY: { - json_string_->push_back('{'); - if (pretty_print_) - json_string_->append(kPrettyPrintLineEnding); - - const DictionaryValue* dict = nullptr; - bool first_value_has_been_output = false; - bool result = node.GetAsDictionary(&dict); - DCHECK(result); - for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd(); - itr.Advance()) { - if (omit_binary_values_ && itr.value().type() == Value::Type::BINARY) { - continue; - } - - if (first_value_has_been_output) { - json_string_->push_back(','); - if (pretty_print_) - json_string_->append(kPrettyPrintLineEnding); - } - - if (pretty_print_) - IndentLine(depth + 1U); - - EscapeJSONString(itr.key(), true, json_string_); - json_string_->push_back(':'); - if (pretty_print_) - json_string_->push_back(' '); - - if (!BuildJSONString(itr.value(), depth + 1U)) - result = false; - - first_value_has_been_output = true; - } - + if (first_value_has_been_output) { + json_string_->push_back(','); if (pretty_print_) { json_string_->append(kPrettyPrintLineEnding); - IndentLine(depth); } - - json_string_->push_back('}'); - return result; } - case Value::Type::BINARY: - // Successful only if we're allowed to omit it. - DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value."; - return omit_binary_values_; + if (pretty_print_) { + IndentLine(depth + 1U); + } + + EscapeJSONString(key, true, json_string_); + json_string_->push_back(':'); + if (pretty_print_) { + json_string_->push_back(' '); + } + + result &= value.Visit([this, depth = depth + 1](const auto& member) { + return BuildJSONString(member, depth); + }); + + first_value_has_been_output = true; } - NOTREACHED(); - return false; + + if (pretty_print_) { + if (first_value_has_been_output) { + json_string_->append(kPrettyPrintLineEnding); + } + IndentLine(depth); + } + + json_string_->push_back('}'); + return result; +} + +bool JSONWriter::BuildJSONString(const Value::List& node, size_t depth) { + internal::StackMarker depth_check(max_depth_, &stack_depth_); + + if (depth_check.IsTooDeep()) { + return false; + } + + json_string_->push_back('['); + if (pretty_print_) { + json_string_->push_back(' '); + } + + bool first_value_has_been_output = false; + bool result = true; + for (const auto& value : node) { + if (omit_binary_values_ && value.type() == Value::Type::BINARY) { + continue; + } + + if (first_value_has_been_output) { + json_string_->push_back(','); + if (pretty_print_) { + json_string_->push_back(' '); + } + } + + result &= value.Visit([this, depth](const auto& member) { + return BuildJSONString(member, depth); + }); + + first_value_has_been_output = true; + } + + if (pretty_print_) { + json_string_->push_back(' '); + } + json_string_->push_back(']'); + return result; } void JSONWriter::IndentLine(size_t depth) { json_string_->append(depth * 3U, ' '); } +absl::optional<std::string> WriteJson(ValueView node, size_t max_depth) { + std::string result; + if (!JSONWriter::Write(node, &result, max_depth)) { + return absl::nullopt; + } + return result; +} + +absl::optional<std::string> WriteJsonWithOptions(ValueView node, + uint32_t options, + size_t max_depth) { + std::string result; + if (!JSONWriter::WriteWithOptions(node, static_cast<int>(options), &result, + max_depth)) { + return absl::nullopt; + } + return result; +} + } // namespace base
diff --git a/base/json/json_writer.h b/base/json/json_writer.h index a260432..ddb943d 100644 --- a/base/json/json_writer.h +++ b/base/json/json_writer.h
@@ -1,60 +1,114 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_JSON_JSON_WRITER_H_ #define BASE_JSON_JSON_WRITER_H_ +#include <stddef.h> +#include <cstdint> #include <string> #include "base/base_export.h" -#include "base/macros.h" -#include "starboard/types.h" +#include "base/json/json_common.h" +#include "base/memory/raw_ptr.h" +#include "base/strings/string_piece.h" +#include "base/values.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace base { -class Value; +enum JsonOptions { + // This option instructs the writer that if a Binary value is encountered, + // the value (and key if within a dictionary) will be omitted from the + // output, and success will be returned. Otherwise, if a binary value is + // encountered, failure will be returned. + OPTIONS_OMIT_BINARY_VALUES = 1 << 0, + + // This option instructs the writer to write doubles that have no fractional + // part as a normal integer (i.e., without using exponential notation + // or appending a '.0') as long as the value is within the range of a + // 64-bit int. + OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION = 1 << 1, + + // Return a slightly nicer formatted json string (pads with whitespace to + // help with readability). + OPTIONS_PRETTY_PRINT = 1 << 2, +}; + +// Given a root node, generates and returns a JSON string. +// +// Returns `absl::nullopt` if +// * the nesting depth exceeds `max_depth`, or +// * the JSON contains binary values. +BASE_EXPORT absl::optional<std::string> WriteJson( + ValueView node, + size_t max_depth = internal::kAbsoluteMaxDepth); + +// Given a root node, generates and returns a JSON string. +// The string is formatted according to `options` which is a bitmask of +// `JsonOptions`. +// +// Returns `absl::nullopt` if +// * the nesting depth exceeds `max_depth,` or +// * the JSON contains binary values +// (unless `JsonOptions::OPTIONS_OMIT_BINARY_VALUES` is passed). +BASE_EXPORT absl::optional<std::string> WriteJsonWithOptions( + ValueView node, + uint32_t options, + size_t max_depth = internal::kAbsoluteMaxDepth); class BASE_EXPORT JSONWriter { public: - enum Options { - // This option instructs the writer that if a Binary value is encountered, - // the value (and key if within a dictionary) will be omitted from the - // output, and success will be returned. Otherwise, if a binary value is - // encountered, failure will be returned. - OPTIONS_OMIT_BINARY_VALUES = 1 << 0, + using Options = JsonOptions; + // TODO: Once we support c++20 we replace these with + // `using enum ::JsonOptions`. + static constexpr auto OPTIONS_OMIT_BINARY_VALUES = + JsonOptions::OPTIONS_OMIT_BINARY_VALUES; + static constexpr auto OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION = + JsonOptions::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION; + static constexpr auto OPTIONS_PRETTY_PRINT = + JsonOptions::OPTIONS_PRETTY_PRINT; - // This option instructs the writer to write doubles that have no fractional - // part as a normal integer (i.e., without using exponential notation - // or appending a '.0') as long as the value is within the range of a - // 64-bit int. - OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION = 1 << 1, - - // Return a slightly nicer formatted json string (pads with whitespace to - // help with readability). - OPTIONS_PRETTY_PRINT = 1 << 2, - }; + JSONWriter(const JSONWriter&) = delete; + JSONWriter& operator=(const JSONWriter&) = delete; // Given a root node, generates a JSON string and puts it into |json|. // The output string is overwritten and not appended. // // TODO(tc): Should we generate json if it would be invalid json (e.g., - // |node| is not a DictionaryValue/ListValue or if there are inf/-inf float + // |node| is not a dictionary/list Value or if there are inf/-inf float // values)? Return true on success and false on failure. - static bool Write(const Value& node, std::string* json); + // + // Deprecated: use the standalone method `WriteJson()` instead. + static bool Write(ValueView node, + std::string* json, + size_t max_depth = internal::kAbsoluteMaxDepth); // Same as above but with |options| which is a bunch of JSONWriter::Options // bitwise ORed together. Return true on success and false on failure. - static bool WriteWithOptions(const Value& node, + // + // Deprecated: use the standalone method `WriteJsonWithOptions()` instead. + static bool WriteWithOptions(ValueView node, int options, - std::string* json); + std::string* json, + size_t max_depth = internal::kAbsoluteMaxDepth); private: - JSONWriter(int options, std::string* json); + JSONWriter(int options, + std::string* json, + size_t max_depth = internal::kAbsoluteMaxDepth); // Called recursively to build the JSON string. When completed, // |json_string_| will contain the JSON. - bool BuildJSONString(const Value& node, size_t depth); + bool BuildJSONString(absl::monostate node, size_t depth); + bool BuildJSONString(bool node, size_t depth); + bool BuildJSONString(int node, size_t depth); + bool BuildJSONString(double node, size_t depth); + bool BuildJSONString(StringPiece node, size_t depth); + bool BuildJSONString(const Value::BlobStorage& node, size_t depth); + bool BuildJSONString(const Value::Dict& node, size_t depth); + bool BuildJSONString(const Value::List& node, size_t depth); // Adds space to json_string_ for the indent level. void IndentLine(size_t depth); @@ -64,9 +118,13 @@ bool pretty_print_; // Where we write JSON data as we generate it. - std::string* json_string_; + raw_ptr<std::string> json_string_; - DISALLOW_COPY_AND_ASSIGN(JSONWriter); + // Maximum depth to write. + const size_t max_depth_; + + // The number of times the writer has recursed (current stack depth). + size_t stack_depth_; }; } // namespace base
diff --git a/base/json/json_writer_unittest.cc b/base/json/json_writer_unittest.cc index 2d81af3..0fc6da9 100644 --- a/base/json/json_writer_unittest.cc +++ b/base/json/json_writer_unittest.cc
@@ -1,154 +1,235 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/json/json_writer.h" +#include "base/json/json_reader.h" -#include "base/memory/ptr_util.h" +#include "base/containers/span.h" +#include "base/strings/stringprintf.h" #include "base/values.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +#if BUILDFLAG(IS_WIN) +#include "base/strings/string_util.h" +#endif namespace base { -TEST(JSONWriterTest, BasicTypes) { - std::string output_js; +namespace { - // Test null. - EXPECT_TRUE(JSONWriter::Write(Value(), &output_js)); - EXPECT_EQ("null", output_js); - - // Test empty dict. - EXPECT_TRUE(JSONWriter::Write(DictionaryValue(), &output_js)); - EXPECT_EQ("{}", output_js); - - // Test empty list. - EXPECT_TRUE(JSONWriter::Write(ListValue(), &output_js)); - EXPECT_EQ("[]", output_js); - - // Test integer values. - EXPECT_TRUE(JSONWriter::Write(Value(42), &output_js)); - EXPECT_EQ("42", output_js); - - // Test boolean values. - EXPECT_TRUE(JSONWriter::Write(Value(true), &output_js)); - EXPECT_EQ("true", output_js); - - // Test Real values should always have a decimal or an 'e'. - EXPECT_TRUE(JSONWriter::Write(Value(1.0), &output_js)); - EXPECT_EQ("1.0", output_js); - - // Test Real values in the the range (-1, 1) must have leading zeros - EXPECT_TRUE(JSONWriter::Write(Value(0.2), &output_js)); - EXPECT_EQ("0.2", output_js); - - // Test Real values in the the range (-1, 1) must have leading zeros - EXPECT_TRUE(JSONWriter::Write(Value(-0.8), &output_js)); - EXPECT_EQ("-0.8", output_js); - - // Test String values. - EXPECT_TRUE(JSONWriter::Write(Value("foo"), &output_js)); - EXPECT_EQ("\"foo\"", output_js); -} - -TEST(JSONWriterTest, NestedTypes) { - std::string output_js; - - // Writer unittests like empty list/dict nesting, - // list list nesting, etc. - DictionaryValue root_dict; - std::unique_ptr<ListValue> list(new ListValue()); - std::unique_ptr<DictionaryValue> inner_dict(new DictionaryValue()); - inner_dict->SetInteger("inner int", 10); - list->Append(std::move(inner_dict)); - list->Append(std::make_unique<ListValue>()); - list->AppendBoolean(true); - root_dict.Set("list", std::move(list)); - - // Test the pretty-printer. - EXPECT_TRUE(JSONWriter::Write(root_dict, &output_js)); - EXPECT_EQ("{\"list\":[{\"inner int\":10},[],true]}", output_js); - EXPECT_TRUE(JSONWriter::WriteWithOptions( - root_dict, JSONWriter::OPTIONS_PRETTY_PRINT, &output_js)); - +std::string FixNewlines(const std::string& json) { // The pretty-printer uses a different newline style on Windows than on // other platforms. -#if defined(OS_WIN) -#define JSON_NEWLINE "\r\n" +#if BUILDFLAG(IS_WIN) + std::string result; + ReplaceChars(json, "\n", "\r\n", &result); + return result; #else -#define JSON_NEWLINE "\n" + return json; #endif - EXPECT_EQ("{" JSON_NEWLINE - " \"list\": [ {" JSON_NEWLINE - " \"inner int\": 10" JSON_NEWLINE - " }, [ ], true ]" JSON_NEWLINE - "}" JSON_NEWLINE, - output_js); -#undef JSON_NEWLINE } -TEST(JSONWriterTest, KeysWithPeriods) { - std::string output_js; +} // namespace - DictionaryValue period_dict; - period_dict.SetKey("a.b", base::Value(3)); - period_dict.SetKey("c", base::Value(2)); - std::unique_ptr<DictionaryValue> period_dict2(new DictionaryValue()); - period_dict2->SetKey("g.h.i.j", base::Value(1)); - period_dict.SetWithoutPathExpansion("d.e.f", std::move(period_dict2)); - EXPECT_TRUE(JSONWriter::Write(period_dict, &output_js)); - EXPECT_EQ("{\"a.b\":3,\"c\":2,\"d.e.f\":{\"g.h.i.j\":1}}", output_js); +TEST(JsonWriterTest, BasicTypes) { + // Test null. + EXPECT_EQ(WriteJson(Value()), "null"); - DictionaryValue period_dict3; - period_dict3.SetInteger("a.b", 2); - period_dict3.SetKey("a.b", base::Value(1)); - EXPECT_TRUE(JSONWriter::Write(period_dict3, &output_js)); - EXPECT_EQ("{\"a\":{\"b\":2},\"a.b\":1}", output_js); + // Test empty dict. + EXPECT_EQ(WriteJson(Value(Value::Type::DICT)), "{}"); + + // Test empty list. + EXPECT_EQ(WriteJson(Value(Value::Type::LIST)), "[]"); + + // Test integer values. + EXPECT_EQ(WriteJson(Value(42)), "42"); + + // Test boolean values. + EXPECT_EQ(WriteJson(Value(true)), "true"); + + // Test Real values should always have a decimal or an 'e'. + EXPECT_EQ(WriteJson(Value(1.0)), "1.0"); + + // Test Real values in the range (-1, 1) must have leading zeros + EXPECT_EQ(WriteJson(Value(0.2)), "0.2"); + + // Test Real values in the range (-1, 1) must have leading zeros + EXPECT_EQ(WriteJson(Value(-0.8)), "-0.8"); + + // Test String values. + EXPECT_EQ(WriteJson(Value("foo")), "\"foo\""); } -TEST(JSONWriterTest, BinaryValues) { - std::string output_js; +TEST(JsonWriterTest, NestedTypes) { + // Writer unittests like empty list/dict nesting, + // list list nesting, etc. + auto dict = + Value::Dict().Set("list", Value::List() + .Append(Value::Dict().Set("inner int", 10)) + .Append(Value::Dict()) + .Append(Value::List()) + .Append(true)); + + EXPECT_EQ(WriteJson(dict), "{\"list\":[{\"inner int\":10},{},[],true]}"); + + // Test the pretty-printer. + EXPECT_EQ(WriteJsonWithOptions(dict, JSONWriter::OPTIONS_PRETTY_PRINT), + FixNewlines(R"({ + "list": [ { + "inner int": 10 + }, { + }, [ ], true ] +} +)")); +} + +TEST(JsonWriterTest, KeysWithPeriods) { + EXPECT_EQ(WriteJson(Value::Dict() // + .Set("a.b", 3) + .Set("c", 2) + .Set("d.e.f", Value::Dict().Set("g.h.i.j", 1))), + R"({"a.b":3,"c":2,"d.e.f":{"g.h.i.j":1}})"); + + EXPECT_EQ(WriteJson(Value::Dict() // + .Set("a", Value::Dict().Set("b", 2)) + .Set("a.b", 1)), + R"({"a":{"b":2},"a.b":1})"); +} + +TEST(JsonWriterTest, BinaryValues) { + const auto kBinaryData = + base::make_span(reinterpret_cast<const uint8_t*>("asdf"), 4u); // Binary values should return errors unless suppressed via the - // OPTIONS_OMIT_BINARY_VALUES flag. - std::unique_ptr<Value> root(Value::CreateWithCopiedBuffer("asdf", 4)); - EXPECT_FALSE(JSONWriter::Write(*root, &output_js)); - EXPECT_TRUE(JSONWriter::WriteWithOptions( - *root, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js)); - EXPECT_TRUE(output_js.empty()); + // `OPTIONS_OMIT_BINARY_VALUES` flag. + EXPECT_EQ(WriteJson(Value(kBinaryData)), absl::nullopt); + EXPECT_EQ(WriteJsonWithOptions(Value(kBinaryData), + JsonOptions::OPTIONS_OMIT_BINARY_VALUES), + ""); - ListValue binary_list; - binary_list.Append(Value::CreateWithCopiedBuffer("asdf", 4)); - binary_list.Append(std::make_unique<Value>(5)); - binary_list.Append(Value::CreateWithCopiedBuffer("asdf", 4)); - binary_list.Append(std::make_unique<Value>(2)); - binary_list.Append(Value::CreateWithCopiedBuffer("asdf", 4)); - EXPECT_FALSE(JSONWriter::Write(binary_list, &output_js)); - EXPECT_TRUE(JSONWriter::WriteWithOptions( - binary_list, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js)); - EXPECT_EQ("[5,2]", output_js); + auto binary_list = Value::List() + .Append(Value(kBinaryData)) + .Append(5) + .Append(Value(kBinaryData)) + .Append(2) + .Append(Value(kBinaryData)); + EXPECT_EQ(WriteJson(binary_list), absl::nullopt); + EXPECT_EQ( + WriteJsonWithOptions(binary_list, JSONWriter::OPTIONS_OMIT_BINARY_VALUES), + "[5,2]"); - DictionaryValue binary_dict; - binary_dict.Set("a", Value::CreateWithCopiedBuffer("asdf", 4)); - binary_dict.SetInteger("b", 5); - binary_dict.Set("c", Value::CreateWithCopiedBuffer("asdf", 4)); - binary_dict.SetInteger("d", 2); - binary_dict.Set("e", Value::CreateWithCopiedBuffer("asdf", 4)); - EXPECT_FALSE(JSONWriter::Write(binary_dict, &output_js)); - EXPECT_TRUE(JSONWriter::WriteWithOptions( - binary_dict, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js)); - EXPECT_EQ("{\"b\":5,\"d\":2}", output_js); + auto binary_dict = Value::Dict() + .Set("a", Value(kBinaryData)) + .Set("b", 5) + .Set("c", Value(kBinaryData)) + .Set("d", 2) + .Set("e", Value(kBinaryData)); + EXPECT_EQ(WriteJson(binary_dict), absl::nullopt); + EXPECT_EQ( + WriteJsonWithOptions(binary_dict, JSONWriter::OPTIONS_OMIT_BINARY_VALUES), + R"({"b":5,"d":2})"); } -TEST(JSONWriterTest, DoublesAsInts) { - std::string output_js; - +TEST(JsonWriterTest, DoublesAsInts) { // Test allowing a double with no fractional part to be written as an integer. Value double_value(1e10); + EXPECT_EQ( + WriteJsonWithOptions(double_value, + JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION), + "10000000000"); +} + +TEST(JsonWriterTest, StackOverflow) { + Value::List deep_list; + const size_t max_depth = 100000; + + for (size_t i = 0; i < max_depth; ++i) { + Value::List new_top_list; + new_top_list.Append(std::move(deep_list)); + deep_list = std::move(new_top_list); + } + + Value deep_list_value(std::move(deep_list)); + EXPECT_EQ(WriteJson(deep_list_value), absl::nullopt); + EXPECT_EQ( + WriteJsonWithOptions(deep_list_value, JSONWriter::OPTIONS_PRETTY_PRINT), + absl::nullopt); + + // We cannot just let `deep_list` tear down since it + // would cause a stack overflow. Therefore, we tear + // down the deep list manually. + deep_list = std::move(deep_list_value).TakeList(); + while (!deep_list.empty()) { + DCHECK_EQ(deep_list.size(), 1u); + Value::List inner_list = std::move(deep_list[0]).TakeList(); + deep_list = std::move(inner_list); + } +} + +TEST(JsonWriterTest, TestMaxDepthWithValidNodes) { + // Create JSON to the max depth - 1. Nodes at that depth are still valid + // for writing which matches the JSONParser logic. + std::string nested_json; + for (int i = 0; i < 199; ++i) { + std::string node = "["; + for (int j = 0; j < 5; j++) { + node.append(StringPrintf("%d,", j)); + } + nested_json.insert(0, node); + nested_json.append("]"); + } + + // Ensure we can read and write the JSON + auto json_val = JSONReader::ReadAndReturnValueWithError( + nested_json, JSON_ALLOW_TRAILING_COMMAS); + EXPECT_TRUE(json_val.has_value()); + const Value& value = *json_val; + EXPECT_NE(WriteJson(value), absl::nullopt); +} + +// Test that the JSONWriter::Write method still works. +TEST(JsonWriterTest, JSONWriterWriteSuccess) { + std::string output_js; + + EXPECT_TRUE( + JSONWriter::Write(base::Value::Dict().Set("key", "value"), &output_js)); + EXPECT_EQ(output_js, R"({"key":"value"})"); +} + +// Test that the JSONWriter::Write method still works. +TEST(JsonWriterTest, JSONWriterWriteFailure) { + std::string output_js; + + EXPECT_FALSE(JSONWriter::Write( + base::Value::Dict() // + .Set("key", + base::Value::Dict().Set("nested-key", base::Value::Dict())), + &output_js, /*max_depth=*/1)); +} + +// Test that the JSONWriter::WriteWithOptions method still works. +TEST(JsonWriterTest, JSONWriterWriteWithOptionsSuccess) { + std::string output_js; EXPECT_TRUE(JSONWriter::WriteWithOptions( - double_value, JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION, + base::Value::Dict().Set("key", "value"), JSONWriter::OPTIONS_PRETTY_PRINT, &output_js)); - EXPECT_EQ("10000000000", output_js); + EXPECT_EQ(output_js, FixNewlines(R"({ + "key": "value" +} +)")); +} + +// Test that the JSONWriter::WriteWithOptions method still works. +TEST(JsonWriterTest, JSONWriterWriteWithOptionsFailure) { + std::string output_js; + + EXPECT_FALSE(JSONWriter::WriteWithOptions( + base::Value::Dict().Set( + "key", base::Value::Dict().Set("nested-key", base::Value::Dict())), + JSONWriter::OPTIONS_PRETTY_PRINT, &output_js, /*max_depth=*/1)); } } // namespace base
diff --git a/base/json/string_escape.cc b/base/json/string_escape.cc index d866c14..84db0f8 100644 --- a/base/json/string_escape.cc +++ b/base/json/string_escape.cc
@@ -1,18 +1,21 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright 2006-2008 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/json/string_escape.h" +#include <stddef.h> +#include <stdint.h> + #include <limits> #include <string> +#include "base/check_op.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversion_utils.h" #include "base/strings/utf_string_conversions.h" #include "base/third_party/icu/icu_utf.h" -#include "starboard/types.h" namespace base { @@ -22,7 +25,7 @@ const char kU16EscapeFormat[] = "\\u%04X"; // The code point to output for an invalid input code unit. -const uint32_t kReplacementCodePoint = 0xFFFD; +const base_icu::UChar32 kReplacementCodePoint = 0xFFFD; // Used below in EscapeSpecialCodePoint(). static_assert('<' == 0x3C, "less than sign must be 0x3c"); @@ -30,7 +33,7 @@ // Try to escape the |code_point| if it is a known special character. If // successful, returns true and appends the escape sequence to |dest|. This // isn't required by the spec, but it's more readable by humans. -bool EscapeSpecialCodePoint(uint32_t code_point, std::string* dest) { +bool EscapeSpecialCodePoint(base_icu::UChar32 code_point, std::string* dest) { // WARNING: if you add a new case here, you need to update the reader as well. // Note: \v is in the reader, but not here since the JSON spec doesn't // allow it. @@ -82,16 +85,11 @@ if (put_in_quotes) dest->push_back('"'); - // Casting is necessary because ICU uses int32_t. Try and do so safely. - CHECK_LE(str.length(), - static_cast<size_t>(std::numeric_limits<int32_t>::max())); - const int32_t length = static_cast<int32_t>(str.length()); - - for (int32_t i = 0; i < length; ++i) { - uint32_t code_point; + const size_t length = str.length(); + for (size_t i = 0; i < length; ++i) { + base_icu::UChar32 code_point; if (!ReadUnicodeCharacter(str.data(), length, &i, &code_point) || - code_point == static_cast<decltype(code_point)>(CBU_SENTINEL) || - !IsValidCharacter(code_point)) { + code_point == CBU_SENTINEL) { code_point = kReplacementCodePoint; did_replacement = true; } @@ -126,15 +124,13 @@ std::string GetQuotedJSONString(StringPiece str) { std::string dest; - bool ok = EscapeJSONStringImpl(str, true, &dest); - DCHECK(ok); + EscapeJSONStringImpl(str, true, &dest); return dest; } std::string GetQuotedJSONString(StringPiece16 str) { std::string dest; - bool ok = EscapeJSONStringImpl(str, true, &dest); - DCHECK(ok); + EscapeJSONStringImpl(str, true, &dest); return dest; } @@ -145,15 +141,16 @@ if (put_in_quotes) dest.push_back('"'); - for (StringPiece::const_iterator it = str.begin(); it != str.end(); ++it) { - unsigned char c = *it; + for (char c : str) { if (EscapeSpecialCodePoint(c, &dest)) continue; - if (c < 32 || c > 126) - base::StringAppendF(&dest, kU16EscapeFormat, c); - else - dest.push_back(*it); + if (c < 32 || c > 126) { + base::StringAppendF(&dest, kU16EscapeFormat, + static_cast<unsigned char>(c)); + } else { + dest.push_back(c); + } } if (put_in_quotes)
diff --git a/base/json/string_escape.h b/base/json/string_escape.h index f75f475..ca7f7fa 100644 --- a/base/json/string_escape.h +++ b/base/json/string_escape.h
@@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright 2011 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -52,7 +52,7 @@ // interpret it as UTF-16 and convert it to UTF-8. // // The output of this function takes the *appearance* of JSON but is not in -// fact valid according to RFC 4627. +// fact valid according to RFC 8259. BASE_EXPORT std::string EscapeBytesAsInvalidJSONString(StringPiece str, bool put_in_quotes);
diff --git a/base/json/string_escape_fuzzer.cc b/base/json/string_escape_fuzzer.cc index 3cd8509..9a63aab 100644 --- a/base/json/string_escape_fuzzer.cc +++ b/base/json/string_escape_fuzzer.cc
@@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2018 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,8 +6,6 @@ #include <memory> -#include "starboard/memory.h" - // Entry point for LibFuzzer. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size < 2) @@ -30,8 +28,8 @@ return 0; size_t actual_size_char16 = actual_size_char8 / 2; - base::StringPiece16 input_string16( - reinterpret_cast<base::char16*>(input.get()), actual_size_char16); + base::StringPiece16 input_string16(reinterpret_cast<char16_t*>(input.get()), + actual_size_char16); escaped_string.clear(); base::EscapeJSONString(input_string16, put_in_quotes, &escaped_string);
diff --git a/base/json/string_escape_unittest.cc b/base/json/string_escape_unittest.cc index bc7f3c2..0a4106e 100644 --- a/base/json/string_escape_unittest.cc +++ b/base/json/string_escape_unittest.cc
@@ -1,13 +1,13 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/json/string_escape.h" -#include "base/macros.h" +#include <stddef.h> + #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "starboard/types.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -22,43 +22,78 @@ {"b\x0f\x7f\xf0\xff!", // \xf0\xff is not a valid UTF-8 unit. "b\\u000F\x7F\xEF\xBF\xBD\xEF\xBF\xBD!"}, {"c<>d", "c\\u003C>d"}, - {"Hello\xe2\x80\xa8world", "Hello\\u2028world"}, - {"\xe2\x80\xa9purple", "\\u2029purple"}, - {"\xF3\xBF\xBF\xBF", "\xEF\xBF\xBD"}, + {"Hello\xE2\x80\xA8world", "Hello\\u2028world"}, // U+2028 + {"\xE2\x80\xA9purple", "\\u2029purple"}, // U+2029 + // Unicode non-characters. + {"\xEF\xB7\x90", "\xEF\xB7\x90"}, // U+FDD0 + {"\xEF\xB7\x9F", "\xEF\xB7\x9F"}, // U+FDDF + {"\xEF\xB7\xAF", "\xEF\xB7\xAF"}, // U+FDEF + {"\xEF\xBF\xBE", "\xEF\xBF\xBE"}, // U+FFFE + {"\xEF\xBF\xBF", "\xEF\xBF\xBF"}, // U+FFFF + {"\xF0\x9F\xBF\xBE", "\xF0\x9F\xBF\xBE"}, // U+01FFFE + {"\xF0\x9F\xBF\xBF", "\xF0\x9F\xBF\xBF"}, // U+01FFFF + {"\xF0\xAF\xBF\xBE", "\xF0\xAF\xBF\xBE"}, // U+02FFFE + {"\xF0\xAF\xBF\xBF", "\xF0\xAF\xBF\xBF"}, // U+02FFFF + {"\xF0\xBF\xBF\xBE", "\xF0\xBF\xBF\xBE"}, // U+03FFFE + {"\xF0\xBF\xBF\xBF", "\xF0\xBF\xBF\xBF"}, // U+03FFFF + {"\xF1\x8F\xBF\xBE", "\xF1\x8F\xBF\xBE"}, // U+04FFFE + {"\xF1\x8F\xBF\xBF", "\xF1\x8F\xBF\xBF"}, // U+04FFFF + {"\xF1\x9F\xBF\xBE", "\xF1\x9F\xBF\xBE"}, // U+05FFFE + {"\xF1\x9F\xBF\xBF", "\xF1\x9F\xBF\xBF"}, // U+05FFFF + {"\xF1\xAF\xBF\xBE", "\xF1\xAF\xBF\xBE"}, // U+06FFFE + {"\xF1\xAF\xBF\xBF", "\xF1\xAF\xBF\xBF"}, // U+06FFFF + {"\xF1\xBF\xBF\xBE", "\xF1\xBF\xBF\xBE"}, // U+07FFFE + {"\xF1\xBF\xBF\xBF", "\xF1\xBF\xBF\xBF"}, // U+07FFFF + {"\xF2\x8F\xBF\xBE", "\xF2\x8F\xBF\xBE"}, // U+08FFFE + {"\xF2\x8F\xBF\xBF", "\xF2\x8F\xBF\xBF"}, // U+08FFFF + {"\xF2\x9F\xBF\xBE", "\xF2\x9F\xBF\xBE"}, // U+09FFFE + {"\xF2\x9F\xBF\xBF", "\xF2\x9F\xBF\xBF"}, // U+09FFFF + {"\xF2\xAF\xBF\xBE", "\xF2\xAF\xBF\xBE"}, // U+0AFFFE + {"\xF2\xAF\xBF\xBF", "\xF2\xAF\xBF\xBF"}, // U+0AFFFF + {"\xF2\xBF\xBF\xBE", "\xF2\xBF\xBF\xBE"}, // U+0BFFFE + {"\xF2\xBF\xBF\xBF", "\xF2\xBF\xBF\xBF"}, // U+0BFFFF + {"\xF3\x8F\xBF\xBE", "\xF3\x8F\xBF\xBE"}, // U+0CFFFE + {"\xF3\x8F\xBF\xBF", "\xF3\x8F\xBF\xBF"}, // U+0CFFFF + {"\xF3\x9F\xBF\xBE", "\xF3\x9F\xBF\xBE"}, // U+0DFFFE + {"\xF3\x9F\xBF\xBF", "\xF3\x9F\xBF\xBF"}, // U+0DFFFF + {"\xF3\xAF\xBF\xBE", "\xF3\xAF\xBF\xBE"}, // U+0EFFFE + {"\xF3\xAF\xBF\xBF", "\xF3\xAF\xBF\xBF"}, // U+0EFFFF + {"\xF3\xBF\xBF\xBE", "\xF3\xBF\xBF\xBE"}, // U+0FFFFE + {"\xF3\xBF\xBF\xBF", "\xF3\xBF\xBF\xBF"}, // U+0FFFFF + {"\xF4\x8F\xBF\xBE", "\xF4\x8F\xBF\xBE"}, // U+10FFFE + {"\xF4\x8F\xBF\xBF", "\xF4\x8F\xBF\xBF"}, // U+10FFFF }; - for (size_t i = 0; i < arraysize(cases); ++i) { - const char* in_ptr = cases[i].to_escape; + for (const auto& i : cases) { + const char* in_ptr = i.to_escape; std::string in_str = in_ptr; std::string out; EscapeJSONString(in_ptr, false, &out); - EXPECT_EQ(std::string(cases[i].escaped), out); - EXPECT_TRUE(IsStringUTF8(out)); + EXPECT_EQ(std::string(i.escaped), out); + EXPECT_TRUE(IsStringUTF8AllowingNoncharacters(out)); out.erase(); - bool convert_ok = EscapeJSONString(in_str, false, &out); - EXPECT_EQ(std::string(cases[i].escaped), out); - EXPECT_TRUE(IsStringUTF8(out)); + EscapeJSONString(in_str, false, &out); + EXPECT_EQ(std::string(i.escaped), out); + EXPECT_TRUE(IsStringUTF8AllowingNoncharacters(out)); - if (convert_ok) { - std::string fooout = GetQuotedJSONString(in_str); - EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"", fooout); - EXPECT_TRUE(IsStringUTF8(out)); - } + std::string fooout = GetQuotedJSONString(in_str); + EXPECT_EQ("\"" + std::string(i.escaped) + "\"", fooout); + EXPECT_TRUE(IsStringUTF8AllowingNoncharacters(out)); } std::string in = cases[0].to_escape; std::string out; EscapeJSONString(in, false, &out); - EXPECT_TRUE(IsStringUTF8(out)); + EXPECT_TRUE(IsStringUTF8AllowingNoncharacters(out)); // test quoting std::string out_quoted; EscapeJSONString(in, true, &out_quoted); EXPECT_EQ(out.length() + 2, out_quoted.length()); EXPECT_EQ(out_quoted.find(out), 1U); - EXPECT_TRUE(IsStringUTF8(out_quoted)); + EXPECT_TRUE(IsStringUTF8AllowingNoncharacters(out_quoted)); // now try with a NULL in the string std::string null_prepend = "test"; @@ -69,7 +104,7 @@ out.clear(); EscapeJSONString(in, false, &out); EXPECT_EQ(expected, out); - EXPECT_TRUE(IsStringUTF8(out)); + EXPECT_TRUE(IsStringUTF8AllowingNoncharacters(out)); } TEST(JSONStringEscapeTest, EscapeUTF16) { @@ -77,43 +112,80 @@ const wchar_t* to_escape; const char* escaped; } cases[] = { - {L"b\uffb1\u00ff", "b\xEF\xBE\xB1\xC3\xBF"}, - {L"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"}, - {L"a\b\f\n\r\t\v\1\\.\"z", - "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"}, - {L"b\x0f\x7f\xf0\xff!", "b\\u000F\x7F\xC3\xB0\xC3\xBF!"}, - {L"c<>d", "c\\u003C>d"}, - {L"Hello\u2028world", "Hello\\u2028world"}, - {L"\u2029purple", "\\u2029purple"}, + {L"b\uffb1\u00ff", "b\xEF\xBE\xB1\xC3\xBF"}, + {L"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"}, + {L"a\b\f\n\r\t\v\1\\.\"z", "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"}, + {L"b\x0F\x7F\xF0\xFF!", "b\\u000F\x7F\xC3\xB0\xC3\xBF!"}, + {L"c<>d", "c\\u003C>d"}, + {L"Hello\u2028world", "Hello\\u2028world"}, + {L"\u2029purple", "\\u2029purple"}, + // Unicode non-characters. + {L"\uFDD0", "\xEF\xB7\x90"}, // U+FDD0 + {L"\uFDDF", "\xEF\xB7\x9F"}, // U+FDDF + {L"\uFDEF", "\xEF\xB7\xAF"}, // U+FDEF + {L"\uFFFE", "\xEF\xBF\xBE"}, // U+FFFE + {L"\uFFFF", "\xEF\xBF\xBF"}, // U+FFFF + {L"\U0001FFFE", "\xF0\x9F\xBF\xBE"}, // U+01FFFE + {L"\U0001FFFF", "\xF0\x9F\xBF\xBF"}, // U+01FFFF + {L"\U0002FFFE", "\xF0\xAF\xBF\xBE"}, // U+02FFFE + {L"\U0002FFFF", "\xF0\xAF\xBF\xBF"}, // U+02FFFF + {L"\U0003FFFE", "\xF0\xBF\xBF\xBE"}, // U+03FFFE + {L"\U0003FFFF", "\xF0\xBF\xBF\xBF"}, // U+03FFFF + {L"\U0004FFFE", "\xF1\x8F\xBF\xBE"}, // U+04FFFE + {L"\U0004FFFF", "\xF1\x8F\xBF\xBF"}, // U+04FFFF + {L"\U0005FFFE", "\xF1\x9F\xBF\xBE"}, // U+05FFFE + {L"\U0005FFFF", "\xF1\x9F\xBF\xBF"}, // U+05FFFF + {L"\U0006FFFE", "\xF1\xAF\xBF\xBE"}, // U+06FFFE + {L"\U0006FFFF", "\xF1\xAF\xBF\xBF"}, // U+06FFFF + {L"\U0007FFFE", "\xF1\xBF\xBF\xBE"}, // U+07FFFE + {L"\U0007FFFF", "\xF1\xBF\xBF\xBF"}, // U+07FFFF + {L"\U0008FFFE", "\xF2\x8F\xBF\xBE"}, // U+08FFFE + {L"\U0008FFFF", "\xF2\x8F\xBF\xBF"}, // U+08FFFF + {L"\U0009FFFE", "\xF2\x9F\xBF\xBE"}, // U+09FFFE + {L"\U0009FFFF", "\xF2\x9F\xBF\xBF"}, // U+09FFFF + {L"\U000AFFFE", "\xF2\xAF\xBF\xBE"}, // U+0AFFFE + {L"\U000AFFFF", "\xF2\xAF\xBF\xBF"}, // U+0AFFFF + {L"\U000BFFFE", "\xF2\xBF\xBF\xBE"}, // U+0BFFFE + {L"\U000BFFFF", "\xF2\xBF\xBF\xBF"}, // U+0BFFFF + {L"\U000CFFFE", "\xF3\x8F\xBF\xBE"}, // U+0CFFFE + {L"\U000CFFFF", "\xF3\x8F\xBF\xBF"}, // U+0CFFFF + {L"\U000DFFFE", "\xF3\x9F\xBF\xBE"}, // U+0DFFFE + {L"\U000DFFFF", "\xF3\x9F\xBF\xBF"}, // U+0DFFFF + {L"\U000EFFFE", "\xF3\xAF\xBF\xBE"}, // U+0EFFFE + {L"\U000EFFFF", "\xF3\xAF\xBF\xBF"}, // U+0EFFFF + {L"\U000FFFFE", "\xF3\xBF\xBF\xBE"}, // U+0FFFFE + {L"\U000FFFFF", "\xF3\xBF\xBF\xBF"}, // U+0FFFFF + {L"\U0010FFFE", "\xF4\x8F\xBF\xBE"}, // U+10FFFE + {L"\U0010FFFF", "\xF4\x8F\xBF\xBF"}, // U+10FFFF }; - for (size_t i = 0; i < arraysize(cases); ++i) { - string16 in = WideToUTF16(cases[i].to_escape); + for (const auto& i : cases) { + std::u16string in = WideToUTF16(i.to_escape); std::string out; EscapeJSONString(in, false, &out); - EXPECT_EQ(std::string(cases[i].escaped), out); - EXPECT_TRUE(IsStringUTF8(out)); + EXPECT_EQ(std::string(i.escaped), out); + EXPECT_TRUE(IsStringUTF8AllowingNoncharacters(out)); out = GetQuotedJSONString(in); - EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"", out); - EXPECT_TRUE(IsStringUTF8(out)); + EXPECT_EQ("\"" + std::string(i.escaped) + "\"", out); + EXPECT_TRUE(IsStringUTF8AllowingNoncharacters(out)); } - string16 in = WideToUTF16(cases[0].to_escape); + std::u16string in = WideToUTF16(cases[0].to_escape); std::string out; EscapeJSONString(in, false, &out); - EXPECT_TRUE(IsStringUTF8(out)); + EXPECT_TRUE(IsStringUTF8AllowingNoncharacters(out)); // test quoting std::string out_quoted; EscapeJSONString(in, true, &out_quoted); EXPECT_EQ(out.length() + 2, out_quoted.length()); EXPECT_EQ(out_quoted.find(out), 1U); - EXPECT_TRUE(IsStringUTF8(out)); + EXPECT_TRUE(IsStringUTF8AllowingNoncharacters(out)); // now try with a NULL in the string - string16 null_prepend = WideToUTF16(L"test"); + std::u16string null_prepend = u"test"; null_prepend.push_back(0); in = null_prepend + in; std::string expected = "test\\u0000"; @@ -121,13 +193,13 @@ out.clear(); EscapeJSONString(in, false, &out); EXPECT_EQ(expected, out); - EXPECT_TRUE(IsStringUTF8(out)); + EXPECT_TRUE(IsStringUTF8AllowingNoncharacters(out)); } TEST(JSONStringEscapeTest, EscapeUTF16OutsideBMP) { { // {a, U+10300, !}, SMP. - string16 test; + std::u16string test; test.push_back('a'); test.push_back(0xD800); test.push_back(0xDF00); @@ -138,7 +210,7 @@ } { // {U+20021, U+2002B}, SIP. - string16 test; + std::u16string test; test.push_back(0xD840); test.push_back(0xDC21); test.push_back(0xD840); @@ -149,7 +221,7 @@ } { // {?, U+D800, @}, lone surrogate. - string16 test; + std::u16string test; test.push_back('?'); test.push_back(0xD800); test.push_back('@'); @@ -168,19 +240,19 @@ {"\xe5\xc4\x4f\x05\xb6\xfd", "\\u00E5\\u00C4O\\u0005\\u00B6\\u00FD"}, }; - for (size_t i = 0; i < arraysize(cases); ++i) { - std::string in = std::string(cases[i].to_escape); - EXPECT_FALSE(IsStringUTF8(in)); + for (const auto& i : cases) { + std::string in = std::string(i.to_escape); + EXPECT_FALSE(IsStringUTF8AllowingNoncharacters(in)); - EXPECT_EQ(std::string(cases[i].escaped), - EscapeBytesAsInvalidJSONString(in, false)); - EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"", - EscapeBytesAsInvalidJSONString(in, true)); + EXPECT_EQ(std::string(i.escaped), + EscapeBytesAsInvalidJSONString(in, false)); + EXPECT_EQ("\"" + std::string(i.escaped) + "\"", + EscapeBytesAsInvalidJSONString(in, true)); } const char kEmbedNull[] = { '\xab', '\x39', '\0', '\x9f', '\xab' }; - std::string in(kEmbedNull, arraysize(kEmbedNull)); - EXPECT_FALSE(IsStringUTF8(in)); + std::string in(kEmbedNull, std::size(kEmbedNull)); + EXPECT_FALSE(IsStringUTF8AllowingNoncharacters(in)); EXPECT_EQ(std::string("\\u00AB9\\u0000\\u009F\\u00AB"), EscapeBytesAsInvalidJSONString(in, false)); }
diff --git a/base/json/values_util.cc b/base/json/values_util.cc new file mode 100644 index 0000000..8f14256 --- /dev/null +++ b/base/json/values_util.cc
@@ -0,0 +1,123 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/json/values_util.h" + +#include "base/files/file_path.h" +#include "base/strings/string_number_conversions.h" +#include "base/time/time.h" +#include "base/unguessable_token.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +// Warning: The Values involved could be stored on persistent storage like files +// on disks. Therefore, changes in implementation could lead to data corruption +// and must be done with caution. + +namespace base { + +namespace { + +// Helper to serialize/deserialize an UnguessableToken. +// +// It assumes a little-endian CPU, which is arguably a bug. +union UnguessableTokenRepresentation { + struct Field { + uint64_t high; + uint64_t low; + } field; + + uint8_t buffer[sizeof(Field)]; +}; + +} // namespace + +Value Int64ToValue(int64_t integer) { + return Value(NumberToString(integer)); +} + +absl::optional<int64_t> ValueToInt64(const Value* value) { + return value ? ValueToInt64(*value) : absl::nullopt; +} + +absl::optional<int64_t> ValueToInt64(const Value& value) { + if (!value.is_string()) + return absl::nullopt; + + int64_t integer; + if (!StringToInt64(value.GetString(), &integer)) + return absl::nullopt; + + return integer; +} + +Value TimeDeltaToValue(TimeDelta time_delta) { + return Int64ToValue(time_delta.InMicroseconds()); +} + +absl::optional<TimeDelta> ValueToTimeDelta(const Value* value) { + return value ? ValueToTimeDelta(*value) : absl::nullopt; +} + +absl::optional<TimeDelta> ValueToTimeDelta(const Value& value) { + absl::optional<int64_t> integer = ValueToInt64(value); + if (!integer) + return absl::nullopt; + return Microseconds(*integer); +} + +Value TimeToValue(Time time) { + return TimeDeltaToValue(time.ToDeltaSinceWindowsEpoch()); +} + +absl::optional<Time> ValueToTime(const Value* value) { + return value ? ValueToTime(*value) : absl::nullopt; +} + +absl::optional<Time> ValueToTime(const Value& value) { + absl::optional<TimeDelta> time_delta = ValueToTimeDelta(value); + if (!time_delta) + return absl::nullopt; + return Time::FromDeltaSinceWindowsEpoch(*time_delta); +} + +Value FilePathToValue(FilePath file_path) { + return Value(file_path.AsUTF8Unsafe()); +} + +absl::optional<FilePath> ValueToFilePath(const Value* value) { + return value ? ValueToFilePath(*value) : absl::nullopt; +} + +absl::optional<FilePath> ValueToFilePath(const Value& value) { + if (!value.is_string()) + return absl::nullopt; + return FilePath::FromUTF8Unsafe(value.GetString()); +} + +Value UnguessableTokenToValue(UnguessableToken token) { + UnguessableTokenRepresentation repr; + repr.field.high = token.GetHighForSerialization(); + repr.field.low = token.GetLowForSerialization(); + return Value(HexEncode(repr.buffer, sizeof(repr.buffer))); +} + +absl::optional<UnguessableToken> ValueToUnguessableToken(const Value* value) { + return value ? ValueToUnguessableToken(*value) : absl::nullopt; +} + +absl::optional<UnguessableToken> ValueToUnguessableToken(const Value& value) { + if (!value.is_string()) + return absl::nullopt; + UnguessableTokenRepresentation repr; + if (!HexStringToSpan(value.GetString(), repr.buffer)) + return absl::nullopt; + absl::optional<base::UnguessableToken> token = + UnguessableToken::Deserialize(repr.field.high, repr.field.low); + if (!token.has_value()) { + return absl::nullopt; + } + return token; +} + +} // namespace base
diff --git a/base/json/values_util.h b/base/json/values_util.h new file mode 100644 index 0000000..2c3722d --- /dev/null +++ b/base/json/values_util.h
@@ -0,0 +1,60 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_JSON_VALUES_UTIL_H_ +#define BASE_JSON_VALUES_UTIL_H_ + +#include "base/base_export.h" +#include "base/values.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace base { +class FilePath; +class Time; +class TimeDelta; +class UnguessableToken; + +// Simple helper functions for converting between Value and other types. +// The Value representation is stable, suitable for persistent storage +// e.g. as JSON on disk. +// +// It is valid to pass nullptr to the ValueToEtc functions. They will just +// return absl::nullopt. + +// Converts between an int64_t and a string-flavored Value (a human +// readable string of that number). +BASE_EXPORT Value Int64ToValue(int64_t integer); +BASE_EXPORT absl::optional<int64_t> ValueToInt64(const Value* value); +BASE_EXPORT absl::optional<int64_t> ValueToInt64(const Value& value); + +// Converts between a TimeDelta (an int64_t number of microseconds) and a +// string-flavored Value (a human readable string of that number). +BASE_EXPORT Value TimeDeltaToValue(TimeDelta time_delta); +BASE_EXPORT absl::optional<TimeDelta> ValueToTimeDelta(const Value* value); +BASE_EXPORT absl::optional<TimeDelta> ValueToTimeDelta(const Value& value); + +// Converts between a Time (an int64_t number of microseconds since the +// Windows epoch) and a string-flavored Value (a human readable string of +// that number). +BASE_EXPORT Value TimeToValue(Time time); +BASE_EXPORT absl::optional<Time> ValueToTime(const Value* value); +BASE_EXPORT absl::optional<Time> ValueToTime(const Value& value); + +// Converts between a FilePath (a std::string or std::u16string) and a +// string-flavored Value (the UTF-8 representation). +BASE_EXPORT Value FilePathToValue(FilePath file_path); +BASE_EXPORT absl::optional<FilePath> ValueToFilePath(const Value* value); +BASE_EXPORT absl::optional<FilePath> ValueToFilePath(const Value& value); + +// Converts between a UnguessableToken (128 bits) and a string-flavored +// Value (32 hexadecimal digits). +BASE_EXPORT Value UnguessableTokenToValue(UnguessableToken token); +BASE_EXPORT absl::optional<UnguessableToken> ValueToUnguessableToken( + const Value* value); +BASE_EXPORT absl::optional<UnguessableToken> ValueToUnguessableToken( + const Value& value); + +} // namespace base + +#endif // BASE_JSON_VALUES_UTIL_H_
diff --git a/base/json/values_util_unittest.cc b/base/json/values_util_unittest.cc new file mode 100644 index 0000000..ef7cb98 --- /dev/null +++ b/base/json/values_util_unittest.cc
@@ -0,0 +1,109 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/json/values_util.h" + +#include <limits> + +#include "base/files/file_path.h" +#include "base/strings/string_piece.h" +#include "base/time/time.h" +#include "base/unguessable_token.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +namespace { + +TEST(ValuesUtilTest, BasicInt64Limits) { + constexpr struct { + int64_t input; + StringPiece expected; + } kTestCases[] = { + {0, "0"}, + {-1234, "-1234"}, + {5678, "5678"}, + {std::numeric_limits<int64_t>::lowest(), "-9223372036854775808"}, + {std::numeric_limits<int64_t>::max(), "9223372036854775807"}, + }; + for (const auto& test_case : kTestCases) { + int64_t input = test_case.input; + TimeDelta time_delta_input = Microseconds(input); + Time time_input = Time::FromDeltaSinceWindowsEpoch(time_delta_input); + Value expected(test_case.expected); + SCOPED_TRACE(testing::Message() + << "input: " << input << ", expected: " << expected); + + EXPECT_EQ(Int64ToValue(input), expected); + EXPECT_EQ(*ValueToInt64(&expected), input); + + EXPECT_EQ(TimeDeltaToValue(time_delta_input), expected); + EXPECT_EQ(*ValueToTimeDelta(&expected), time_delta_input); + + EXPECT_EQ(TimeToValue(time_input), expected); + EXPECT_EQ(*ValueToTime(&expected), time_input); + } +} + +TEST(ValuesUtilTest, InvalidInt64Values) { + const std::unique_ptr<Value> kTestCases[] = { + nullptr, + std::make_unique<Value>(), + std::make_unique<Value>(0), + std::make_unique<Value>(1234), + std::make_unique<Value>(true), + std::make_unique<Value>(Value::Type::BINARY), + std::make_unique<Value>(Value::Type::LIST), + std::make_unique<Value>(Value::Type::DICT), + std::make_unique<Value>(""), + std::make_unique<Value>("abcd"), + std::make_unique<Value>("1234.0"), + std::make_unique<Value>("1234a"), + std::make_unique<Value>("a1234"), + }; + for (const auto& test_case : kTestCases) { + EXPECT_FALSE(ValueToInt64(test_case.get())); + EXPECT_FALSE(ValueToTimeDelta(test_case.get())); + EXPECT_FALSE(ValueToTime(test_case.get())); + } +} + +TEST(ValuesUtilTest, FilePath) { + // Ω is U+03A9 GREEK CAPITAL LETTER OMEGA, a non-ASCII character. + constexpr StringPiece kTestCases[] = { + "/unix/Ω/path.dat", + "C:\\windows\\Ω\\path.dat", + }; + for (auto test_case : kTestCases) { + FilePath input = FilePath::FromUTF8Unsafe(test_case); + Value expected(test_case); + SCOPED_TRACE(testing::Message() << "test_case: " << test_case); + + EXPECT_EQ(FilePathToValue(input), expected); + EXPECT_EQ(*ValueToFilePath(&expected), input); + } +} + +TEST(ValuesUtilTest, UnguessableToken) { + constexpr struct { + uint64_t high; + uint64_t low; + StringPiece expected; + } kTestCases[] = { + {0x123456u, 0x9ABCu, "5634120000000000BC9A000000000000"}, + }; + for (const auto& test_case : kTestCases) { + UnguessableToken input = + UnguessableToken::CreateForTesting(test_case.high, test_case.low); + Value expected(test_case.expected); + SCOPED_TRACE(testing::Message() << "expected: " << test_case.expected); + + EXPECT_EQ(UnguessableTokenToValue(input), expected); + EXPECT_EQ(*ValueToUnguessableToken(&expected), input); + } +} + +} // namespace + +} // namespace base