From dfabfe7a56bfa07e349d38bcfa93d6b80f9913f5 Mon Sep 17 00:00:00 2001 From: Szilagyi Adam Date: Fri, 10 Jul 2020 16:06:27 +0200 Subject: [PATCH] Add missing isArray checks based on the ES11 standard (#3983) Added checks to the following methods: - ecma_builtin_json_stringify, ECMA-262 v11, 24.5.2 - ecma_builtin_json_internalize_property, ECMA-262 v11, 24.5.1.1 Fixes #3945 Fixes #3950 JerryScript-DCO-1.0-Signed-off-by: Adam Szilagyi aszilagy@inf.u-szeged.hu --- .../ecma/builtin-objects/ecma-builtin-json.c | 269 ++++++++++++------ .../es.next/regression-test-issue-3945.js | 19 ++ .../es.next/regression-test-issue-3950.js | 20 ++ 3 files changed, 217 insertions(+), 91 deletions(-) create mode 100644 tests/jerry/es.next/regression-test-issue-3945.js create mode 100644 tests/jerry/es.next/regression-test-issue-3950.js diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-json.c b/jerry-core/ecma/builtin-objects/ecma-builtin-json.c index 65ebae52f..f0f0daee6 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-json.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-json.c @@ -602,7 +602,11 @@ ecma_builtin_json_parse_value (ecma_json_token_t *token_p) /**< token argument * } /* ecma_builtin_json_parse_value */ /** - * Abstract operation InternalizeJSONProperty defined in 24.3.1.1 + * Abstract operation InternalizeJSONProperty + * + * See also: + * ECMA-262 v5, 24.3.1.1 + * ECMA-262 v11, 24.5.1.1 * * @return ecma value * Returned value must be freed with ecma_free_value. @@ -628,48 +632,111 @@ ecma_builtin_json_internalize_property (ecma_object_t *reviver_p, /**< reviver f /* 3. */ if (ecma_is_value_object (value)) { + /* 3.a */ + ecma_value_t is_array = ecma_is_value_array (value); + +#if ENABLED (JERRY_ESNEXT) + if (ECMA_IS_VALUE_ERROR (is_array)) + { + ecma_free_value (value); + return is_array; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + ecma_object_t *object_p = ecma_get_object_from_value (value); - ecma_collection_t *props_p = ecma_op_object_get_property_names (object_p, ECMA_LIST_ENUMERABLE); - - ecma_value_t *buffer_p = props_p->buffer_p; - - /* 3.d.iii */ - for (uint32_t i = 0; i < props_p->item_count; i++) + /* 3.c */ + if (ecma_is_value_true (is_array)) { - ecma_string_t *property_name_p = ecma_get_string_from_value (buffer_p[i]); + /* 3.c.ii */ + uint32_t len; + ecma_value_t to_len = ecma_op_object_get_length (object_p, &len); - /* 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)) + /* 3.c.iii */ + if (ECMA_IS_VALUE_ERROR (to_len)) { - ecma_collection_free (props_p); ecma_deref_object (object_p); - - return result; + return to_len; } - /* 3.d.iii.3 */ - if (ecma_is_value_undefined (result)) + /* 3.c.iv */ + for (uint32_t i = 0; i < len; i++) { - 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); + ecma_string_t *prop_index = ecma_new_ecma_string_from_uint32 (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_by_uint32_index (object_p, + i, + 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_deref_ecma_string (prop_index); } } + /* 3.d */ + else + { + ecma_collection_t *props_p = ecma_op_object_get_property_names (object_p, ECMA_LIST_ENUMERABLE); - ecma_collection_free (props_p); + JERRY_ASSERT (props_p != NULL); + + ecma_value_t *buffer_p = props_p->buffer_p; + + /* 3.d.iii */ + 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]); + + /* 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); + } + } + + ecma_collection_free (props_p); + } } ecma_value_t arguments_list[2]; @@ -677,10 +744,10 @@ ecma_builtin_json_internalize_property (ecma_object_t *reviver_p, /**< reviver f arguments_list[1] = value; /* 4. */ - ecma_value_t ret_value = ecma_op_function_call (reviver_p, - ecma_make_object_value (holder_p), - arguments_list, - 2); + ecma_value_t ret_value = ecma_op_function_call (reviver_p, + ecma_make_object_value (holder_p), + arguments_list, + 2); ecma_free_value (value); return ret_value; } /* ecma_builtin_json_internalize_property */ @@ -1379,6 +1446,7 @@ ecma_builtin_json_stringify_no_opts (const ecma_value_t value) /**< value to str * * See also: * ECMA-262 v5, 15.12.3 + * ECMA-262 v11, 24.5.2 * * @return ecma value * Returned value must be freed with ecma_free_value. @@ -1406,80 +1474,99 @@ ecma_builtin_json_stringify (ecma_value_t this_arg, /**< 'this' argument */ context.replacer_function_p = obj_p; } /* 4.b */ - else if (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_ARRAY) + else { - context.property_list_p = ecma_new_collection (); - ecma_extended_object_t *array_object_p = (ecma_extended_object_t *) obj_p; - uint32_t array_length = array_object_p->u.array.length; - uint32_t index = 0; + ecma_value_t is_array = ecma_is_value_array (arg2); - /* 4.b.iii.5 */ - while (index < array_length) +#if ENABLED (JERRY_ESNEXT) + if (ECMA_IS_VALUE_ERROR (is_array)) { - ecma_value_t value = ecma_op_object_get_by_uint32_index (obj_p, index); + return is_array; + } +#endif /* ENABLED (JERRY_ESNEXT) */ - if (ECMA_IS_VALUE_ERROR (value)) + if (ecma_is_value_true (is_array)) + { + uint32_t array_length; + ecma_value_t to_len = ecma_op_object_get_length (obj_p, &array_length); + + if (ECMA_IS_VALUE_ERROR (to_len)) { - ecma_collection_free (context.property_list_p); - return value; + return to_len; } - /* 4.b.iii.5.c */ - ecma_value_t item = ECMA_VALUE_UNDEFINED; + context.property_list_p = ecma_new_collection (); - /* 4.b.iii.5.d */ - if (ecma_is_value_string (value)) - { - ecma_ref_ecma_string (ecma_get_string_from_value (value)); - item = value; - } - /* 4.b.iii.5.e */ - else if (ecma_is_value_number (value)) - { - ecma_string_t *number_str_p = ecma_op_to_string (value); - JERRY_ASSERT (number_str_p != NULL); - item = ecma_make_string_value (number_str_p); - } - /* 4.b.iii.5.f */ - else if (ecma_is_value_object (value)) - { - ecma_object_t *value_obj_p = ecma_get_object_from_value (value); - lit_magic_string_id_t class_id = ecma_object_get_class_name (value_obj_p); + uint32_t index = 0; - if (class_id == LIT_MAGIC_STRING_NUMBER_UL || class_id == LIT_MAGIC_STRING_STRING_UL) + /* 4.b.iii.5 */ + while (index < array_length) + { + ecma_value_t value = ecma_op_object_get_by_uint32_index (obj_p, index); + + if (ECMA_IS_VALUE_ERROR (value)) { - ecma_string_t *str_p = ecma_op_to_string (value); + ecma_collection_free (context.property_list_p); + return value; + } - if (JERRY_UNLIKELY (str_p == NULL)) + /* 4.b.iii.5.c */ + ecma_value_t item = ECMA_VALUE_UNDEFINED; + + /* 4.b.iii.5.d */ + if (ecma_is_value_string (value)) + { + ecma_ref_ecma_string (ecma_get_string_from_value (value)); + item = value; + } + /* 4.b.iii.5.e */ + else if (ecma_is_value_number (value)) + { + ecma_string_t *number_str_p = ecma_op_to_string (value); + JERRY_ASSERT (number_str_p != NULL); + item = ecma_make_string_value (number_str_p); + } + /* 4.b.iii.5.f */ + else if (ecma_is_value_object (value)) + { + ecma_object_t *value_obj_p = ecma_get_object_from_value (value); + lit_magic_string_id_t class_id = ecma_object_get_class_name (value_obj_p); + + if (class_id == LIT_MAGIC_STRING_NUMBER_UL || class_id == LIT_MAGIC_STRING_STRING_UL) { - ecma_collection_free (context.property_list_p); - ecma_free_value (value); - return ECMA_VALUE_ERROR; + ecma_string_t *str_p = ecma_op_to_string (value); + + if (JERRY_UNLIKELY (str_p == NULL)) + { + ecma_collection_free (context.property_list_p); + ecma_free_value (value); + return ECMA_VALUE_ERROR; + } + + item = ecma_make_string_value (str_p); } - - item = ecma_make_string_value (str_p); } - } - ecma_free_value (value); + ecma_free_value (value); - /* 4.b.iii.5.g */ - if (!ecma_is_value_undefined (item)) - { - JERRY_ASSERT (ecma_is_value_string (item)); - ecma_string_t *string_p = ecma_get_string_from_value (item); - - if (!ecma_has_string_value_in_collection (context.property_list_p, string_p)) + /* 4.b.iii.5.g */ + if (!ecma_is_value_undefined (item)) { - ecma_collection_push_back (context.property_list_p, item); - } - else - { - ecma_deref_ecma_string (string_p); - } - } + JERRY_ASSERT (ecma_is_value_string (item)); + ecma_string_t *string_p = ecma_get_string_from_value (item); - index++; + if (!ecma_has_string_value_in_collection (context.property_list_p, string_p)) + { + ecma_collection_push_back (context.property_list_p, item); + } + else + { + ecma_deref_ecma_string (string_p); + } + } + + index++; + } } } } diff --git a/tests/jerry/es.next/regression-test-issue-3945.js b/tests/jerry/es.next/regression-test-issue-3945.js new file mode 100644 index 000000000..05d726682 --- /dev/null +++ b/tests/jerry/es.next/regression-test-issue-3945.js @@ -0,0 +1,19 @@ +// 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. + +function a() { + this[2] = { b : new Proxy(Function, {}) } +} + +JSON.parse("[1, 2, []]", a); diff --git a/tests/jerry/es.next/regression-test-issue-3950.js b/tests/jerry/es.next/regression-test-issue-3950.js new file mode 100644 index 000000000..7d788f170 --- /dev/null +++ b/tests/jerry/es.next/regression-test-issue-3950.js @@ -0,0 +1,20 @@ +// 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. + +function a() { + this[2] = new Proxy(new function() { return new Array }, {}) + return {} +} + +JSON.parse("[1, 2, []]", a);