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 093a07bd17..debde5cacb 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp @@ -1,4 +1,5 @@ /* Copyright 2014-2015 Samsung Electronics Co., Ltd. + * Copyright 2015 University of Szeged. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -892,46 +893,20 @@ ecma_builtin_array_prototype_object_index_of (ecma_value_t this_arg, /**< this a /* 5. */ ECMA_OP_TO_NUMBER_TRY_CATCH (arg_from_idx, arg2, ret_value); - int32_t from_idx_int = ecma_number_to_int32 (arg_from_idx); + uint32_t from_idx = ecma_builtin_helper_array_index_normalize (arg_from_idx, len); /* 6. */ - if (from_idx_int > 0 && (uint32_t) from_idx_int >= len) + if (from_idx >= len) { ret_value = ecma_make_normal_completion_value (ecma_make_number_value (num_p)); } else { - uint32_t k; + JERRY_ASSERT (from_idx < len); - /* 7 */ - if (from_idx_int >= 0) + for (; from_idx < len && *num_p < 0 && ecma_is_completion_value_empty (ret_value); from_idx++) { - k = (uint32_t) from_idx_int; - } - /* 8. */ - else - { - from_idx_int = -from_idx_int; - - /* As opposed to the standard, we prevent k from being negative, so that we can use an uint32 */ - if ((uint32_t) from_idx_int < len) - { - /* 8.a */ - k = len - (uint32_t) from_idx_int; - } - /* If k would've been negative */ - else - { - /* 8.b */ - k = 0; - } - - } - JERRY_ASSERT (k < len); - - for (; k < len && *num_p < 0 && ecma_is_completion_value_empty (ret_value); k++) - { - ecma_string_t *idx_str_p = ecma_new_ecma_string_from_uint32 (k); + ecma_string_t *idx_str_p = ecma_new_ecma_string_from_uint32 (from_idx); /* 9.a */ if (ecma_op_object_get_property (obj_p, idx_str_p) != NULL) @@ -942,7 +917,7 @@ ecma_builtin_array_prototype_object_index_of (ecma_value_t this_arg, /**< this a /* 9.b.ii */ if (ecma_op_strict_equality_compare (arg1, get_value)) { - *num_p = ecma_uint32_to_number (k); + *num_p = ecma_uint32_to_number (from_idx); } ECMA_FINALIZE (get_value); @@ -1019,59 +994,75 @@ ecma_builtin_array_prototype_object_last_index_of (ecma_value_t this_arg, /**< t } else { - uint32_t k = len - 1; + uint32_t from_idx = len - 1; /* 5. */ if (!ecma_is_value_undefined (arg2)) { ECMA_OP_TO_NUMBER_TRY_CATCH (arg_from_idx, arg2, ret_value); - int32_t n = ecma_number_to_int32 (arg_from_idx); - /* 6. */ - if (n >= 0) + if (!ecma_number_is_nan (arg_from_idx)) { - /* min(n, len - 1)*/ - if ((uint32_t) n > len - 1) + + if (ecma_number_is_infinity (arg_from_idx)) { - k = len - 1; + from_idx = ecma_number_is_negative (arg_from_idx) ? (uint32_t) -1 : len - 1; } else { - k = (uint32_t) n; + int32_t int_from_idx = ecma_number_to_int32 (arg_from_idx); + + /* 6. */ + if (int_from_idx >= 0) + { + /* min(int_from_idx, len - 1)*/ + if ((uint32_t) int_from_idx > len - 1) + { + from_idx = len - 1; + } + else + { + from_idx = (uint32_t) int_from_idx; + } + } + /* 7. */ + else + { + int_from_idx = -int_from_idx; + + /* We prevent from_idx from being negative, so that we can use an uint32 */ + if ((uint32_t) int_from_idx <= len) + { + from_idx = len - (uint32_t) int_from_idx; + } + else + { + /* + * If from_idx would be negative, we set it to UINT_MAX. See reasoning for this in the comment + * at the for loop below. + */ + from_idx = (uint32_t) -1; + } + } } } - /* 7. */ else { - n = -n; - - /* We prevent k from being negative, so that we can use an uint32 */ - if ((uint32_t) n <= len) - { - k = len - (uint32_t) n; - } - else - { - /* - * If k would be negative, we set it to UINT_MAX. See reasoning for this in the comment - * at the for loop below. - */ - k = (uint32_t) -1; - } + from_idx = 0; } ECMA_OP_TO_NUMBER_FINALIZE (arg_from_idx); } /* 8. - * We should break from the loop when k < 0. We can still use an uint32_t for k, and check - * for an underflow instead. This is safe, because k will always start in [0, len - 1], - * and len is in [0, UINT_MAX], so k >= len means we've had an underflow, and should stop. + * We should break from the loop when from_idx < 0. We can still use an uint32_t for from_idx, and check + * for an underflow instead. This is safe, because from_idx will always start in [0, len - 1], + * and len is in [0, UINT_MAX], so from_idx >= len means we've had an underflow, and should stop. */ - for (;k < len && *num_p < 0 && ecma_is_completion_value_empty (ret_value); k--) + for (; from_idx < len && *num_p < 0 && ecma_is_completion_value_empty (ret_value); from_idx--) { /* 8.a */ - ecma_string_t *idx_str_p = ecma_new_ecma_string_from_uint32 (k); + ecma_string_t *idx_str_p = ecma_new_ecma_string_from_uint32 (from_idx); /* 8.a */ if (ecma_op_object_get_property (obj_p, idx_str_p) != NULL) @@ -1082,7 +1073,7 @@ ecma_builtin_array_prototype_object_last_index_of (ecma_value_t this_arg, /**< t /* 8.b.ii */ if (ecma_op_strict_equality_compare (arg1, get_value)) { - *num_p = ecma_uint32_to_number (k); + *num_p = ecma_uint32_to_number (from_idx); } ECMA_FINALIZE (get_value); @@ -2029,30 +2020,8 @@ ecma_builtin_array_prototype_object_slice (ecma_value_t this_arg, /**< 'this' ar /* 5. */ ECMA_OP_TO_NUMBER_TRY_CATCH (start_num, arg1, ret_value); - int32_t relative_start = ecma_number_to_int32 (start_num); - /* 6. */ - if (relative_start < 0) - { - uint32_t start_abs = (uint32_t) -relative_start; - - if (start_abs > len) - { - start = 0; - } - else - { - start = len - start_abs; - } - } - else - { - start = (uint32_t) relative_start; - if (start > len) - { - start = len; - } - } + start = ecma_builtin_helper_array_index_normalize (start_num, len); /* 7. */ if (ecma_is_value_undefined (arg2)) @@ -2062,30 +2031,9 @@ ecma_builtin_array_prototype_object_slice (ecma_value_t this_arg, /**< 'this' ar else { /* 7. part 2*/ - ECMA_OP_TO_NUMBER_TRY_CATCH (end_num, arg2, ret_value) - int32_t relative_end = ecma_number_to_int32 (end_num); + ECMA_OP_TO_NUMBER_TRY_CATCH (end_num, arg2, ret_value); - if (relative_end < 0) - { - uint32_t end_abs = (uint32_t) -relative_end; - - if (end_abs > len) - { - end = 0; - } - else - { - end = len - end_abs; - } - } - else - { - end = (uint32_t) relative_end; - if (end > len) - { - end = len; - } - } + end = ecma_builtin_helper_array_index_normalize (end_num, len); ECMA_OP_TO_NUMBER_FINALIZE (end_num); } @@ -2197,29 +2145,7 @@ ecma_builtin_array_prototype_object_splice (ecma_value_t this_arg, /**< this arg args[0], ret_value); - int32_t relative_start = ecma_number_to_int32 (start_num); - - /* 6. */ - if (relative_start < 0) - { - uint32_t start_abs = (uint32_t) - relative_start; - if (start_abs > len) - { - start = 0; - } - else - { - start = len - start_abs; - } - } - else - { - start = (uint32_t) relative_start; - if (start > len) - { - start = len; - } - } + start = ecma_builtin_helper_array_index_normalize (start_num, len); /* * If there is only one argument, that will be the start argument, @@ -2236,22 +2162,22 @@ ecma_builtin_array_prototype_object_splice (ecma_value_t this_arg, /**< this arg args[1], ret_value); - int32_t delete_count_int = ecma_number_to_int32 (delete_num); - - if (delete_count_int > 0) + if (!ecma_number_is_nan (delete_num)) { - delete_count = (uint32_t) delete_count_int; + if (ecma_number_is_negative (delete_num)) + { + delete_count = 0; + } + else + { + delete_count = ecma_number_is_infinity (delete_num) ? len : ecma_number_to_uint32 (delete_num); + } } else { delete_count = 0; } - if (len - start < delete_count) - { - delete_count = len - start; - } - ECMA_OP_TO_NUMBER_FINALIZE (delete_num); } diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp index c03c874e5f..4f3e0eee31 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp @@ -270,6 +270,69 @@ ecma_builtin_helper_object_get_properties (ecma_object_t *obj_p, /** < object */ return new_array; } /* ecma_builtin_helper_object_get_properties */ +/** + * Helper function to normalizing an array index + * + * This function clamps the given index to the [0, length] range. + * If the index is negative, it is used as the offset from the end of the array, + * to compute normalized index. + * If the index is greater than the length of the array, the normalized index will be the length of the array. + * + * See also: + * ECMA-262 v5, 15.4.4.10 steps 5-6, 7 (part 2) and 8 + * ECMA-262 v5, 15.4.4.12 steps 5-6 + * ECMA-262 v5, 15.4.4.14 steps 5 + * ECMA-262 v5, 15.5.4.13 steps 4, 5 (part 2) and 6-7 + * + * Used by: + * - The Array.prototype.slice routine. + * - The Array.prototype.splice routine. + * - The Array.prototype.indexOf routine. + * - The String.prototype.slice routine. + * + * @return uint32_t - the normalized value of the index + */ +uint32_t +ecma_builtin_helper_array_index_normalize (ecma_number_t index, /**< index */ + uint32_t length) /**< array's length */ +{ + uint32_t norm_index; + + if (!ecma_number_is_nan (index)) + { + + if (ecma_number_is_infinity (index)) + { + norm_index = ecma_number_is_negative (index) ? 0 : length; + } + else + { + const int32_t int_index = ecma_number_to_int32 (index); + + if (int_index < 0) + { + const uint32_t uint_index = (uint32_t) - int_index; + norm_index = uint_index > length ? 0 : length - uint_index; + } + else + { + norm_index = (uint32_t) int_index; + + if (norm_index > length) + { + norm_index = length; + } + } + } + } + else + { + norm_index = 0; + } + + return norm_index; +} /* ecma_builtin_helper_array_index_normalize */ + /** * @} * @} diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h index 39a6b97285..0660b8344c 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h @@ -30,6 +30,7 @@ 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 uint32_t ecma_builtin_helper_array_index_normalize (ecma_number_t index, uint32_t length); /** * @} diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.cpp index d2d139c2ee..055d791638 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.cpp @@ -15,6 +15,7 @@ */ #include "ecma-alloc.h" +#include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" @@ -331,46 +332,16 @@ ecma_builtin_string_prototype_object_slice (ecma_value_t this_arg, /**< this arg const uint32_t len = (uint32_t) ecma_string_get_length (get_string_val); - /* 4. */ + /* 4. 6. */ uint32_t start = 0, end = len; ECMA_OP_TO_NUMBER_TRY_CATCH (start_num, arg1, ret_value); - if (!ecma_number_is_nan (start_num)) - { + start = ecma_builtin_helper_array_index_normalize (start_num, len); - if (ecma_number_is_infinity (start_num)) - { - start = ecma_number_is_negative (start_num) ? 0 : len; - } - else - { - const int int_start = ecma_number_to_int32 (start_num); - - if (int_start < 0) - { - const uint32_t start_abs = (uint32_t) - int_start; - start = start_abs > len ? 0 : len - start_abs; - } - else - { - start = (uint32_t) int_start; - - if (start > len) - { - start = len; - } - } - } - } - else - { - start = 0; - } - - /* 5. */ + /* 5. 7. */ if (ecma_is_value_undefined (arg2)) { end = len; @@ -381,37 +352,7 @@ ecma_builtin_string_prototype_object_slice (ecma_value_t this_arg, /**< this arg arg2, ret_value); - if (!ecma_number_is_nan (end_num)) - { - - if (ecma_number_is_infinity (end_num)) - { - end = ecma_number_is_negative (end_num) ? 0 : len; - } - else - { - const int32_t int_end = ecma_number_to_int32 (end_num); - - if (int_end < 0) - { - const uint32_t end_abs = (uint32_t) - int_end; - end = end_abs > len ? 0 : len - end_abs; - } - else - { - end = (uint32_t) int_end; - - if (end > len) - { - end = len; - } - } - } - } - else - { - end = 0; - } + end = ecma_builtin_helper_array_index_normalize (end_num, len); ECMA_OP_TO_NUMBER_FINALIZE (end_num); } diff --git a/tests/jerry/array-prototype-indexof.js b/tests/jerry/array-prototype-indexof.js index 15511c6b0b..5972fdd5f5 100644 --- a/tests/jerry/array-prototype-indexof.js +++ b/tests/jerry/array-prototype-indexof.js @@ -33,6 +33,10 @@ var index = array.indexOf(obj); assert(index === 3); assert(array[index] === obj); +assert(array.indexOf("foo", NaN) === 0); +assert(array.indexOf("foo", Infinity) === -1); +assert(array.indexOf("foo", -Infinity) === 0); + // Checking behavior when length is zero var obj = { indexOf : Array.prototype.indexOf, length : 0 }; assert(obj.indexOf("foo") === -1); diff --git a/tests/jerry/array-prototype-lastindexof.js b/tests/jerry/array-prototype-lastindexof.js index 32ca31460c..ba71eab1b9 100644 --- a/tests/jerry/array-prototype-lastindexof.js +++ b/tests/jerry/array-prototype-lastindexof.js @@ -33,6 +33,10 @@ var index = array.lastIndexOf(obj); assert(index === 3); assert(array[index] === obj); +assert(array.lastIndexOf("foo", NaN) === 0); +assert(array.lastIndexOf("foo", Infinity) === 4); +assert(array.lastIndexOf("foo", -Infinity) === -1); + var arr = []; arr[4294967294] = "foo"; assert(arr.lastIndexOf("foo", -1) === 4294967294) diff --git a/tests/jerry/array-prototype-slice.js b/tests/jerry/array-prototype-slice.js index 7fdfba8d48..9948418a27 100644 --- a/tests/jerry/array-prototype-slice.js +++ b/tests/jerry/array-prototype-slice.js @@ -20,6 +20,9 @@ var array2 = array.slice("a", "3"); var array3 = array.slice(-2); var array4 = array.slice(-12, undefined); var array5 = array.slice(undefined, -3); +var array6 = array.slice(Infinity, NaN); +var array7 = array.slice(-Infinity, Infinity); +var array8 = array.slice(NaN, -Infinity); assert (array1.length == 4); assert (array1[0] == 54); @@ -45,6 +48,16 @@ assert (array4[3] == -127); assert (array5.length == 1); assert (array5[0] == 54); +assert (array6.length == 0); + +assert (array7.length == 4); +assert (array7[0] == 54); +assert (array7[1] == undefined); +assert (array7[2] == "Lemon"); +assert (array7[3] == -127); + +assert (array8.length == 0); + // Checking behavior when unable to get length var obj = { slice : Array.prototype.slice }; Object.defineProperty(obj, 'length', { 'get' : function () { throw new ReferenceError ("foo"); } }); diff --git a/tests/jerry/array-prototype-splice.js b/tests/jerry/array-prototype-splice.js index e4c79f1c7e..14789f0f83 100644 --- a/tests/jerry/array-prototype-splice.js +++ b/tests/jerry/array-prototype-splice.js @@ -88,6 +88,37 @@ assert (array[3] == -127); assert (array[4] == "sunshine"); assert (array6.length == 0); +// -------------------------------------------------------- +array = setDefaultValues(); +var array7 = array.splice(Infinity, NaN); +assert (array.length == 4); +assert (array[0] == 54); +assert (array[1] == undefined); +assert (array[2] == -127); +assert (array[3] == "sunshine"); +assert (array7.length == 0); + +// -------------------------------------------------------- +array = setDefaultValues(); +var array8 = array.splice(-Infinity, Infinity); + +assert (array.length == 0); +assert (array8.length == 4); +assert (array8[0] == 54); +assert (array8[1] == undefined); +assert (array8[2] == -127); +assert (array8[3] == "sunshine"); + +// -------------------------------------------------------- +array = setDefaultValues(); +var array9 = array.splice(NaN, -Infinity); +assert (array.length == 4); +assert (array[0] == 54); +assert (array[1] == undefined); +assert (array[2] == -127); +assert (array[3] == "sunshine"); +assert (array9.length == 0); + // Checking behavior when unable to get length var obj = {splice : Array.prototype.splice}; Object.defineProperty(obj, 'length', { 'get' : function () { throw new ReferenceError ("foo"); } });