diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-json.c b/jerry-core/ecma/builtin-objects/ecma-builtin-json.c index 982bab14a..21eb0da05 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-json.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-json.c @@ -601,6 +601,11 @@ ecma_builtin_json_parse_value (ecma_json_token_t *token_p) /**< token argument * } } /* ecma_builtin_json_parse_value */ +static ecma_value_t +ecma_builtin_json_internalize_process_property (ecma_object_t *reviver_p, + ecma_object_t *object_p, + ecma_string_t *prop_name); + /** * Abstract operation InternalizeJSONProperty * @@ -666,30 +671,17 @@ ecma_builtin_json_internalize_property (ecma_object_t *reviver_p, /**< reviver f for (ecma_length_t i = 0; i < length; i++) { ecma_string_t *prop_index = ecma_new_ecma_string_from_length (i); - - ecma_value_t new_element = ecma_builtin_json_internalize_property (reviver_p, object_p, prop_index); - - if (ECMA_IS_VALUE_ERROR (new_element)) - { - ecma_deref_ecma_string (prop_index); - ecma_deref_object (object_p); - return new_element; - } - - if (ecma_is_value_undefined (new_element)) - { - ecma_value_t delete_val = ecma_op_object_delete (object_p, prop_index, false); - JERRY_ASSERT (ecma_is_value_boolean (delete_val)); - } - else - { - ecma_builtin_json_define_value_property (object_p, - prop_index, - new_element); - ecma_free_value (new_element); - } + ecma_value_t result = ecma_builtin_json_internalize_process_property (reviver_p, object_p, prop_index); ecma_deref_ecma_string (prop_index); + + if (ECMA_IS_VALUE_ERROR (result)) + { + ecma_deref_object (object_p); + return result; + } + + JERRY_ASSERT (result == ECMA_VALUE_TRUE); } } /* 3.d */ @@ -697,8 +689,15 @@ ecma_builtin_json_internalize_property (ecma_object_t *reviver_p, /**< reviver f { ecma_collection_t *props_p = ecma_op_object_get_enumerable_property_names (object_p, ECMA_ENUMERABLE_PROPERTY_KEYS); - +#if ENABLED (JERRY_ESNEXT) + if (JERRY_UNLIKELY (props_p == NULL)) + { + ecma_deref_object (object_p); + return ECMA_VALUE_ERROR; + } +#else /* !ENABLED (JERRY_ESNEXT) */ JERRY_ASSERT (props_p != NULL); +#endif /* ENABLED (JERRY_ESNEXT) */ ecma_value_t *buffer_p = props_p->buffer_p; @@ -706,35 +705,16 @@ ecma_builtin_json_internalize_property (ecma_object_t *reviver_p, /**< reviver f for (uint32_t i = 0; i < props_p->item_count; i++) { ecma_string_t *property_name_p = ecma_get_string_from_value (buffer_p[i]); + ecma_value_t result = ecma_builtin_json_internalize_process_property (reviver_p, object_p, property_name_p); - /* 3.d.iii.1 */ - ecma_value_t result = ecma_builtin_json_internalize_property (reviver_p, object_p, property_name_p); - - /* 3.d.iii.2 */ if (ECMA_IS_VALUE_ERROR (result)) { ecma_collection_free (props_p); ecma_deref_object (object_p); - return result; } - /* 3.d.iii.3 */ - if (ecma_is_value_undefined (result)) - { - ecma_value_t delete_val = ecma_op_general_object_delete (object_p, - property_name_p, - false); - JERRY_ASSERT (ecma_is_value_boolean (delete_val)); - } - /* 3.d.iii.4 */ - else - { - ecma_builtin_json_define_value_property (object_p, - property_name_p, - result); - ecma_free_value (result); - } + JERRY_ASSERT (result == ECMA_VALUE_TRUE); } ecma_collection_free (props_p); @@ -754,6 +734,66 @@ ecma_builtin_json_internalize_property (ecma_object_t *reviver_p, /**< reviver f return ret_value; } /* ecma_builtin_json_internalize_property */ +/** + * Part of the InternalizeJSONProperty abstract method. + * + * See also: + * ECMA-262 v5, 15.12.2 + * ECMA-262 v11, 24.5.1.1 in step 2 + * + * @return ECMA_VALUE_TRUE - if no error occured. + * error if one of the operation failed. + */ +static +ecma_value_t ecma_builtin_json_internalize_process_property (ecma_object_t *reviver_p, /**< reviver function */ + ecma_object_t *object_p, /**< holder object */ + ecma_string_t *prop_name) /**< property name */ +{ + /* ES11: 2.b.iii.1 / 2.c.ii.1 */ + ecma_value_t new_element = ecma_builtin_json_internalize_property (reviver_p, object_p, prop_name); + + if (ECMA_IS_VALUE_ERROR (new_element)) + { + return new_element; + } + + /* ES11: 2.b.iii.2 / 2.c.ii.2 */ + if (ecma_is_value_undefined (new_element)) + { + /* ES11: 2.b.iii.2.a / 2.c.ii.2.a */ + ecma_value_t delete_val = ecma_op_object_delete (object_p, prop_name, false); + +#if ENABLED (JERRY_ESNEXT) + if (ECMA_IS_VALUE_ERROR (delete_val)) + { + return delete_val; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + + JERRY_ASSERT (ecma_is_value_boolean (delete_val)); + } + else + { + /* ES11: 2.b.iii.3.a / 2.c.ii.3.a */ + ecma_value_t def_value = ecma_builtin_helper_def_prop (object_p, + prop_name, + new_element, + ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); + ecma_free_value (new_element); + +#if ENABLED (JERRY_ESNEXT) + if (ECMA_IS_VALUE_ERROR (def_value)) + { + return def_value; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + + JERRY_ASSERT (ecma_is_value_boolean (def_value)); + } + + return ECMA_VALUE_TRUE; +} /* ecma_builtin_json_internalize_process_property */ + /** * Function to set a string token from the given arguments, fills its fields and advances the string pointer. * diff --git a/tests/jerry/es.next/json-parse-proxy.js b/tests/jerry/es.next/json-parse-proxy.js new file mode 100644 index 000000000..0a4413ea8 --- /dev/null +++ b/tests/jerry/es.next/json-parse-proxy.js @@ -0,0 +1,88 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* Test JSON parse [[delete]] with Array */ +var badDeleteArray = new Proxy([0], { + deleteProperty: function() { + throw "My Super Error A"; + } +}); + +try { + JSON.parse('[0,0]', function() { this[1] = badDeleteArray; }); +} catch (ex) { + assert(ex === "My Super Error A"); +} + +/* Test JSON parse [[delete]] with Objects */ +var badDeleteObj = new Proxy([0], { + deleteProperty: function() { + throw "My Super Error B"; + } +}); + +try { + JSON.parse('[0,0]', function() { this[1] = badDeleteObj; }); +} catch (ex) { + assert(ex === "My Super Error B"); +} + +/* Test JSON parse property define with Array */ +var badDefineArray = new Proxy([null], { + defineProperty: function(_, name) { + throw "My Super Error C"; + } +}); + +try { + JSON.parse('["first", null]', function(_, value) { + if (value === 'first') { + this[1] = badDefineArray; + } + return value; + }); +} catch (ex) { + assert(ex === "My Super Error C"); +} + +/* Test JSON parse property define with Object */ +var badDefineObj = new Proxy({0: null}, { + defineProperty: function(_, name) { + throw "My Super Error D"; + } +}); + +try { + JSON.parse('["first", null]', function(_, value) { + if (value === 'first') { + this[1] = badDefineObj; + } + return value; + }); +} catch (ex) { + assert(ex === "My Super Error D"); +} + +/* Test JSON parse keys call */ +var badKeys = new Proxy({}, { + ownKeys: function() { + throw "My Super Error E"; + } +}); + +try { + JSON.parse('[0,0]', function() { this[1] = badKeys; }); +} catch (ex) { + assert(ex === "My Super Error E"); +} diff --git a/tests/test262-esnext-excludelist.xml b/tests/test262-esnext-excludelist.xml index 0d8070f15..3fc3ef330 100644 --- a/tests/test262-esnext-excludelist.xml +++ b/tests/test262-esnext-excludelist.xml @@ -979,11 +979,6 @@ - - - - -