From 9513808647ffac944276515c4499609471fe86d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20B=C3=A1tyai?= Date: Wed, 15 Jul 2015 14:45:31 +0200 Subject: [PATCH] Fix Array.prototype.concat() when 'this' is not an array. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai.u-szeged@partner.samsung.com --- .../ecma-builtin-array-prototype.cpp | 115 +++------------- .../builtin-objects/ecma-builtin-helpers.cpp | 126 ++++++++++++++++++ .../builtin-objects/ecma-builtin-helpers.h | 3 + tests/jerry/array-prototype-concat.js | 32 ++--- 4 files changed, 163 insertions(+), 113 deletions(-) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp index d320512f2a..7f3adf6c35 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp @@ -239,129 +239,48 @@ ecma_builtin_array_prototype_object_concat (ecma_value_t this_arg, /**< this arg ecma_length_t args_number) /**< number of arguments */ { ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + /* 1. */ ECMA_TRY_CATCH (obj_this, ecma_op_to_object (this_arg), ret_value); - ecma_object_t *obj_p = ecma_get_object_from_value (obj_this); - ecma_string_t *magic_string_length_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH); - - ECMA_TRY_CATCH (len_value, - ecma_op_object_get (obj_p, magic_string_length_p), - ret_value); - - ECMA_OP_TO_NUMBER_TRY_CATCH (len_number, len_value, ret_value); - - uint32_t len = ecma_number_to_uint32 (len_number); - - uint32_t new_array_index = 0; - /* 2. */ ecma_completion_value_t new_array = ecma_op_create_array_object (0, 0, false); ecma_object_t *new_array_p = ecma_get_object_from_completion_value (new_array); + uint32_t new_length = 0; + /* 5.b - 5.c for this_arg */ + ECMA_TRY_CATCH (concat_this_value, + ecma_builtin_helper_array_concat_value (new_array_p, &new_length, obj_this), + ret_value); - for (uint32_t index = 0; - index < len && ecma_is_completion_value_empty (ret_value); - index++, new_array_index++) - { - ecma_string_t *index_string_p = ecma_new_ecma_string_from_uint32 (index); - ECMA_TRY_CATCH (get_value, ecma_op_object_get (obj_p, index_string_p), ret_value); - - /* Using [[Put]] is equvalent to [[DefineOwnProperty]] in this case, so we use it for simplicity. */ - ecma_completion_value_t put_comp = ecma_op_object_put (new_array_p, - index_string_p, - get_value, - false); - JERRY_ASSERT (ecma_is_completion_value_normal (put_comp)); - ecma_free_completion_value (put_comp); - - ECMA_FINALIZE (get_value); - ecma_deref_ecma_string (index_string_p); - } - + /* 5. */ for (uint32_t arg_index = 0; arg_index < args_number && ecma_is_completion_value_empty (ret_value); arg_index++) { - /* 5.b */ - if (ecma_is_value_object (args[arg_index]) && - (ecma_object_get_class_name (ecma_get_object_from_value (args[arg_index])) == LIT_MAGIC_STRING_ARRAY_UL)) - { - /* 5.b.ii */ - ECMA_TRY_CATCH (arg_len_value, - ecma_op_object_get (ecma_get_object_from_value (args[arg_index]), - magic_string_length_p), - ret_value); - ECMA_OP_TO_NUMBER_TRY_CATCH (arg_len_number, arg_len_value, ret_value); - - uint32_t arg_len = ecma_number_to_uint32 (arg_len_number); - - for (uint32_t array_index = 0; - array_index < arg_len && ecma_is_completion_value_empty (ret_value); - array_index++, new_array_index++) - { - ecma_string_t *array_index_string_p = ecma_new_ecma_string_from_uint32 (array_index); - - /* 5.b.iii.2 */ - if (ecma_op_object_get_property (ecma_get_object_from_value (args[arg_index]), - array_index_string_p) != NULL) - { - ecma_string_t *new_array_index_string_p = ecma_new_ecma_string_from_uint32 (new_array_index); - - ECMA_TRY_CATCH (get_value, - ecma_op_object_get (ecma_get_object_from_value (args[arg_index]), - array_index_string_p), - ret_value); - - /* Using [[Put]] is equvalent to [[DefineOwnProperty]] in this case, so we use it for simplicity. */ - ecma_completion_value_t put_comp = ecma_op_object_put (new_array_p, - new_array_index_string_p, - get_value, - false); - JERRY_ASSERT (ecma_is_completion_value_normal (put_comp)); - ecma_free_completion_value (put_comp); - - ECMA_FINALIZE (get_value); - ecma_deref_ecma_string (new_array_index_string_p); - } - - ecma_deref_ecma_string (array_index_string_p); - } - - ECMA_OP_TO_NUMBER_FINALIZE (len_number); - ECMA_FINALIZE (arg_len_value); - } - else - { - ecma_string_t *new_array_index_string_p = ecma_new_ecma_string_from_uint32 (new_array_index); - - /* Using [[Put]] is equvalent to [[DefineOwnProperty]] in this case, so we use it for simplicity. */ - ecma_completion_value_t put_comp = ecma_op_object_put (new_array_p, - new_array_index_string_p, - args[arg_index], - false); - JERRY_ASSERT (ecma_is_completion_value_normal (put_comp)); - ecma_free_completion_value (put_comp); - - ecma_deref_ecma_string (new_array_index_string_p); - new_array_index++; - } + ECMA_TRY_CATCH (concat_value, + ecma_builtin_helper_array_concat_value (new_array_p, &new_length, args[arg_index]), + ret_value); + ECMA_FINALIZE (concat_value); } + ECMA_FINALIZE (concat_this_value); + if (ecma_is_completion_value_empty (ret_value)) { + ECMA_TRY_CATCH (set_length_value, + ecma_builtin_array_prototype_helper_set_length (new_array_p, new_length), + ret_value); ret_value = new_array; + ECMA_FINALIZE (set_length_value); } else { ecma_free_completion_value (new_array); } - ECMA_OP_TO_NUMBER_FINALIZE (len_number); - ECMA_FINALIZE (len_value); - ecma_deref_ecma_string (magic_string_length_p); ECMA_FINALIZE (obj_this); return ret_value; diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp index 61ab53e229..3dba2b60c0 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp @@ -325,6 +325,132 @@ ecma_builtin_helper_array_index_normalize (ecma_number_t index, /**< index */ return norm_index; } /* ecma_builtin_helper_array_index_normalize */ +/** + * Helper function for concatenating an ecma_value_t to an Array. + * + * See also: + * ECMA-262 v5, 15.4.4.4 steps 5.b - 5.c + * + * Used by: + * - The Array.prototype.concat routine. + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +ecma_completion_value_t +ecma_builtin_helper_array_concat_value (ecma_object_t *obj_p, /**< array */ + uint32_t *length_p, /**< in-out: array's length */ + ecma_value_t value) /**< value to concat */ +{ + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + /* 5.b */ + if (ecma_is_value_object (value) + && (ecma_object_get_class_name (ecma_get_object_from_value (value)) == LIT_MAGIC_STRING_ARRAY_UL)) + { + ecma_string_t *magic_string_length_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH); + /* 5.b.ii */ + ECMA_TRY_CATCH (arg_len_value, + ecma_op_object_get (ecma_get_object_from_value (value), + magic_string_length_p), + ret_value); + ECMA_OP_TO_NUMBER_TRY_CATCH (arg_len_number, arg_len_value, ret_value); + + uint32_t arg_len = ecma_number_to_uint32 (arg_len_number); + + /* 5.b.iii */ + for (uint32_t array_index = 0; + array_index < arg_len && ecma_is_completion_value_empty (ret_value); + array_index++) + { + ecma_string_t *array_index_string_p = ecma_new_ecma_string_from_uint32 (array_index); + + /* 5.b.iii.2 */ + if (ecma_op_object_get_property (ecma_get_object_from_value (value), + array_index_string_p) != NULL) + { + ecma_string_t *new_array_index_string_p = ecma_new_ecma_string_from_uint32 (*length_p + array_index); + + /* 5.b.iii.3.a */ + ECMA_TRY_CATCH (get_value, + ecma_op_object_get (ecma_get_object_from_value (value), + array_index_string_p), + ret_value); + + ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor (); + { + prop_desc.is_value_defined = true; + prop_desc.value = get_value; + + prop_desc.is_writable_defined = true; + prop_desc.is_writable = true; + + prop_desc.is_enumerable_defined = true; + prop_desc.is_enumerable = true; + + prop_desc.is_configurable_defined = true; + prop_desc.is_configurable = true; + } + + /* 5.b.iii.3.b */ + /* This will always be a simple value since 'is_throw' is false, so no need to free. */ + ecma_completion_value_t put_comp = ecma_op_object_define_own_property (obj_p, + new_array_index_string_p, + &prop_desc, + false); + JERRY_ASSERT (ecma_is_completion_value_normal_true (put_comp)); + + ECMA_FINALIZE (get_value); + ecma_deref_ecma_string (new_array_index_string_p); + } + + ecma_deref_ecma_string (array_index_string_p); + } + + *length_p += arg_len; + + ECMA_OP_TO_NUMBER_FINALIZE (arg_len_number); + ECMA_FINALIZE (arg_len_value); + ecma_deref_ecma_string (magic_string_length_p); + } + else + { + ecma_string_t *new_array_index_string_p = ecma_new_ecma_string_from_uint32 ((*length_p)++); + + ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor (); + { + prop_desc.is_value_defined = true; + prop_desc.value = value; + + prop_desc.is_writable_defined = true; + prop_desc.is_writable = true; + + prop_desc.is_enumerable_defined = true; + prop_desc.is_enumerable = true; + + prop_desc.is_configurable_defined = true; + prop_desc.is_configurable = true; + } + + /* 5.c.i */ + /* This will always be a simple value since 'is_throw' is false, so no need to free. */ + ecma_completion_value_t put_comp = ecma_op_object_define_own_property (obj_p, + new_array_index_string_p, + &prop_desc, + false); + JERRY_ASSERT (ecma_is_completion_value_normal_true (put_comp)); + + ecma_deref_ecma_string (new_array_index_string_p); + } + + if (ecma_is_completion_value_empty (ret_value)) + { + ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_TRUE); + } + + return ret_value; +} /* ecma_builtin_helper_array_concat_value */ + /** * Helper function to normalizing a string index * diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h index 68f1c1d52f..f87a3896f3 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h @@ -30,6 +30,9 @@ extern ecma_completion_value_t ecma_builtin_helper_object_to_string (const ecma_ extern ecma_completion_value_t ecma_builtin_helper_get_to_locale_string_at_index (ecma_object_t *obj_p, uint32_t index); extern ecma_completion_value_t ecma_builtin_helper_object_get_properties (ecma_object_t *obj, bool only_enumerable_properties); +extern ecma_completion_value_t ecma_builtin_helper_array_concat_value (ecma_object_t *obj, + uint32_t *length, + ecma_value_t); extern uint32_t ecma_builtin_helper_array_index_normalize (ecma_number_t index, uint32_t length); extern uint32_t ecma_builtin_helper_string_index_normalize (ecma_number_t index, uint32_t length); diff --git a/tests/jerry/array-prototype-concat.js b/tests/jerry/array-prototype-concat.js index 7b2c9ba66d..a5ab67c87f 100644 --- a/tests/jerry/array-prototype-concat.js +++ b/tests/jerry/array-prototype-concat.js @@ -21,10 +21,17 @@ for (i = 0; i < array.length; i++) { assert(array[i] === new_arr[i]); } -var obj = {}; +var obj = { concat : Array.prototype.concat }; var arr1 = ["Apple", 6, "Peach"]; var arr2 = [obj, "Cherry", "Grape"]; +var new_array = obj.concat(arr1); +assert(new_array.length === 4); +assert(new_array[0] === obj); +assert(new_array[1] === "Apple"); +assert(new_array[2] === 6); +assert(new_array[3] === "Peach"); + var new_array = arr1.concat(arr2, obj, 1); assert(new_array.length === 8); @@ -50,24 +57,19 @@ for (i = 0; i < result.length; i++) { assert(result[i] === expected[i]); } -// Checking behavior when unable to get length -var obj = { concat : Array.prototype.concat } -Object.defineProperty(obj, 'length', { 'get' : function () {throw new ReferenceError ("foo"); } }); - -try { - obj.concat(); - assert(false); -} catch (e) { - assert(e.message === "foo"); - assert(e instanceof ReferenceError); -} +var arr1 = []; +arr1.length = 2; +var arr2 = []; +arr2.length = 3; +assert(arr1.concat(arr2).length === arr1.length + arr2.length); // Checking behavior when unable to get element -var obj = { concat : Array.prototype.concat, length : 1 } -Object.defineProperty(obj, '0', { 'get' : function () {throw new ReferenceError ("foo"); } }); +var arr = [] +Object.defineProperty(arr, '0', { 'get' : function () {throw new ReferenceError ("foo"); } }); +arr.length = 1; try { - obj.concat(); + arr.concat(); assert(false); } catch (e) { assert(e.message === "foo");