Commit a11e05be authored by Mathias Bynens's avatar Mathias Bynens Committed by Commit Bot

[esnext] Implement well-formed JSON.stringify

The proposal is currently at Stage 2 of the TC39 process.

Repository: https://github.com/tc39/proposal-well-formed-stringify

Bug: v8:7782
Change-Id: I7383f0df5b330aa71e3d80b50b7e52d474f153a3
Reviewed-on: https://chromium-review.googlesource.com/1238475
Commit-Queue: Mathias Bynens <mathias@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56145}
parent 0525e178
......@@ -4320,6 +4320,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_class_fields)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_dynamic_import)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_import_meta)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_numeric_separator)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_json_stringify)
#undef EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE
......
......@@ -209,11 +209,12 @@ DEFINE_IMPLICATION(harmony_class_fields, harmony_private_fields)
// Update bootstrapper.cc whenever adding a new feature flag.
// Features that are still work in progress (behind individual flags).
#define HARMONY_INPROGRESS_BASE(V) \
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_class_fields, "harmony fields in class literals") \
V(harmony_await_optimization, "harmony await taking 1 tick") \
V(harmony_regexp_sequence, "RegExp Unicode sequence properties")
#define HARMONY_INPROGRESS_BASE(V) \
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_class_fields, "harmony fields in class literals") \
V(harmony_await_optimization, "harmony await taking 1 tick") \
V(harmony_regexp_sequence, "RegExp Unicode sequence properties") \
V(harmony_json_stringify, "Well-formed JSON.stringify")
#ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) \
......
......@@ -755,11 +755,49 @@ void JsonStringifier::SerializeStringUnchecked_(
// Assert that uc16 character is not truncated down to 8 bit.
// The <uc16, char> version of this method must not be called.
DCHECK(sizeof(DestChar) >= sizeof(SrcChar));
for (int i = 0; i < src.length(); i++) {
SrcChar c = src[i];
if (DoNotEscape(c)) {
dest->Append(c);
} else if (FLAG_harmony_json_stringify && c >= 0xD800 && c <= 0xDFFF) {
// The current character is a surrogate.
if (c <= 0xDBFF) {
// The current character is a leading surrogate.
if (i + 1 < src.length()) {
// There is a next character.
SrcChar next = src[i + 1];
if (next >= 0xDC00 && next <= 0xDFFF) {
// The next character is a trailing surrogate, meaning this is a
// surrogate pair.
dest->Append(c);
dest->Append(next);
i++;
} else {
// The next character is not a trailing surrogate. Thus, the
// current character is a lone leading surrogate.
dest->AppendCString("\\u");
char* const hex = DoubleToRadixCString(c, 16);
dest->AppendCString(hex);
DeleteArray(hex);
}
} else {
// There is no next character. Thus, the current character is a lone
// leading surrogate.
dest->AppendCString("\\u");
char* const hex = DoubleToRadixCString(c, 16);
dest->AppendCString(hex);
DeleteArray(hex);
}
} else {
// The current character is a lone trailing surrogate. (If it had been
// preceded by a leading surrogate, we would've ended up in the other
// branch earlier on, and the current character would've been handled
// as part of the surrogate pair already.)
dest->AppendCString("\\u");
char* const hex = DoubleToRadixCString(c, 16);
dest->AppendCString(hex);
DeleteArray(hex);
}
} else {
dest->AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
}
......@@ -784,6 +822,45 @@ void JsonStringifier::SerializeString_(Handle<String> string) {
SrcChar c = reader.Get<SrcChar>(i);
if (DoNotEscape(c)) {
builder_.Append<SrcChar, DestChar>(c);
} else if (FLAG_harmony_json_stringify && c >= 0xD800 && c <= 0xDFFF) {
// The current character is a surrogate.
if (c <= 0xDBFF) {
// The current character is a leading surrogate.
if (i + 1 < reader.length()) {
// There is a next character.
SrcChar next = reader.Get<SrcChar>(i + 1);
if (next >= 0xDC00 && next <= 0xDFFF) {
// The next character is a trailing surrogate, meaning this is a
// surrogate pair.
builder_.Append<SrcChar, DestChar>(c);
builder_.Append<SrcChar, DestChar>(next);
i++;
} else {
// The next character is not a trailing surrogate. Thus, the
// current character is a lone leading surrogate.
builder_.AppendCString("\\u");
char* const hex = DoubleToRadixCString(c, 16);
builder_.AppendCString(hex);
DeleteArray(hex);
}
} else {
// There is no next character. Thus, the current character is a
// lone leading surrogate.
builder_.AppendCString("\\u");
char* const hex = DoubleToRadixCString(c, 16);
builder_.AppendCString(hex);
DeleteArray(hex);
}
} else {
// The current character is a lone trailing surrogate. (If it had
// been preceded by a leading surrogate, we would've ended up in the
// other branch earlier on, and the current character would've been
// handled as part of the surrogate pair already.)
builder_.AppendCString("\\u");
char* const hex = DoubleToRadixCString(c, 16);
builder_.AppendCString(hex);
DeleteArray(hex);
}
} else {
builder_.AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
}
......@@ -794,12 +871,15 @@ void JsonStringifier::SerializeString_(Handle<String> string) {
template <>
bool JsonStringifier::DoNotEscape(uint8_t c) {
return c >= '#' && c <= '~' && c != '\\';
// https://tc39.github.io/ecma262/#table-json-single-character-escapes
return c >= 0x23 && c <= 0x7E && c != 0x5C;
}
template <>
bool JsonStringifier::DoNotEscape(uint16_t c) {
return c >= '#' && c != '\\' && c != 0x7F;
// https://tc39.github.io/ecma262/#table-json-single-character-escapes
return c >= 0x23 && c != 0x5C && c != 0x7F &&
(!FLAG_harmony_json_stringify || (c < 0xD800 || c > 0xDFFF));
}
void JsonStringifier::NewLine() {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -375,32 +375,30 @@ var o = { toString: function() { return "42"; } };
assertEquals(42, JSON.parse(o));
for (var i = 0; i < 65536; i++) {
for (var i = 0x0000; i <= 0xFFFF; i++) {
var string = String.fromCharCode(i);
var encoded = JSON.stringify(string);
var expected = "uninitialized";
var expected = 'uninitialized';
// Following the ES5 specification of the abstraction function Quote.
if (string == '"' || string == '\\') {
// Step 2.a
expected = '\\' + string;
} else if ("\b\t\n\r\f".indexOf(string) >= 0) {
} else if ("\b\t\n\r\f".includes(string)) {
// Step 2.b
if (string == '\b') expected = '\\b';
else if (string == '\t') expected = '\\t';
else if (string == '\n') expected = '\\n';
else if (string == '\f') expected = '\\f';
else if (string == '\r') expected = '\\r';
} else if (i < 32) {
} else if (i < 0x20) {
// Step 2.c
if (i < 16) {
expected = "\\u000" + i.toString(16);
} else {
expected = "\\u00" + i.toString(16);
}
expected = '\\u' + i.toString(16).padStart(4, '0');
// TODO(mathias): Add i >= 0xD800 && i <= 0xDFFF case once
// --harmony-json-stringify is enabled by default.
} else {
expected = string;
}
assertEquals('"' + expected + '"', encoded, "Codepoint " + i);
assertEquals('"' + expected + '"', encoded, "code point " + i);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment