Commit a5380fe9 authored by neis's avatar neis Committed by Commit bot

JSON.parse: properly deal with reviver result

When the reviver returns undefined, the property in question must be deleted
even for arrays.  So far this only happened for non-array objects.

Also change the property enumeration to be spec-conformant, which is observable when the reviver modifies its "this" object directly.  There are a few further issues that need to be addressed in a separate CL.

R=yangguo@chromium.org
BUG=

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

Cr-Commit-Position: refs/heads/master@{#32750}
parent 3ef18f5a
......@@ -17,6 +17,7 @@ var MakeTypeError;
var MaxSimple;
var MinSimple;
var ObjectHasOwnProperty;
var ObjectKeys;
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
utils.Import(function(from) {
......@@ -24,28 +25,32 @@ utils.Import(function(from) {
MaxSimple = from.MaxSimple;
MinSimple = from.MinSimple;
ObjectHasOwnProperty = from.ObjectHasOwnProperty;
ObjectKeys = from.ObjectKeys;
});
// -------------------------------------------------------------------
function Revive(holder, name, reviver) {
function InternalizeJSONProperty(holder, name, reviver) {
var val = holder[name];
if (IS_OBJECT(val)) {
if (IS_OBJECT(val) && val !== null) {
if (IS_ARRAY(val)) {
var length = val.length;
for (var i = 0; i < length; i++) {
var newElement = Revive(val, %_NumberToString(i), reviver);
val[i] = newElement;
var newElement =
InternalizeJSONProperty(val, %_NumberToString(i), reviver);
if (IS_UNDEFINED(newElement)) {
delete val[i];
} else {
val[i] = newElement;
}
}
} else {
for (var p in val) {
if (HAS_OWN_PROPERTY(val, p)) {
var newElement = Revive(val, p, reviver);
if (IS_UNDEFINED(newElement)) {
delete val[p];
} else {
val[p] = newElement;
}
for (var p of ObjectKeys(val)) {
var newElement = InternalizeJSONProperty(val, p, reviver);
if (IS_UNDEFINED(newElement)) {
delete val[p];
} else {
val[p] = newElement;
}
}
}
......@@ -57,7 +62,7 @@ function Revive(holder, name, reviver) {
function JSONParse(text, reviver) {
var unfiltered = %ParseJson(text);
if (IS_CALLABLE(reviver)) {
return Revive({'': unfiltered}, '', reviver);
return InternalizeJSONProperty({'': unfiltered}, '', reviver);
} else {
return unfiltered;
}
......
......@@ -1514,6 +1514,7 @@ utils.Export(function(to) {
to.ObjectHasOwnProperty = ObjectHasOwnProperty;
to.ObjectIsFrozen = ObjectIsFrozen;
to.ObjectIsSealed = ObjectIsSealed;
to.ObjectKeys = ObjectKeys;
to.ObjectToString = ObjectToString;
});
......
......@@ -140,9 +140,16 @@ var pointJson = '{"x": 1, "y": 2}';
assertEquals({'x': 1, 'y': 2}, JSON.parse(pointJson));
assertEquals({'x': 1}, JSON.parse(pointJson, GetFilter('y')));
assertEquals({'y': 2}, JSON.parse(pointJson, GetFilter('x')));
assertEquals([1, 2, 3], JSON.parse("[1, 2, 3]"));
assertEquals([1, undefined, 3], JSON.parse("[1, 2, 3]", GetFilter(1)));
assertEquals([1, 2, undefined], JSON.parse("[1, 2, 3]", GetFilter(2)));
var array1 = JSON.parse("[1, 2, 3]", GetFilter(1));
assertEquals([1, , 3], array1);
assertFalse(array1.hasOwnProperty(1)); // assertEquals above is not enough
var array2 = JSON.parse("[1, 2, 3]", GetFilter(2));
assertEquals([1, 2, ,], array2);
assertFalse(array2.hasOwnProperty(2));
function DoubleNumbers(key, value) {
return (typeof value == 'number') ? 2 * value : value;
......
......@@ -802,6 +802,7 @@
'global-load-from-eval-in-with': [SKIP],
'global-vars-with': [SKIP],
'instanceof-2': [SKIP],
'json': [SKIP],
'math-floor-of-div-minus-zero': [SKIP],
'math-min-max': [SKIP],
'messages': [SKIP],
......
......@@ -46,7 +46,7 @@ Ensure that we always get the same holder
PASS currentHolder is lastHolder
Ensure that returning undefined has removed the property 0 from the holder during filtering.
FAIL currentHolder.hasOwnProperty(0) should be false. Was true.
PASS currentHolder.hasOwnProperty(0) is false
Ensure the holder for our array is indeed an array
PASS Array.isArray(currentHolder) is true
......@@ -76,18 +76,19 @@ PASS value is undefined.
Ensure the holder for our array is indeed an array
PASS Array.isArray(currentHolder) is true
FAIL currentHolder.length should be 3. Was 4.
PASS currentHolder.length is 4
Ensure that we always get the same holder
PASS currentHolder is lastHolder
FAIL Did not call reviver for deleted property
PASS Ensured that property was visited despite Array length being reduced.
PASS value is undefined.
Ensure that we created the root holder as specified in ES5
PASS '' in lastHolder is true
PASS result is lastHolder['']
Ensure that a deleted value is revived if the reviver function returns a value
FAIL result.hasOwnProperty(3) should be true. Was false.
PASS result.hasOwnProperty(3) is true
Test behaviour of revivor used in conjunction with an object
PASS currentHolder != globalObject is true
......@@ -113,11 +114,21 @@ PASS currentHolder['and another property'] is "a replaced value"
Ensure that the changed value is reflected in the arguments passed to the reviver
PASS value is "a replaced value"
PASS currentHolder != globalObject is true
Ensure that we get the same holder object for each property
PASS currentHolder is lastHolder
Ensure that we visited a value that we have deleted, and that deletion is reflected while filtering.
PASS currentHolder.hasOwnProperty('to delete') is false
Ensure that when visiting a deleted property value is undefined
PASS value is undefined.
Ensure that we created the root holder as specified in ES5
PASS lastHolder.hasOwnProperty('') is true
PASS result.hasOwnProperty('a property') is false
FAIL result.hasOwnProperty('to delete') should be true. Was false.
PASS result.hasOwnProperty('to delete') is true
PASS result is lastHolder['']
Test behaviour of revivor that introduces a cycle
......
......@@ -75,12 +75,13 @@ function arrayReviver(i,v) {
debug("");
debug("Ensure that when visiting a deleted property value is undefined");
shouldBeUndefined("value");
this.length = 3;
v = "undelete the property";
expectedLength = this.length = 3;
expectedLength = 4;
break;
case 4:
if (this.length != 3) {
if (this.length != 4) {
testFailed("Did not call reviver for deleted property");
expectedLength = this.length = 3;
break;
......
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