Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/json/json_reader.h" | 5 #include "base/json/json_reader.h" |
| 6 | 6 |
| 7 #include "base/float_util.h" | 7 #include "base/float_util.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
| 10 #include "base/stringprintf.h" | 10 #include "base/stringprintf.h" |
| 11 #include "base/string_number_conversions.h" | 11 #include "base/string_number_conversions.h" |
| 12 #include "base/string_util.h" | 12 #include "base/string_util.h" |
| 13 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
| 14 #include "base/values.h" | 14 #include "base/values.h" |
| 15 | 15 |
| 16 namespace { | 16 namespace { |
| 17 | 17 |
| 18 const wchar_t kNullString[] = L"null"; | 18 const wchar_t kNullString[] = L"null"; |
| 19 const wchar_t kTrueString[] = L"true"; | 19 const wchar_t kTrueString[] = L"true"; |
| 20 const wchar_t kFalseString[] = L"false"; | 20 const wchar_t kFalseString[] = L"false"; |
| 21 | 21 |
| 22 const int kStackLimit = 100; | 22 const int kStackLimit = 100; |
| 23 | 23 |
| 24 } // namespace | |
| 25 | |
| 26 namespace base { | |
| 27 | |
| 28 static const JSONReader::Token kInvalidToken(JSONReader::Token::INVALID_TOKEN, | |
| 29 0, 0); | |
| 30 namespace { | |
| 31 | |
| 32 // A helper method for ParseNumberToken. It reads an int from the end of | 24 // A helper method for ParseNumberToken. It reads an int from the end of |
| 33 // token. The method returns false if there is no valid integer at the end of | 25 // token. The method returns false if there is no valid integer at the end of |
| 34 // the token. | 26 // the token. |
| 35 bool ReadInt(JSONReader::Token& token, bool can_have_leading_zeros) { | 27 bool ReadInt(base::JSONReader::Token& token, bool can_have_leading_zeros) { |
| 36 wchar_t first = token.NextChar(); | 28 wchar_t first = token.NextChar(); |
| 37 int len = 0; | 29 int len = 0; |
| 38 | 30 |
| 39 // Read in more digits | 31 // Read in more digits |
| 40 wchar_t c = first; | 32 wchar_t c = first; |
| 41 while ('\0' != c && '0' <= c && c <= '9') { | 33 while ('\0' != c && '0' <= c && c <= '9') { |
| 42 ++token.length; | 34 ++token.length; |
| 43 ++len; | 35 ++len; |
| 44 c = token.NextChar(); | 36 c = token.NextChar(); |
| 45 } | 37 } |
| 46 // We need at least 1 digit. | 38 // We need at least 1 digit. |
| 47 if (len == 0) | 39 if (len == 0) |
| 48 return false; | 40 return false; |
| 49 | 41 |
| 50 if (!can_have_leading_zeros && len > 1 && '0' == first) | 42 if (!can_have_leading_zeros && len > 1 && '0' == first) |
| 51 return false; | 43 return false; |
| 52 | 44 |
| 53 return true; | 45 return true; |
| 54 } | 46 } |
| 55 | 47 |
| 56 // A helper method for ParseStringToken. It reads |digits| hex digits from the | 48 // A helper method for ParseStringToken. It reads |digits| hex digits from the |
| 57 // token. If the sequence if digits is not valid (contains other characters), | 49 // token. If the sequence if digits is not valid (contains other characters), |
| 58 // the method returns false. | 50 // the method returns false. |
| 59 bool ReadHexDigits(JSONReader::Token& token, int digits) { | 51 bool ReadHexDigits(base::JSONReader::Token& token, int digits) { |
| 60 for (int i = 1; i <= digits; ++i) { | 52 for (int i = 1; i <= digits; ++i) { |
| 61 wchar_t c = *(token.begin + token.length + i); | 53 wchar_t c = *(token.begin + token.length + i); |
| 62 if ('\0' == c) | 54 if ('\0' == c) |
| 63 return false; | 55 return false; |
| 64 if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || | 56 if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || |
| 65 ('A' <= c && c <= 'F'))) { | 57 ('A' <= c && c <= 'F'))) { |
| 66 return false; | 58 return false; |
| 67 } | 59 } |
| 68 } | 60 } |
| 69 | 61 |
| 70 token.length += digits; | 62 token.length += digits; |
| 71 return true; | 63 return true; |
| 72 } | 64 } |
| 73 | 65 |
| 74 } // namespace | 66 } // namespace |
| 75 | 67 |
| 68 namespace base { | |
| 69 | |
| 76 const char* JSONReader::kBadRootElementType = | 70 const char* JSONReader::kBadRootElementType = |
| 77 "Root value must be an array or object."; | 71 "Root value must be an array or object."; |
| 78 const char* JSONReader::kInvalidEscape = | 72 const char* JSONReader::kInvalidEscape = |
| 79 "Invalid escape sequence."; | 73 "Invalid escape sequence."; |
| 80 const char* JSONReader::kSyntaxError = | 74 const char* JSONReader::kSyntaxError = |
| 81 "Syntax error."; | 75 "Syntax error."; |
| 82 const char* JSONReader::kTrailingComma = | 76 const char* JSONReader::kTrailingComma = |
| 83 "Trailing comma not allowed."; | 77 "Trailing comma not allowed."; |
| 84 const char* JSONReader::kTooMuchNesting = | 78 const char* JSONReader::kTooMuchNesting = |
| 85 "Too much nesting."; | 79 "Too much nesting."; |
| 86 const char* JSONReader::kUnexpectedDataAfterRoot = | 80 const char* JSONReader::kUnexpectedDataAfterRoot = |
| 87 "Unexpected data after root element."; | 81 "Unexpected data after root element."; |
| 88 const char* JSONReader::kUnsupportedEncoding = | 82 const char* JSONReader::kUnsupportedEncoding = |
| 89 "Unsupported encoding. JSON must be UTF-8."; | 83 "Unsupported encoding. JSON must be UTF-8."; |
| 90 const char* JSONReader::kUnquotedDictionaryKey = | 84 const char* JSONReader::kUnquotedDictionaryKey = |
| 91 "Dictionary keys must be quoted."; | 85 "Dictionary keys must be quoted."; |
| 92 | 86 |
| 93 JSONReader::JSONReader() | 87 JSONReader::JSONReader() |
| 94 : start_pos_(NULL), json_pos_(NULL), stack_depth_(0), | 88 : kInvalidToken(new Token(Token::INVALID_TOKEN, 0, 0)), |
|
tfarina
2011/09/01 01:21:07
I'm not convinced this is the best way to do this.
tony
2011/09/01 01:48:11
I think we should just get rid of kInvalidToken an
| |
| 89 start_pos_(NULL), | |
| 90 json_pos_(NULL), | |
| 91 stack_depth_(0), | |
| 95 allow_trailing_comma_(false), | 92 allow_trailing_comma_(false), |
| 96 error_code_(JSON_NO_ERROR), error_line_(0), error_col_(0) {} | 93 error_code_(JSON_NO_ERROR), |
| 94 error_line_(0), | |
| 95 error_col_(0) {} | |
| 97 | 96 |
| 98 /* static */ | 97 // static |
| 99 Value* JSONReader::Read(const std::string& json, | 98 Value* JSONReader::Read(const std::string& json, |
| 100 bool allow_trailing_comma) { | 99 bool allow_trailing_comma) { |
| 101 return ReadAndReturnError(json, allow_trailing_comma, NULL, NULL); | 100 return ReadAndReturnError(json, allow_trailing_comma, NULL, NULL); |
| 102 } | 101 } |
| 103 | 102 |
| 104 /* static */ | 103 // static |
| 105 Value* JSONReader::ReadAndReturnError(const std::string& json, | 104 Value* JSONReader::ReadAndReturnError(const std::string& json, |
| 106 bool allow_trailing_comma, | 105 bool allow_trailing_comma, |
| 107 int* error_code_out, | 106 int* error_code_out, |
| 108 std::string* error_msg_out) { | 107 std::string* error_msg_out) { |
| 109 JSONReader reader = JSONReader(); | 108 JSONReader reader = JSONReader(); |
| 110 Value* root = reader.JsonToValue(json, true, allow_trailing_comma); | 109 Value* root = reader.JsonToValue(json, true, allow_trailing_comma); |
| 111 if (root) | 110 if (root) |
| 112 return root; | 111 return root; |
| 113 | 112 |
| 114 if (error_code_out) | 113 if (error_code_out) |
| 115 *error_code_out = reader.error_code(); | 114 *error_code_out = reader.error_code(); |
| 116 if (error_msg_out) | 115 if (error_msg_out) |
| 117 *error_msg_out = reader.GetErrorMessage(); | 116 *error_msg_out = reader.GetErrorMessage(); |
| 118 | 117 |
| 119 return NULL; | 118 return NULL; |
| 120 } | 119 } |
| 121 | 120 |
| 122 /* static */ | 121 // static |
| 123 std::string JSONReader::ErrorCodeToString(JsonParseError error_code) { | 122 std::string JSONReader::ErrorCodeToString(JsonParseError error_code) { |
| 124 switch (error_code) { | 123 switch (error_code) { |
| 125 case JSON_NO_ERROR: | 124 case JSON_NO_ERROR: |
| 126 return std::string(); | 125 return std::string(); |
| 127 case JSON_BAD_ROOT_ELEMENT_TYPE: | 126 case JSON_BAD_ROOT_ELEMENT_TYPE: |
| 128 return kBadRootElementType; | 127 return kBadRootElementType; |
| 129 case JSON_INVALID_ESCAPE: | 128 case JSON_INVALID_ESCAPE: |
| 130 return kInvalidEscape; | 129 return kInvalidEscape; |
| 131 case JSON_SYNTAX_ERROR: | 130 case JSON_SYNTAX_ERROR: |
| 132 return kSyntaxError; | 131 return kSyntaxError; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 187 } | 186 } |
| 188 } | 187 } |
| 189 | 188 |
| 190 // Default to calling errors "syntax errors". | 189 // Default to calling errors "syntax errors". |
| 191 if (error_code_ == 0) | 190 if (error_code_ == 0) |
| 192 SetErrorCode(JSON_SYNTAX_ERROR, json_pos_); | 191 SetErrorCode(JSON_SYNTAX_ERROR, json_pos_); |
| 193 | 192 |
| 194 return NULL; | 193 return NULL; |
| 195 } | 194 } |
| 196 | 195 |
| 197 /* static */ | 196 // static |
| 198 std::string JSONReader::FormatErrorMessage(int line, int column, | 197 std::string JSONReader::FormatErrorMessage(int line, int column, |
| 199 const std::string& description) { | 198 const std::string& description) { |
| 200 if (line || column) { | 199 if (line || column) { |
| 201 return base::StringPrintf( | 200 return base::StringPrintf( |
| 202 "Line: %i, column: %i, %s", line, column, description.c_str()); | 201 "Line: %i, column: %i, %s", line, column, description.c_str()); |
| 203 } | 202 } |
| 204 return description; | 203 return description; |
| 205 } | 204 } |
| 206 | 205 |
| 207 Value* JSONReader::BuildValue(bool is_root) { | 206 Value* JSONReader::BuildValue(bool is_root) { |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 362 // We just grab the number here. We validate the size in DecodeNumber. | 361 // We just grab the number here. We validate the size in DecodeNumber. |
| 363 // According to RFC4627, a valid number is: [minus] int [frac] [exp] | 362 // According to RFC4627, a valid number is: [minus] int [frac] [exp] |
| 364 Token token(Token::NUMBER, json_pos_, 0); | 363 Token token(Token::NUMBER, json_pos_, 0); |
| 365 wchar_t c = *json_pos_; | 364 wchar_t c = *json_pos_; |
| 366 if ('-' == c) { | 365 if ('-' == c) { |
| 367 ++token.length; | 366 ++token.length; |
| 368 c = token.NextChar(); | 367 c = token.NextChar(); |
| 369 } | 368 } |
| 370 | 369 |
| 371 if (!ReadInt(token, false)) | 370 if (!ReadInt(token, false)) |
| 372 return kInvalidToken; | 371 return *kInvalidToken; |
| 373 | 372 |
| 374 // Optional fraction part | 373 // Optional fraction part |
| 375 c = token.NextChar(); | 374 c = token.NextChar(); |
| 376 if ('.' == c) { | 375 if ('.' == c) { |
| 377 ++token.length; | 376 ++token.length; |
| 378 if (!ReadInt(token, true)) | 377 if (!ReadInt(token, true)) |
| 379 return kInvalidToken; | 378 return *kInvalidToken; |
| 380 c = token.NextChar(); | 379 c = token.NextChar(); |
| 381 } | 380 } |
| 382 | 381 |
| 383 // Optional exponent part | 382 // Optional exponent part |
| 384 if ('e' == c || 'E' == c) { | 383 if ('e' == c || 'E' == c) { |
| 385 ++token.length; | 384 ++token.length; |
| 386 c = token.NextChar(); | 385 c = token.NextChar(); |
| 387 if ('-' == c || '+' == c) { | 386 if ('-' == c || '+' == c) { |
| 388 ++token.length; | 387 ++token.length; |
| 389 c = token.NextChar(); | 388 c = token.NextChar(); |
| 390 } | 389 } |
| 391 if (!ReadInt(token, true)) | 390 if (!ReadInt(token, true)) |
| 392 return kInvalidToken; | 391 return *kInvalidToken; |
| 393 } | 392 } |
| 394 | 393 |
| 395 return token; | 394 return token; |
| 396 } | 395 } |
| 397 | 396 |
| 398 Value* JSONReader::DecodeNumber(const Token& token) { | 397 Value* JSONReader::DecodeNumber(const Token& token) { |
| 399 const std::wstring num_string(token.begin, token.length); | 398 const std::wstring num_string(token.begin, token.length); |
| 400 | 399 |
| 401 int num_int; | 400 int num_int; |
| 402 if (StringToInt(WideToUTF8(num_string), &num_int)) | 401 if (StringToInt(WideToUTF8(num_string), &num_int)) |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 415 wchar_t c = token.NextChar(); | 414 wchar_t c = token.NextChar(); |
| 416 while ('\0' != c) { | 415 while ('\0' != c) { |
| 417 if ('\\' == c) { | 416 if ('\\' == c) { |
| 418 ++token.length; | 417 ++token.length; |
| 419 c = token.NextChar(); | 418 c = token.NextChar(); |
| 420 // Make sure the escaped char is valid. | 419 // Make sure the escaped char is valid. |
| 421 switch (c) { | 420 switch (c) { |
| 422 case 'x': | 421 case 'x': |
| 423 if (!ReadHexDigits(token, 2)) { | 422 if (!ReadHexDigits(token, 2)) { |
| 424 SetErrorCode(JSON_INVALID_ESCAPE, json_pos_ + token.length); | 423 SetErrorCode(JSON_INVALID_ESCAPE, json_pos_ + token.length); |
| 425 return kInvalidToken; | 424 return *kInvalidToken; |
| 426 } | 425 } |
| 427 break; | 426 break; |
| 428 case 'u': | 427 case 'u': |
| 429 if (!ReadHexDigits(token, 4)) { | 428 if (!ReadHexDigits(token, 4)) { |
| 430 SetErrorCode(JSON_INVALID_ESCAPE, json_pos_ + token.length); | 429 SetErrorCode(JSON_INVALID_ESCAPE, json_pos_ + token.length); |
| 431 return kInvalidToken; | 430 return *kInvalidToken; |
| 432 } | 431 } |
| 433 break; | 432 break; |
| 434 case '\\': | 433 case '\\': |
| 435 case '/': | 434 case '/': |
| 436 case 'b': | 435 case 'b': |
| 437 case 'f': | 436 case 'f': |
| 438 case 'n': | 437 case 'n': |
| 439 case 'r': | 438 case 'r': |
| 440 case 't': | 439 case 't': |
| 441 case 'v': | 440 case 'v': |
| 442 case '"': | 441 case '"': |
| 443 break; | 442 break; |
| 444 default: | 443 default: |
| 445 SetErrorCode(JSON_INVALID_ESCAPE, json_pos_ + token.length); | 444 SetErrorCode(JSON_INVALID_ESCAPE, json_pos_ + token.length); |
| 446 return kInvalidToken; | 445 return *kInvalidToken; |
| 447 } | 446 } |
| 448 } else if ('"' == c) { | 447 } else if ('"' == c) { |
| 449 ++token.length; | 448 ++token.length; |
| 450 return token; | 449 return token; |
| 451 } | 450 } |
| 452 ++token.length; | 451 ++token.length; |
| 453 c = token.NextChar(); | 452 c = token.NextChar(); |
| 454 } | 453 } |
| 455 return kInvalidToken; | 454 return *kInvalidToken; |
| 456 } | 455 } |
| 457 | 456 |
| 458 Value* JSONReader::DecodeString(const Token& token) { | 457 Value* JSONReader::DecodeString(const Token& token) { |
| 459 std::wstring decoded_str; | 458 std::wstring decoded_str; |
| 460 decoded_str.reserve(token.length - 2); | 459 decoded_str.reserve(token.length - 2); |
| 461 | 460 |
| 462 for (int i = 1; i < token.length - 1; ++i) { | 461 for (int i = 1; i < token.length - 1; ++i) { |
| 463 wchar_t c = *(token.begin + i); | 462 wchar_t c = *(token.begin + i); |
| 464 if ('\\' == c) { | 463 if ('\\' == c) { |
| 465 ++i; | 464 ++i; |
| (...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 663 ++column_number; | 662 ++column_number; |
| 664 } | 663 } |
| 665 } | 664 } |
| 666 | 665 |
| 667 error_line_ = line_number; | 666 error_line_ = line_number; |
| 668 error_col_ = column_number; | 667 error_col_ = column_number; |
| 669 error_code_ = error; | 668 error_code_ = error; |
| 670 } | 669 } |
| 671 | 670 |
| 672 } // namespace base | 671 } // namespace base |
| OLD | NEW |