Fix the indexing of Array builtin functions.

The index-dependant builtins didn't handle correctly the positive Infinity value.

JerryScript-DCO-1.0-Signed-off-by: Zsolt Borbély zsborbely.u-szeged@partner.samsung.com
This commit is contained in:
Zsolt Borbély
2015-06-16 12:37:28 +02:00
committed by Peter Gal
parent caa1617ea2
commit 3f28cb3bf8
8 changed files with 187 additions and 204 deletions
@@ -1,4 +1,5 @@
/* Copyright 2014-2015 Samsung Electronics Co., Ltd. /* Copyright 2014-2015 Samsung Electronics Co., Ltd.
* Copyright 2015 University of Szeged.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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. */ /* 5. */
ECMA_OP_TO_NUMBER_TRY_CATCH (arg_from_idx, arg2, ret_value); 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. */ /* 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)); ret_value = ecma_make_normal_completion_value (ecma_make_number_value (num_p));
} }
else else
{ {
uint32_t k; JERRY_ASSERT (from_idx < len);
/* 7 */ for (; from_idx < len && *num_p < 0 && ecma_is_completion_value_empty (ret_value); from_idx++)
if (from_idx_int >= 0)
{ {
k = (uint32_t) from_idx_int; ecma_string_t *idx_str_p = ecma_new_ecma_string_from_uint32 (from_idx);
}
/* 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);
/* 9.a */ /* 9.a */
if (ecma_op_object_get_property (obj_p, idx_str_p) != NULL) 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 */ /* 9.b.ii */
if (ecma_op_strict_equality_compare (arg1, get_value)) 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); ECMA_FINALIZE (get_value);
@@ -1019,59 +994,75 @@ ecma_builtin_array_prototype_object_last_index_of (ecma_value_t this_arg, /**< t
} }
else else
{ {
uint32_t k = len - 1; uint32_t from_idx = len - 1;
/* 5. */ /* 5. */
if (!ecma_is_value_undefined (arg2)) if (!ecma_is_value_undefined (arg2))
{ {
ECMA_OP_TO_NUMBER_TRY_CATCH (arg_from_idx, arg2, ret_value); ECMA_OP_TO_NUMBER_TRY_CATCH (arg_from_idx, arg2, ret_value);
int32_t n = ecma_number_to_int32 (arg_from_idx);
/* 6. */ if (!ecma_number_is_nan (arg_from_idx))
if (n >= 0)
{ {
/* 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 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 else
{ {
n = -n; from_idx = 0;
/* 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;
}
} }
ECMA_OP_TO_NUMBER_FINALIZE (arg_from_idx); ECMA_OP_TO_NUMBER_FINALIZE (arg_from_idx);
} }
/* 8. /* 8.
* We should break from the loop when k < 0. We can still use an uint32_t for k, and check * 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 k will always start in [0, len - 1], * 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 k >= len means we've had an underflow, and should stop. * 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 */ /* 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 */ /* 8.a */
if (ecma_op_object_get_property (obj_p, idx_str_p) != NULL) 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 */ /* 8.b.ii */
if (ecma_op_strict_equality_compare (arg1, get_value)) 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); ECMA_FINALIZE (get_value);
@@ -2029,30 +2020,8 @@ ecma_builtin_array_prototype_object_slice (ecma_value_t this_arg, /**< 'this' ar
/* 5. */ /* 5. */
ECMA_OP_TO_NUMBER_TRY_CATCH (start_num, arg1, ret_value); ECMA_OP_TO_NUMBER_TRY_CATCH (start_num, arg1, ret_value);
int32_t relative_start = ecma_number_to_int32 (start_num);
/* 6. */ start = ecma_builtin_helper_array_index_normalize (start_num, len);
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;
}
}
/* 7. */ /* 7. */
if (ecma_is_value_undefined (arg2)) if (ecma_is_value_undefined (arg2))
@@ -2062,30 +2031,9 @@ ecma_builtin_array_prototype_object_slice (ecma_value_t this_arg, /**< 'this' ar
else else
{ {
/* 7. part 2*/ /* 7. part 2*/
ECMA_OP_TO_NUMBER_TRY_CATCH (end_num, arg2, ret_value) ECMA_OP_TO_NUMBER_TRY_CATCH (end_num, arg2, ret_value);
int32_t relative_end = ecma_number_to_int32 (end_num);
if (relative_end < 0) end = ecma_builtin_helper_array_index_normalize (end_num, len);
{
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;
}
}
ECMA_OP_TO_NUMBER_FINALIZE (end_num); 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], args[0],
ret_value); ret_value);
int32_t relative_start = ecma_number_to_int32 (start_num); start = ecma_builtin_helper_array_index_normalize (start_num, len);
/* 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;
}
}
/* /*
* If there is only one argument, that will be the start argument, * 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], args[1],
ret_value); ret_value);
int32_t delete_count_int = ecma_number_to_int32 (delete_num); if (!ecma_number_is_nan (delete_num))
if (delete_count_int > 0)
{ {
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 else
{ {
delete_count = 0; delete_count = 0;
} }
if (len - start < delete_count)
{
delete_count = len - start;
}
ECMA_OP_TO_NUMBER_FINALIZE (delete_num); ECMA_OP_TO_NUMBER_FINALIZE (delete_num);
} }
@@ -270,6 +270,69 @@ ecma_builtin_helper_object_get_properties (ecma_object_t *obj_p, /** < object */
return new_array; return new_array;
} /* ecma_builtin_helper_object_get_properties */ } /* 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 */
/** /**
* @} * @}
* @} * @}
@@ -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_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, extern ecma_completion_value_t ecma_builtin_helper_object_get_properties (ecma_object_t *obj,
bool only_enumerable_properties); bool only_enumerable_properties);
extern uint32_t ecma_builtin_helper_array_index_normalize (ecma_number_t index, uint32_t length);
/** /**
* @} * @}
@@ -15,6 +15,7 @@
*/ */
#include "ecma-alloc.h" #include "ecma-alloc.h"
#include "ecma-builtin-helpers.h"
#include "ecma-builtins.h" #include "ecma-builtins.h"
#include "ecma-conversion.h" #include "ecma-conversion.h"
#include "ecma-exceptions.h" #include "ecma-exceptions.h"
@@ -332,46 +333,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); const uint32_t len = (uint32_t) ecma_string_get_length (get_string_val);
/* 4. */ /* 4. 6. */
uint32_t start = 0, end = len; uint32_t start = 0, end = len;
ECMA_OP_TO_NUMBER_TRY_CATCH (start_num, ECMA_OP_TO_NUMBER_TRY_CATCH (start_num,
arg1, arg1,
ret_value); 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)) /* 5. 7. */
{
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. */
if (ecma_is_value_undefined (arg2)) if (ecma_is_value_undefined (arg2))
{ {
end = len; end = len;
@@ -382,37 +353,7 @@ ecma_builtin_string_prototype_object_slice (ecma_value_t this_arg, /**< this arg
arg2, arg2,
ret_value); ret_value);
if (!ecma_number_is_nan (end_num)) end = ecma_builtin_helper_array_index_normalize (end_num, len);
{
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;
}
ECMA_OP_TO_NUMBER_FINALIZE (end_num); ECMA_OP_TO_NUMBER_FINALIZE (end_num);
} }
+4
View File
@@ -33,6 +33,10 @@ var index = array.indexOf(obj);
assert(index === 3); assert(index === 3);
assert(array[index] === obj); 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 // Checking behavior when length is zero
var obj = { indexOf : Array.prototype.indexOf, length : 0 }; var obj = { indexOf : Array.prototype.indexOf, length : 0 };
assert(obj.indexOf("foo") === -1); assert(obj.indexOf("foo") === -1);
@@ -33,6 +33,10 @@ var index = array.lastIndexOf(obj);
assert(index === 3); assert(index === 3);
assert(array[index] === obj); assert(array[index] === obj);
assert(array.lastIndexOf("foo", NaN) === 0);
assert(array.lastIndexOf("foo", Infinity) === 4);
assert(array.lastIndexOf("foo", -Infinity) === -1);
var arr = []; var arr = [];
arr[4294967294] = "foo"; arr[4294967294] = "foo";
assert(arr.lastIndexOf("foo", -1) === 4294967294) assert(arr.lastIndexOf("foo", -1) === 4294967294)
+13
View File
@@ -20,6 +20,9 @@ var array2 = array.slice("a", "3");
var array3 = array.slice(-2); var array3 = array.slice(-2);
var array4 = array.slice(-12, undefined); var array4 = array.slice(-12, undefined);
var array5 = array.slice(undefined, -3); 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.length == 4);
assert (array1[0] == 54); assert (array1[0] == 54);
@@ -45,6 +48,16 @@ assert (array4[3] == -127);
assert (array5.length == 1); assert (array5.length == 1);
assert (array5[0] == 54); 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 // Checking behavior when unable to get length
var obj = { slice : Array.prototype.slice }; var obj = { slice : Array.prototype.slice };
Object.defineProperty(obj, 'length', { 'get' : function () { throw new ReferenceError ("foo"); } }); Object.defineProperty(obj, 'length', { 'get' : function () { throw new ReferenceError ("foo"); } });
+31
View File
@@ -88,6 +88,37 @@ assert (array[3] == -127);
assert (array[4] == "sunshine"); assert (array[4] == "sunshine");
assert (array6.length == 0); 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 // Checking behavior when unable to get length
var obj = {splice : Array.prototype.splice}; var obj = {splice : Array.prototype.splice};
Object.defineProperty(obj, 'length', { 'get' : function () { throw new ReferenceError ("foo"); } }); Object.defineProperty(obj, 'length', { 'get' : function () { throw new ReferenceError ("foo"); } });