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 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);
}
@@ -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 */
/**
* @}
* @}
@@ -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);
/**
* @}
@@ -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"
@@ -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);
/* 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;
@@ -382,37 +353,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);
}
+4
View File
@@ -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);
@@ -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)
+13
View File
@@ -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"); } });
+31
View File
@@ -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"); } });