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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"); } });
|
||||
|
||||
@@ -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"); } });
|
||||
|
||||
Reference in New Issue
Block a user