dart-sdk/runtime/platform/json.cc
hausner@google.com e5cd233fd9 Fix json message in debugger wire protocol
Removed faulty comma in message. This hopefully solves issue 13792.

R=devoncarew@google.com

Review URL: https://codereview.chromium.org//59883019

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@30311 260f80e4-7a28-3924-810f-c04153c831b5
2013-11-15 19:14:47 +00:00

652 lines
15 KiB
C++

// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/json.h"
#include "platform/assert.h"
#include "platform/globals.h"
#include "platform/utils.h"
#include "vm/os.h"
namespace dart {
JSONScanner::JSONScanner(const char* json_text) {
SetText(json_text);
}
void JSONScanner::SetText(const char* json_text) {
current_pos_ = json_text;
token_start_ = json_text;
token_length_ = 0;
token_ = TokenIllegal;
}
void JSONScanner::Recognize(Token t) {
++current_pos_;
token_ = t;
}
bool JSONScanner::IsLetter(char ch) const {
return (('A' <= ch) && (ch <= 'Z')) || (('a' <= ch) && (ch <= 'z'));
}
bool JSONScanner::IsDigit(char ch) const {
return ('0' <= ch) && (ch <= '9');
}
bool JSONScanner::IsLiteral(const char* literal) {
int i = 0;
while ((literal[i] != '\0') && (current_pos_[i] == literal[i])) {
i++;
}
if ((literal[i] == '\0') && !IsLetter(current_pos_[i])) {
current_pos_ += i;
return true;
}
return false;
}
bool JSONScanner::IsStringLiteral(const char* literal) const {
if (token_ != TokenString) {
return false;
}
int i = 0;
while ((i < token_length_) && (token_start_[i] == literal[i])) {
i++;
}
return (i == token_length_) && (literal[i] == '\0');
}
void JSONScanner::Skip(Token matching_token) {
while (!EOM() && (token_ != TokenIllegal)) {
Scan();
if (token_ == TokenLBrace) {
Skip(TokenRBrace);
} else if (token_ == TokenLBrack) {
Skip(TokenRBrack);
} else if (token_ == matching_token) {
return;
} else if ((token_ == TokenRBrace) || (token_ == TokenRBrack)) {
// Mismatched brace or bracket.
token_ = TokenIllegal;
}
}
}
void JSONScanner::ScanString() {
ASSERT(*current_pos_ == '"');
++current_pos_;
token_start_ = current_pos_;
while (*current_pos_ != '"') {
if (*current_pos_ == '\0') {
token_length_ = 0;
token_ = TokenIllegal;
return;
} else if (*current_pos_ == '\\') {
++current_pos_;
if (*current_pos_ == '"') {
// Consume escaped double quote.
++current_pos_;
}
} else {
++current_pos_;
}
}
token_ = TokenString;
token_length_ = current_pos_ - token_start_;
++current_pos_;
}
void JSONScanner::ScanNumber() {
if (*current_pos_ == '-') {
++current_pos_;
}
if (!IsDigit(*current_pos_)) {
token_ = TokenIllegal;
token_length_ = 0;
return;
}
while (IsDigit(*current_pos_)) {
++current_pos_;
}
if ((*current_pos_ == '.') ||
(*current_pos_ == 'e') ||
(*current_pos_ == 'E')) {
// Floating point numbers not supported.
token_ = TokenIllegal;
token_length_ = 0;
return;
}
token_ = TokenInteger;
token_length_ = current_pos_ - token_start_;
}
void JSONScanner::Scan() {
while ((*current_pos_ == ' ') ||
(*current_pos_ == '\t') ||
(*current_pos_ == '\n')) {
++current_pos_;
}
token_start_ = current_pos_;
if (*current_pos_ == '\0') {
token_length_ = 0;
token_ = TokenEOM;
return;
}
switch (*current_pos_) {
case '{':
Recognize(TokenLBrace);
break;
case '}':
Recognize(TokenRBrace);
break;
case '[':
Recognize(TokenLBrack);
break;
case ']':
Recognize(TokenRBrack);
break;
case ':':
Recognize(TokenColon);
break;
case ',':
Recognize(TokenComma);
break;
case '"':
ScanString();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
ScanNumber();
break;
default:
if (IsLiteral("true")) {
token_ = TokenTrue;
token_length_ = 4;
} else if (IsLiteral("false")) {
token_ = TokenFalse;
token_length_ = 5;
} else if (IsLiteral("null")) {
token_ = TokenNull;
token_length_ = 4;
} else {
token_length_ = 0;
token_ = TokenIllegal;
}
}
}
JSONReader::JSONReader(const char* json_object)
: scanner_(json_object) {
Set(json_object);
}
void JSONReader::Set(const char* json_object) {
scanner_.SetText(json_object);
json_object_ = json_object;
error_ = false;
}
bool JSONReader::CheckMessage() {
scanner_.SetText(json_object_);
scanner_.Scan();
CheckObject();
return true;
}
void JSONReader::CheckValue() {
switch (scanner_.CurrentToken()) {
case JSONScanner::TokenLBrace:
CheckObject();
break;
case JSONScanner::TokenLBrack:
CheckArray();
break;
case JSONScanner::TokenString: {
// Check the encoding.
const char* s = ValueChars();
int remaining = ValueLen();
while (remaining > 0) {
if ((*s == '\n') || (*s == '\t')) {
OS::Print("Un-escaped character in JSON string: '%s'\n",
ValueChars());
FATAL("illegal character in JSON string value");
}
s++;
remaining--;
}
scanner_.Scan();
break;
}
case JSONScanner::TokenInteger:
case JSONScanner::TokenTrue:
case JSONScanner::TokenFalse:
case JSONScanner::TokenNull:
scanner_.Scan();
break;
default:
OS::Print("Malformed JSON: expected a value but got '%s'\n",
scanner_.TokenChars());
FATAL("illegal JSON value found");
}
}
#if defined (DEBUG)
#define CHECK_TOKEN(token) \
if (scanner_.CurrentToken() != token) { \
OS::Print("Malformed JSON: expected %s but got '%s'\n", \
#token, scanner_.TokenChars()); \
intptr_t offset = scanner_.TokenChars() - this->json_object_; \
OS::Print("Malformed JSON: expected %s at offset %" Pd "of buffer:\n%s\n", \
#token, offset, this->json_object_); \
ASSERT(scanner_.CurrentToken() == token); \
}
#else
#define CHECK_TOKEN(token)
#endif
void JSONReader::CheckArray() {
CHECK_TOKEN(JSONScanner::TokenLBrack);
scanner_.Scan();
while (scanner_.CurrentToken() != JSONScanner::TokenRBrack) {
CheckValue();
if (scanner_.CurrentToken() != JSONScanner::TokenComma) {
break;
}
scanner_.Scan();
}
CHECK_TOKEN(JSONScanner::TokenRBrack);
scanner_.Scan();
}
void JSONReader::CheckObject() {
CHECK_TOKEN(JSONScanner::TokenLBrace);
scanner_.Scan();
while (scanner_.CurrentToken() == JSONScanner::TokenString) {
scanner_.Scan();
CHECK_TOKEN(JSONScanner::TokenColon);
scanner_.Scan();
CheckValue();
if (scanner_.CurrentToken() != JSONScanner::TokenComma) {
break;
}
scanner_.Scan();
}
CHECK_TOKEN(JSONScanner::TokenRBrace);
scanner_.Scan();
}
#undef CHECK_TOKEN
bool JSONReader::Seek(const char* name) {
error_ = false;
scanner_.SetText(json_object_);
scanner_.Scan();
if (scanner_.CurrentToken() != JSONScanner::TokenLBrace) {
error_ = true;
return false;
}
scanner_.Scan();
if (scanner_.CurrentToken() == JSONScanner::TokenRBrace) {
return false;
}
while (scanner_.CurrentToken() == JSONScanner::TokenString) {
bool found = scanner_.IsStringLiteral(name);
scanner_.Scan();
if (scanner_.CurrentToken() != JSONScanner::TokenColon) {
error_ = true;
return false;
}
scanner_.Scan();
switch (scanner_.CurrentToken()) {
case JSONScanner::TokenString:
case JSONScanner::TokenInteger:
case JSONScanner::TokenLBrace:
case JSONScanner::TokenLBrack:
case JSONScanner::TokenTrue:
case JSONScanner::TokenFalse:
case JSONScanner::TokenNull:
// Found a legal value.
if (found) {
return true;
}
break;
default:
error_ = true;
return false;
}
// Skip the value.
if (scanner_.CurrentToken() == JSONScanner::TokenLBrace) {
scanner_.Skip(JSONScanner::TokenRBrace);
if (scanner_.CurrentToken() != JSONScanner::TokenRBrace) {
error_ = true;
return false;
}
} else if (scanner_.CurrentToken() == JSONScanner::TokenLBrack) {
scanner_.Skip(JSONScanner::TokenRBrack);
if (scanner_.CurrentToken() != JSONScanner::TokenRBrack) {
error_ = true;
return false;
}
}
scanner_.Scan(); // Value or closing brace or bracket.
if (scanner_.CurrentToken() == JSONScanner::TokenComma) {
scanner_.Scan();
} else if (scanner_.CurrentToken() == JSONScanner::TokenRBrace) {
return false;
} else {
error_ = true;
return false;
}
}
error_ = true;
return false;
}
const char* JSONReader::EndOfObject() {
bool found = Seek("***"); // Look for illegally named value.
ASSERT(!found);
if (!found && !error_) {
const char* s = scanner_.TokenChars();
ASSERT(*s == '}');
return s;
}
return NULL;
}
JSONReader::JSONType JSONReader::Type() const {
if (error_) {
return kNone;
}
switch (scanner_.CurrentToken()) {
case JSONScanner::TokenString:
return kString;
case JSONScanner::TokenInteger:
return kInteger;
case JSONScanner::TokenLBrace:
return kObject;
case JSONScanner::TokenLBrack:
return kArray;
case JSONScanner::TokenTrue:
case JSONScanner::TokenFalse:
case JSONScanner::TokenNull:
return kLiteral;
default:
return kNone;
}
}
void JSONReader::GetRawValueChars(char* buf, intptr_t buflen) const {
if (Type() == kNone) {
return;
}
intptr_t max = buflen - 1;
if (ValueLen() < max) {
max = ValueLen();
}
const char* val = ValueChars();
intptr_t i = 0;
for (; i < max; i++) {
buf[i] = val[i];
}
buf[i] = '\0';
}
void JSONReader::GetDecodedValueChars(char* buf, intptr_t buflen) const {
if (Type() == kNone) {
return;
}
const intptr_t last_idx = buflen - 1;
const intptr_t value_len = ValueLen();
const char* val = ValueChars();
intptr_t buf_idx = 0;
intptr_t val_idx = 0;
while ((buf_idx < last_idx) && (val_idx < value_len)) {
char ch = val[val_idx];
val_idx++;
buf[buf_idx] = ch;
if ((ch == '\\') && (val_idx < value_len)) {
switch (val[val_idx]) {
case '"':
case '\\':
case '/':
buf[buf_idx] = val[val_idx];
val_idx++;
break;
case 'b':
buf[buf_idx] = '\b';
val_idx++;
break;
case 'f':
buf[buf_idx] = '\f';
val_idx++;
break;
case 'n':
buf[buf_idx] = '\n';
val_idx++;
break;
case 'r':
buf[buf_idx] = '\r';
val_idx++;
break;
case 't':
buf[buf_idx] = '\t';
val_idx++;
break;
case 'u':
// \u00XX
// If the value is malformed or > 255, ignore and copy the
// encoded characters.
if ((val_idx < value_len - 4) &&
(val[val_idx + 1] == '0') && (val[val_idx + 2] == '0') &&
Utils::IsHexDigit(val[val_idx + 3]) &&
Utils::IsHexDigit(val[val_idx + 4])) {
buf[buf_idx] = 16 * Utils::HexDigitToInt(val[val_idx + 3]) +
Utils::HexDigitToInt(val[val_idx + 4]);
val_idx += 5;
}
break;
default:
// Nothing. Copy the character after the backslash
// in the next loop iteration.
break;
}
}
buf_idx++;
}
buf[buf_idx] = '\0';
}
TextBuffer::TextBuffer(intptr_t buf_size) {
ASSERT(buf_size > 0);
buf_ = reinterpret_cast<char*>(malloc(buf_size));
buf_size_ = buf_size;
Clear();
}
TextBuffer::~TextBuffer() {
free(buf_);
buf_ = NULL;
}
void TextBuffer::Clear() {
msg_len_ = 0;
buf_[0] = '\0';
}
void TextBuffer::AddChar(char ch) {
EnsureCapacity(sizeof(ch));
buf_[msg_len_] = ch;
msg_len_++;
buf_[msg_len_] = '\0';
}
void TextBuffer::AddUTF8(uint32_t ch) {
static const uint32_t kMaxOneByteChar = 0x7F;
static const uint32_t kMaxTwoByteChar = 0x7FF;
static const uint32_t kMaxThreeByteChar = 0xFFFF;
static const uint32_t kMaxFourByteChar = 0x10FFFF;
static const uint32_t kMask = ~(1 << 6);
if (ch <= kMaxOneByteChar) {
EnsureCapacity(1);
buf_[msg_len_++] = ch;
buf_[msg_len_] = '\0';
return;
}
if (ch <= kMaxTwoByteChar) {
EnsureCapacity(2);
buf_[msg_len_++] = 0xC0 | (ch >> 6);
buf_[msg_len_++] = 0x80 | (ch & kMask);
buf_[msg_len_] = '\0';
return;
}
if (ch <= kMaxThreeByteChar) {
EnsureCapacity(3);
buf_[msg_len_++] = 0xE0 | (ch >> 12);
buf_[msg_len_++] = 0x80 | ((ch >> 6) & kMask);
buf_[msg_len_++] = 0x80 | (ch & kMask);
buf_[msg_len_] = '\0';
return;
}
ASSERT(ch <= kMaxFourByteChar);
EnsureCapacity(4);
buf_[msg_len_++] = 0xF0 | (ch >> 18);
buf_[msg_len_++] = 0x80 | ((ch >> 12) & kMask);
buf_[msg_len_++] = 0x80 | ((ch >> 6) & kMask);
buf_[msg_len_++] = 0x80 | (ch & kMask);
buf_[msg_len_] = '\0';
}
intptr_t TextBuffer::Printf(const char* format, ...) {
va_list args;
va_start(args, format);
intptr_t remaining = buf_size_ - msg_len_;
ASSERT(remaining >= 0);
intptr_t len = OS::VSNPrint(buf_ + msg_len_, remaining, format, args);
va_end(args);
if (len >= remaining) {
EnsureCapacity(len);
remaining = buf_size_ - msg_len_;
ASSERT(remaining > len);
va_list args2;
va_start(args2, format);
intptr_t len2 = OS::VSNPrint(buf_ + msg_len_, remaining, format, args2);
va_end(args2);
ASSERT(len == len2);
}
msg_len_ += len;
buf_[msg_len_] = '\0';
return len;
}
void TextBuffer::AddEscapedChar(uint32_t cp) {
switch (cp) {
case '"':
Printf("%s", "\\\"");
break;
case '\\':
Printf("%s", "\\\\");
break;
case '/':
Printf("%s", "\\/");
break;
case '\b':
Printf("%s", "\\b");
break;
case '\f':
Printf("%s", "\\f");
break;
case '\n':
Printf("%s", "\\n");
break;
case '\r':
Printf("%s", "\\r");
break;
case '\t':
Printf("%s", "\\t");
break;
default:
if (cp < 0x20) {
// Encode character as \u00HH.
uint32_t digit2 = (cp >> 4) & 0xf;
uint32_t digit3 = (cp & 0xf);
Printf("\\u00%c%c",
digit2 > 9 ? 'A' + (digit2 - 10) : '0' + digit2,
digit3 > 9 ? 'A' + (digit3 - 10) : '0' + digit3);
} else {
AddUTF8(cp);
}
}
}
void TextBuffer::AddString(const char* s) {
Printf("%s", s);
}
void TextBuffer::AddEscapedString(const char* s) {
intptr_t len = strlen(s);
for (int i = 0; i < len; i++) {
AddEscapedChar(s[i]);
}
}
void TextBuffer::EnsureCapacity(intptr_t len) {
intptr_t remaining = buf_size_ - msg_len_;
if (remaining <= len) {
const int kBufferSpareCapacity = 64; // Somewhat arbitrary.
// TODO(turnidge): do we need to guard against overflow or other
// security issues here? Text buffers are used by the debugger
// to send user-controlled data (e.g. values of string variables) to
// the debugger front-end.
intptr_t new_size = buf_size_ + len + kBufferSpareCapacity;
char* new_buf = reinterpret_cast<char*>(realloc(buf_, new_size));
ASSERT(new_buf != NULL);
buf_ = new_buf;
buf_size_ = new_size;
}
}
} // namespace dart