Bug 512266 - JSON.stringify for various special characters should produce the corresponding one-character escapes. r=pbiggar

--HG--
extra : rebase_source : a76fb038143bd053ee9e30ae29e99d3c2e91c528
This commit is contained in:
Jeff Walden 2011-03-18 17:57:18 -07:00
parent 2ff8950407
commit 4b6928b810
3 changed files with 296 additions and 53 deletions

View File

@ -181,47 +181,75 @@ js_TryJSON(JSContext *cx, Value *vp)
}
static const char quote = '\"';
static const char backslash = '\\';
static const char unicodeEscape[] = "\\u00";
static JSBool
write_string(JSContext *cx, StringBuffer &sb, const jschar *buf, uint32 len)
static inline bool IsQuoteSpecialCharacter(jschar c)
{
if (!sb.append(quote))
return JS_FALSE;
JS_STATIC_ASSERT('\b' < ' ');
JS_STATIC_ASSERT('\f' < ' ');
JS_STATIC_ASSERT('\n' < ' ');
JS_STATIC_ASSERT('\r' < ' ');
JS_STATIC_ASSERT('\t' < ' ');
return c == '"' || c == '\\' || c < ' ';
}
uint32 mark = 0;
uint32 i;
for (i = 0; i < len; ++i) {
if (buf[i] == quote || buf[i] == backslash) {
if (!sb.append(&buf[mark], i - mark) || !sb.append(backslash) ||
!sb.append(buf[i])) {
return JS_FALSE;
}
mark = i + 1;
} else if (buf[i] <= 31 || buf[i] == 127) {
if (!sb.append(&buf[mark], i - mark) ||
!sb.append(unicodeEscape)) {
return JS_FALSE;
}
char ubuf[3];
size_t len = JS_snprintf(ubuf, sizeof(ubuf), "%.2x", buf[i]);
JS_ASSERT(len == 2);
jschar wbuf[3];
size_t wbufSize = JS_ARRAY_LENGTH(wbuf);
if (!js_InflateStringToBuffer(cx, ubuf, len, wbuf, &wbufSize) ||
!sb.append(wbuf, wbufSize)) {
return JS_FALSE;
}
mark = i + 1;
/* ES5 15.12.3 Quote. */
static bool
Quote(JSContext *cx, StringBuffer &sb, JSString *str)
{
JS::Anchor<JSString *> anchor(str);
size_t len = str->length();
const jschar *buf = str->getChars(cx);
if (!buf)
return false;
/* Step 1. */
if (!sb.append('"'))
return false;
/* Step 2. */
for (size_t i = 0; i < len; ++i) {
/* Batch-append maximal character sequences containing no escapes. */
size_t mark = i;
do {
if (IsQuoteSpecialCharacter(buf[i]))
break;
} while (++i < len);
if (i > mark) {
if (!sb.append(&buf[mark], i - mark))
return false;
if (i == len)
break;
}
jschar c = buf[i];
if (c == '"' || c == '\\') {
if (!sb.append('\\') || !sb.append(c))
return false;
} else if (c == '\b' || c == '\f' || c == '\n' || c == '\r' || c == '\t') {
jschar abbrev = (c == '\b')
? 'b'
: (c == '\f')
? 'f'
: (c == '\n')
? 'n'
: (c == '\r')
? 'r'
: 't';
if (!sb.append('\\') || !sb.append(abbrev))
return false;
mark = i + 1;
} else {
JS_ASSERT(c < ' ');
if (!sb.append("\\u00"))
return false;
JS_ASSERT((c >> 4) < 10);
uint8 x = c >> 4, y = c % 16;
if (!sb.append('0' + x) || !sb.append(y < 10 ? '0' + y : 'a' + (y - 10)))
return false;
}
}
if (mark < len && !sb.append(&buf[mark], len - mark))
return JS_FALSE;
return sb.append(quote);
/* Steps 3-4. */
return sb.append('"');
}
class StringifyContext
@ -493,18 +521,11 @@ JO(JSContext *cx, JSObject *obj, StringifyContext *scx)
if (!WriteIndent(cx, scx, scx->depth))
return JS_FALSE;
// Be careful below: this string is weakly rooted!
JSString *s = js_ValueToString(cx, IdToValue(id));
JSString *s = IdToString(cx, id);
if (!s)
return JS_FALSE;
JS::Anchor<JSString *> anchor(s);
size_t length = s->length();
const jschar *chars = s->getChars(cx);
if (!chars)
return JS_FALSE;
if (!write_string(cx, scx->sb, chars, length) ||
if (!Quote(cx, scx->sb, s) ||
!scx->sb.append(':') ||
!(scx->gap.empty() || scx->sb.append(' ')) ||
!Str(cx, outputValue, scx)) {
@ -613,14 +634,8 @@ Str(JSContext *cx, const Value &v, StringifyContext *scx)
*/
/* Step 8. */
if (v.isString()) {
JSString *str = v.toString();
size_t length = str->length();
const jschar *chars = str->getChars(cx);
if (!chars)
return false;
return write_string(cx, scx->sb, chars, length);
}
if (v.isString())
return Quote(cx, scx->sb, v.toString());
/* Step 5. */
if (v.isNull())

View File

@ -17,6 +17,7 @@ script stringify-ignore-noncallable-toJSON.js
script stringify-primitives.js
script stringify-replacer.js
script stringify-replacer-with-array-indexes.js
script stringify-special-escapes.js
script stringify-toJSON-arguments.js
script trailing-comma.js

View File

@ -0,0 +1,227 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
var gTestfile = 'stringify-special-escapes.js';
//-----------------------------------------------------------------------------
var BUGNUMBER = 512266;
var summary =
"JSON.stringify of \\b\\f\\n\\r\\t should use one-character escapes, not hex";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
assertEq(JSON.stringify("\u0000"), '"\\u0000"');
assertEq(JSON.stringify("\u0001"), '"\\u0001"');
assertEq(JSON.stringify("\u0002"), '"\\u0002"');
assertEq(JSON.stringify("\u0003"), '"\\u0003"');
assertEq(JSON.stringify("\u0004"), '"\\u0004"');
assertEq(JSON.stringify("\u0005"), '"\\u0005"');
assertEq(JSON.stringify("\u0006"), '"\\u0006"');
assertEq(JSON.stringify("\u0007"), '"\\u0007"');
assertEq(JSON.stringify("\u0008"), '"\\b"');
assertEq(JSON.stringify("\u0009"), '"\\t"');
assertEq(JSON.stringify("\u000A"), '"\\n"');
assertEq(JSON.stringify("\u000B"), '"\\u000b"');
assertEq(JSON.stringify("\u000C"), '"\\f"');
assertEq(JSON.stringify("\u000D"), '"\\r"');
assertEq(JSON.stringify("\u000E"), '"\\u000e"');
assertEq(JSON.stringify("\u000F"), '"\\u000f"');
assertEq(JSON.stringify("\u0010"), '"\\u0010"');
assertEq(JSON.stringify("\u0011"), '"\\u0011"');
assertEq(JSON.stringify("\u0012"), '"\\u0012"');
assertEq(JSON.stringify("\u0013"), '"\\u0013"');
assertEq(JSON.stringify("\u0014"), '"\\u0014"');
assertEq(JSON.stringify("\u0015"), '"\\u0015"');
assertEq(JSON.stringify("\u0016"), '"\\u0016"');
assertEq(JSON.stringify("\u0017"), '"\\u0017"');
assertEq(JSON.stringify("\u0018"), '"\\u0018"');
assertEq(JSON.stringify("\u0019"), '"\\u0019"');
assertEq(JSON.stringify("\u001A"), '"\\u001a"');
assertEq(JSON.stringify("\u001B"), '"\\u001b"');
assertEq(JSON.stringify("\u001C"), '"\\u001c"');
assertEq(JSON.stringify("\u001D"), '"\\u001d"');
assertEq(JSON.stringify("\u001E"), '"\\u001e"');
assertEq(JSON.stringify("\u001F"), '"\\u001f"');
assertEq(JSON.stringify("\u0020"), '" "');
assertEq(JSON.stringify("\\u0000"), '"\\\\u0000"');
assertEq(JSON.stringify("\\u0001"), '"\\\\u0001"');
assertEq(JSON.stringify("\\u0002"), '"\\\\u0002"');
assertEq(JSON.stringify("\\u0003"), '"\\\\u0003"');
assertEq(JSON.stringify("\\u0004"), '"\\\\u0004"');
assertEq(JSON.stringify("\\u0005"), '"\\\\u0005"');
assertEq(JSON.stringify("\\u0006"), '"\\\\u0006"');
assertEq(JSON.stringify("\\u0007"), '"\\\\u0007"');
assertEq(JSON.stringify("\\u0008"), '"\\\\u0008"');
assertEq(JSON.stringify("\\u0009"), '"\\\\u0009"');
assertEq(JSON.stringify("\\u000A"), '"\\\\u000A"');
assertEq(JSON.stringify("\\u000B"), '"\\\\u000B"');
assertEq(JSON.stringify("\\u000C"), '"\\\\u000C"');
assertEq(JSON.stringify("\\u000D"), '"\\\\u000D"');
assertEq(JSON.stringify("\\u000E"), '"\\\\u000E"');
assertEq(JSON.stringify("\\u000F"), '"\\\\u000F"');
assertEq(JSON.stringify("\\u0010"), '"\\\\u0010"');
assertEq(JSON.stringify("\\u0011"), '"\\\\u0011"');
assertEq(JSON.stringify("\\u0012"), '"\\\\u0012"');
assertEq(JSON.stringify("\\u0013"), '"\\\\u0013"');
assertEq(JSON.stringify("\\u0014"), '"\\\\u0014"');
assertEq(JSON.stringify("\\u0015"), '"\\\\u0015"');
assertEq(JSON.stringify("\\u0016"), '"\\\\u0016"');
assertEq(JSON.stringify("\\u0017"), '"\\\\u0017"');
assertEq(JSON.stringify("\\u0018"), '"\\\\u0018"');
assertEq(JSON.stringify("\\u0019"), '"\\\\u0019"');
assertEq(JSON.stringify("\\u001A"), '"\\\\u001A"');
assertEq(JSON.stringify("\\u001B"), '"\\\\u001B"');
assertEq(JSON.stringify("\\u001C"), '"\\\\u001C"');
assertEq(JSON.stringify("\\u001D"), '"\\\\u001D"');
assertEq(JSON.stringify("\\u001E"), '"\\\\u001E"');
assertEq(JSON.stringify("\\u001F"), '"\\\\u001F"');
assertEq(JSON.stringify("\\u0020"), '"\\\\u0020"');
assertEq(JSON.stringify("a\u0000"), '"a\\u0000"');
assertEq(JSON.stringify("a\u0001"), '"a\\u0001"');
assertEq(JSON.stringify("a\u0002"), '"a\\u0002"');
assertEq(JSON.stringify("a\u0003"), '"a\\u0003"');
assertEq(JSON.stringify("a\u0004"), '"a\\u0004"');
assertEq(JSON.stringify("a\u0005"), '"a\\u0005"');
assertEq(JSON.stringify("a\u0006"), '"a\\u0006"');
assertEq(JSON.stringify("a\u0007"), '"a\\u0007"');
assertEq(JSON.stringify("a\u0008"), '"a\\b"');
assertEq(JSON.stringify("a\u0009"), '"a\\t"');
assertEq(JSON.stringify("a\u000A"), '"a\\n"');
assertEq(JSON.stringify("a\u000B"), '"a\\u000b"');
assertEq(JSON.stringify("a\u000C"), '"a\\f"');
assertEq(JSON.stringify("a\u000D"), '"a\\r"');
assertEq(JSON.stringify("a\u000E"), '"a\\u000e"');
assertEq(JSON.stringify("a\u000F"), '"a\\u000f"');
assertEq(JSON.stringify("a\u0010"), '"a\\u0010"');
assertEq(JSON.stringify("a\u0011"), '"a\\u0011"');
assertEq(JSON.stringify("a\u0012"), '"a\\u0012"');
assertEq(JSON.stringify("a\u0013"), '"a\\u0013"');
assertEq(JSON.stringify("a\u0014"), '"a\\u0014"');
assertEq(JSON.stringify("a\u0015"), '"a\\u0015"');
assertEq(JSON.stringify("a\u0016"), '"a\\u0016"');
assertEq(JSON.stringify("a\u0017"), '"a\\u0017"');
assertEq(JSON.stringify("a\u0018"), '"a\\u0018"');
assertEq(JSON.stringify("a\u0019"), '"a\\u0019"');
assertEq(JSON.stringify("a\u001A"), '"a\\u001a"');
assertEq(JSON.stringify("a\u001B"), '"a\\u001b"');
assertEq(JSON.stringify("a\u001C"), '"a\\u001c"');
assertEq(JSON.stringify("a\u001D"), '"a\\u001d"');
assertEq(JSON.stringify("a\u001E"), '"a\\u001e"');
assertEq(JSON.stringify("a\u001F"), '"a\\u001f"');
assertEq(JSON.stringify("a\u0020"), '"a "');
assertEq(JSON.stringify("a\\u0000"), '"a\\\\u0000"');
assertEq(JSON.stringify("a\\u0001"), '"a\\\\u0001"');
assertEq(JSON.stringify("a\\u0002"), '"a\\\\u0002"');
assertEq(JSON.stringify("a\\u0003"), '"a\\\\u0003"');
assertEq(JSON.stringify("a\\u0004"), '"a\\\\u0004"');
assertEq(JSON.stringify("a\\u0005"), '"a\\\\u0005"');
assertEq(JSON.stringify("a\\u0006"), '"a\\\\u0006"');
assertEq(JSON.stringify("a\\u0007"), '"a\\\\u0007"');
assertEq(JSON.stringify("a\\u0008"), '"a\\\\u0008"');
assertEq(JSON.stringify("a\\u0009"), '"a\\\\u0009"');
assertEq(JSON.stringify("a\\u000A"), '"a\\\\u000A"');
assertEq(JSON.stringify("a\\u000B"), '"a\\\\u000B"');
assertEq(JSON.stringify("a\\u000C"), '"a\\\\u000C"');
assertEq(JSON.stringify("a\\u000D"), '"a\\\\u000D"');
assertEq(JSON.stringify("a\\u000E"), '"a\\\\u000E"');
assertEq(JSON.stringify("a\\u000F"), '"a\\\\u000F"');
assertEq(JSON.stringify("a\\u0010"), '"a\\\\u0010"');
assertEq(JSON.stringify("a\\u0011"), '"a\\\\u0011"');
assertEq(JSON.stringify("a\\u0012"), '"a\\\\u0012"');
assertEq(JSON.stringify("a\\u0013"), '"a\\\\u0013"');
assertEq(JSON.stringify("a\\u0014"), '"a\\\\u0014"');
assertEq(JSON.stringify("a\\u0015"), '"a\\\\u0015"');
assertEq(JSON.stringify("a\\u0016"), '"a\\\\u0016"');
assertEq(JSON.stringify("a\\u0017"), '"a\\\\u0017"');
assertEq(JSON.stringify("a\\u0018"), '"a\\\\u0018"');
assertEq(JSON.stringify("a\\u0019"), '"a\\\\u0019"');
assertEq(JSON.stringify("a\\u001A"), '"a\\\\u001A"');
assertEq(JSON.stringify("a\\u001B"), '"a\\\\u001B"');
assertEq(JSON.stringify("a\\u001C"), '"a\\\\u001C"');
assertEq(JSON.stringify("a\\u001D"), '"a\\\\u001D"');
assertEq(JSON.stringify("a\\u001E"), '"a\\\\u001E"');
assertEq(JSON.stringify("a\\u001F"), '"a\\\\u001F"');
assertEq(JSON.stringify("a\\u0020"), '"a\\\\u0020"');
assertEq(JSON.stringify("\u0000Q"), '"\\u0000Q"');
assertEq(JSON.stringify("\u0001Q"), '"\\u0001Q"');
assertEq(JSON.stringify("\u0002Q"), '"\\u0002Q"');
assertEq(JSON.stringify("\u0003Q"), '"\\u0003Q"');
assertEq(JSON.stringify("\u0004Q"), '"\\u0004Q"');
assertEq(JSON.stringify("\u0005Q"), '"\\u0005Q"');
assertEq(JSON.stringify("\u0006Q"), '"\\u0006Q"');
assertEq(JSON.stringify("\u0007Q"), '"\\u0007Q"');
assertEq(JSON.stringify("\u0008Q"), '"\\bQ"');
assertEq(JSON.stringify("\u0009Q"), '"\\tQ"');
assertEq(JSON.stringify("\u000AQ"), '"\\nQ"');
assertEq(JSON.stringify("\u000BQ"), '"\\u000bQ"');
assertEq(JSON.stringify("\u000CQ"), '"\\fQ"');
assertEq(JSON.stringify("\u000DQ"), '"\\rQ"');
assertEq(JSON.stringify("\u000EQ"), '"\\u000eQ"');
assertEq(JSON.stringify("\u000FQ"), '"\\u000fQ"');
assertEq(JSON.stringify("\u0010Q"), '"\\u0010Q"');
assertEq(JSON.stringify("\u0011Q"), '"\\u0011Q"');
assertEq(JSON.stringify("\u0012Q"), '"\\u0012Q"');
assertEq(JSON.stringify("\u0013Q"), '"\\u0013Q"');
assertEq(JSON.stringify("\u0014Q"), '"\\u0014Q"');
assertEq(JSON.stringify("\u0015Q"), '"\\u0015Q"');
assertEq(JSON.stringify("\u0016Q"), '"\\u0016Q"');
assertEq(JSON.stringify("\u0017Q"), '"\\u0017Q"');
assertEq(JSON.stringify("\u0018Q"), '"\\u0018Q"');
assertEq(JSON.stringify("\u0019Q"), '"\\u0019Q"');
assertEq(JSON.stringify("\u001AQ"), '"\\u001aQ"');
assertEq(JSON.stringify("\u001BQ"), '"\\u001bQ"');
assertEq(JSON.stringify("\u001CQ"), '"\\u001cQ"');
assertEq(JSON.stringify("\u001DQ"), '"\\u001dQ"');
assertEq(JSON.stringify("\u001EQ"), '"\\u001eQ"');
assertEq(JSON.stringify("\u001FQ"), '"\\u001fQ"');
assertEq(JSON.stringify("\u0020Q"), '" Q"');
assertEq(JSON.stringify("\\u0000Q"), '"\\\\u0000Q"');
assertEq(JSON.stringify("\\u0001Q"), '"\\\\u0001Q"');
assertEq(JSON.stringify("\\u0002Q"), '"\\\\u0002Q"');
assertEq(JSON.stringify("\\u0003Q"), '"\\\\u0003Q"');
assertEq(JSON.stringify("\\u0004Q"), '"\\\\u0004Q"');
assertEq(JSON.stringify("\\u0005Q"), '"\\\\u0005Q"');
assertEq(JSON.stringify("\\u0006Q"), '"\\\\u0006Q"');
assertEq(JSON.stringify("\\u0007Q"), '"\\\\u0007Q"');
assertEq(JSON.stringify("\\u0008Q"), '"\\\\u0008Q"');
assertEq(JSON.stringify("\\u0009Q"), '"\\\\u0009Q"');
assertEq(JSON.stringify("\\u000AQ"), '"\\\\u000AQ"');
assertEq(JSON.stringify("\\u000BQ"), '"\\\\u000BQ"');
assertEq(JSON.stringify("\\u000CQ"), '"\\\\u000CQ"');
assertEq(JSON.stringify("\\u000DQ"), '"\\\\u000DQ"');
assertEq(JSON.stringify("\\u000EQ"), '"\\\\u000EQ"');
assertEq(JSON.stringify("\\u000FQ"), '"\\\\u000FQ"');
assertEq(JSON.stringify("\\u0010Q"), '"\\\\u0010Q"');
assertEq(JSON.stringify("\\u0011Q"), '"\\\\u0011Q"');
assertEq(JSON.stringify("\\u0012Q"), '"\\\\u0012Q"');
assertEq(JSON.stringify("\\u0013Q"), '"\\\\u0013Q"');
assertEq(JSON.stringify("\\u0014Q"), '"\\\\u0014Q"');
assertEq(JSON.stringify("\\u0015Q"), '"\\\\u0015Q"');
assertEq(JSON.stringify("\\u0016Q"), '"\\\\u0016Q"');
assertEq(JSON.stringify("\\u0017Q"), '"\\\\u0017Q"');
assertEq(JSON.stringify("\\u0018Q"), '"\\\\u0018Q"');
assertEq(JSON.stringify("\\u0019Q"), '"\\\\u0019Q"');
assertEq(JSON.stringify("\\u001AQ"), '"\\\\u001AQ"');
assertEq(JSON.stringify("\\u001BQ"), '"\\\\u001BQ"');
assertEq(JSON.stringify("\\u001CQ"), '"\\\\u001CQ"');
assertEq(JSON.stringify("\\u001DQ"), '"\\\\u001DQ"');
assertEq(JSON.stringify("\\u001EQ"), '"\\\\u001EQ"');
assertEq(JSON.stringify("\\u001FQ"), '"\\\\u001FQ"');
assertEq(JSON.stringify("\\u0020Q"), '"\\\\u0020Q"');
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");