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

Ship well-formed JSON.stringify 🎉

This is a reland of 0d91db0b.

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

Intent to ship:
https://groups.google.com/d/msg/v8-users/IRu3bAC_pLM/pFwz2ti1AgAJ

TBR=gsathya@chromium.org

Bug: v8:7782
Change-Id: I53d006650e2b4099a111d2e5bc067e4a2c7cf4a0
Reviewed-on: https://chromium-review.googlesource.com/c/1282993Reviewed-by: 's avatarMathias Bynens <mathias@chromium.org>
Commit-Queue: Mathias Bynens <mathias@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56689}
parent 27878742
......@@ -234,18 +234,18 @@ DEFINE_IMPLICATION(harmony_class_fields, harmony_private_fields)
V(harmony_private_fields, "harmony private fields in class literals") \
V(harmony_numeric_separator, "harmony numeric separator between digits") \
V(harmony_string_matchall, "harmony String.prototype.matchAll") \
V(harmony_static_fields, "harmony static fields in class literals") \
V(harmony_json_stringify, "Well-formed JSON.stringify")
V(harmony_static_fields, "harmony static fields in class literals")
// Features that are shipping (turned on by default, but internal flag remains).
#define HARMONY_SHIPPING_BASE(V) \
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
V(harmony_import_meta, "harmony import.meta property") \
V(harmony_dynamic_import, "harmony dynamic import") \
V(harmony_array_prototype_values, "harmony Array.prototype.values") \
V(harmony_array_flat, "harmony Array.prototype.{flat,flatMap}") \
V(harmony_symbol_description, "harmony Symbol.prototype.description") \
V(harmony_global, "harmony global")
#define HARMONY_SHIPPING_BASE(V) \
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
V(harmony_import_meta, "harmony import.meta property") \
V(harmony_dynamic_import, "harmony dynamic import") \
V(harmony_array_prototype_values, "harmony Array.prototype.values") \
V(harmony_array_flat, "harmony Array.prototype.{flat,flatMap}") \
V(harmony_symbol_description, "harmony Symbol.prototype.description") \
V(harmony_global, "harmony global") \
V(harmony_json_stringify, "well-formed JSON.stringify")
#ifdef V8_INTL_SUPPORT
#define HARMONY_SHIPPING(V) \
......
......@@ -390,11 +390,9 @@ for (var i = 0x0000; i <= 0xFFFF; i++) {
else if (string == '\n') expected = '\\n';
else if (string == '\f') expected = '\\f';
else if (string == '\r') expected = '\\r';
} else if (i < 0x20) {
} else if (i < 0x20 || (i >= 0xD800 && i <= 0xDFFF)) {
// Step 2.c
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;
}
......
......@@ -82,19 +82,19 @@ function createTests() {
});
result.push(function (jsonObject){
var value = new Number(1);
value.valueOf = function() { return 2; }
value.valueOf = function() { return 2; };
return jsonObject.stringify(value);
});
result[result.length - 1].expected = '2';
result.push(function (jsonObject){
var value = new Boolean(true);
value.valueOf = function() { return 2; }
value.valueOf = function() { return 2; };
return jsonObject.stringify(value);
});
result[result.length - 1].expected = '2';
result.push(function (jsonObject){
var value = new String("fail");
value.toString = function() { return "converted string"; }
value.toString = function() { return "converted string"; };
return jsonObject.stringify(value);
});
result[result.length - 1].expected = '"converted string"';
......@@ -429,7 +429,7 @@ function createTests() {
result[result.length - 1].throws = true;
result.push(function (jsonObject){
cycleTracker = "";
try { jsonObject.stringify(cyclicArray); } catch(e) { cycleTracker += " -> exception" }
try { jsonObject.stringify(cyclicArray); } catch { cycleTracker += " -> exception" }
return cycleTracker;
});
result[result.length - 1].expected = "0(number):[object Object]first, -> exception";
......@@ -508,7 +508,7 @@ function createTests() {
return jsonObject.stringify(toDeepVirtualJSONArray());
});
var fullCharsetString = "";
for (var i = 0; i < 65536; i++)
for (let i = 0; i <= 0xFFFF; i++)
fullCharsetString += String.fromCharCode(i);
result.push(function (jsonObject){
return jsonObject.stringify(fullCharsetString);
......
......@@ -186,7 +186,7 @@ if (!this.JSON) {
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f]/g,
escapable = /[\\\"\x00-\x1F]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,
gap,
indent,
meta = { // table of character substitutions
......@@ -203,17 +203,17 @@ if (!this.JSON) {
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
// If the string contains no control characters, no quote characters, no
// backslash characters, and no lone surrogates, then we can safely
// slap some quotes around it. Otherwise we must also replace the
// offending characters with safe escape sequences.
escapable.lastIndex = 0;
return escapable.test(string) ?
'"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
'\\u' + a.charCodeAt(0).toString(16).padStart(4, '0');
}) + '"' :
'"' + string + '"';
}
......
......@@ -52,19 +52,19 @@ function (jsonObject){
PASS tests[i](nativeJSON) is tests[i](JSON)
function (jsonObject){
var value = new Number(1);
value.valueOf = function() { return 2; }
value.valueOf = function() { return 2; };
return jsonObject.stringify(value);
}
PASS tests[i](nativeJSON) is tests[i].expected
function (jsonObject){
var value = new Boolean(true);
value.valueOf = function() { return 2; }
value.valueOf = function() { return 2; };
return jsonObject.stringify(value);
}
FAIL tests[i](nativeJSON) should be 2. Was true.
function (jsonObject){
var value = new String("fail");
value.toString = function() { return "converted string"; }
value.toString = function() { return "converted string"; };
return jsonObject.stringify(value);
}
PASS tests[i](nativeJSON) is tests[i].expected
......@@ -448,7 +448,7 @@ function (jsonObject){
PASS tests[i](nativeJSON) threw exception TypeError: Converting circular structure to JSON.
function (jsonObject){
cycleTracker = "";
try { jsonObject.stringify(cyclicArray); } catch(e) { cycleTracker += " -> exception" }
try { jsonObject.stringify(cyclicArray); } catch { cycleTracker += " -> exception" }
return cycleTracker;
}
FAIL tests[i](nativeJSON) should be 0(number):[object Object]first, -> exception. Was 0(string):[object Object]first, -> exception.
......@@ -541,4 +541,3 @@ PASS tests[i](nativeJSON) is tests[i](JSON)
PASS successfullyParsed is true
TEST COMPLETE
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