Add indexOf and lastIndexOf function to typedArray (#2938)

The algorithm's are based on ECMA-262 v6, 22.2.3.13, 22.2.3.16

Co-authored-by: Tibor Dusnoki tdusnoki@inf.u-szeged.hu
JerryScript-DCO-1.0-Signed-off-by: Adam Szilagyi aszilagy@inf.u-szeged.hu
This commit is contained in:
Szilagyi Adam
2019-08-30 14:01:30 +02:00
committed by Dániel Bátyai
parent e3c1451d42
commit f0578b2c25
10 changed files with 360 additions and 72 deletions
@@ -750,7 +750,7 @@ ecma_builtin_array_prototype_object_slice (ecma_value_t arg1, /**< start */
return ECMA_VALUE_ERROR;
}
start = ecma_builtin_helper_array_index_normalize (start_num, len);
start = ecma_builtin_helper_array_index_normalize (start_num, len, false);
/* 7. */
if (ecma_is_value_undefined (arg2))
@@ -767,7 +767,7 @@ ecma_builtin_array_prototype_object_slice (ecma_value_t arg1, /**< start */
return ECMA_VALUE_ERROR;
}
end = ecma_builtin_helper_array_index_normalize (end_num, len);
end = ecma_builtin_helper_array_index_normalize (end_num, len, false);
}
JERRY_ASSERT (start <= len && end <= len);
@@ -1122,7 +1122,7 @@ ecma_builtin_array_prototype_object_splice (const ecma_value_t args[], /**< argu
return ECMA_VALUE_ERROR;
}
start = ecma_builtin_helper_array_index_normalize (start_num, len);
start = ecma_builtin_helper_array_index_normalize (start_num, len, false);
/*
* If there is only one argument, that will be the start argument,
@@ -1434,7 +1434,7 @@ ecma_builtin_array_prototype_object_index_of (ecma_value_t arg1, /**< searchElem
ecma_number_t found_index = ECMA_NUMBER_MINUS_ONE;
uint32_t from_idx = ecma_builtin_helper_array_index_normalize (arg_from_idx, len);
uint32_t from_idx = ecma_builtin_helper_array_index_normalize (arg_from_idx, len, false);
/* 6. */
for (; from_idx < len && found_index < 0; from_idx++)
@@ -1483,7 +1483,6 @@ ecma_builtin_array_prototype_object_last_index_of (const ecma_value_t args[], /*
uint32_t from_idx = len - 1;
/* 5. */
if (args_number > 1)
{
ecma_number_t arg_from_idx;
@@ -1492,54 +1491,7 @@ ecma_builtin_array_prototype_object_last_index_of (const ecma_value_t args[], /*
return ECMA_VALUE_ERROR;
}
if (!ecma_number_is_nan (arg_from_idx))
{
if (ecma_number_is_infinity (arg_from_idx))
{
from_idx = ecma_number_is_negative (arg_from_idx) ? (uint32_t) -1 : len - 1;
}
else
{
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;
}
}
}
}
else
{
from_idx = 0;
}
from_idx = ecma_builtin_helper_array_index_normalize (arg_from_idx, len, true);
}
ecma_number_t num = ECMA_NUMBER_MINUS_ONE;
@@ -106,7 +106,7 @@ ecma_builtin_arraybuffer_prototype_object_slice (ecma_value_t this_arg, /**< thi
arg1,
ret_value);
start = ecma_builtin_helper_array_index_normalize (start_num, len);
start = ecma_builtin_helper_array_index_normalize (start_num, len, false);
if (!ecma_is_value_undefined (arg2))
{
@@ -114,7 +114,7 @@ ecma_builtin_arraybuffer_prototype_object_slice (ecma_value_t this_arg, /**< thi
arg2,
ret_value);
end = ecma_builtin_helper_array_index_normalize (end_num, len);
end = ecma_builtin_helper_array_index_normalize (end_num, len, false);
ECMA_OP_TO_NUMBER_FINALIZE (end_num);
}
@@ -314,7 +314,7 @@ ecma_builtin_helper_object_get_properties (ecma_object_t *obj_p, /**< object */
* 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.
*
* If is_last_index_of is true, then we use the method in ECMA-262 v6, 22.2.3.16 to compute the normalized index.
* 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
@@ -326,12 +326,15 @@ ecma_builtin_helper_object_get_properties (ecma_object_t *obj_p, /**< object */
* - The Array.prototype.splice routine.
* - The Array.prototype.indexOf routine.
* - The String.prototype.slice routine.
* - The TypedArray.prototype.indexOf routine.
* - The TypedArray.prototype.lastIndexOf 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 length, /**< array's length */
bool is_last_index_of) /**< true - normalize for lastIndexOf method*/
{
uint32_t norm_index;
@@ -343,15 +346,28 @@ ecma_builtin_helper_array_index_normalize (ecma_number_t index, /**< index */
norm_index = 0;
}
else if (ecma_number_is_infinity (index))
{
if (is_last_index_of)
{
norm_index = ecma_number_is_negative (index) ? (uint32_t) -1 : length - 1;
}
else
{
norm_index = ecma_number_is_negative (index) ? 0 : length;
}
}
else
{
if (ecma_number_is_negative (index))
{
ecma_number_t index_neg = -index;
if (is_last_index_of)
{
norm_index = length - ecma_number_to_uint32 (index_neg);
}
else
{
if (index_neg > length)
{
norm_index = 0;
@@ -361,11 +377,12 @@ ecma_builtin_helper_array_index_normalize (ecma_number_t index, /**< index */
norm_index = length - ecma_number_to_uint32 (index_neg);
}
}
}
else
{
if (index > length)
{
norm_index = length;
norm_index = is_last_index_of ? length - 1 : length;
}
else
{
@@ -48,7 +48,7 @@ ecma_builtin_helper_object_get_properties (ecma_object_t *obj_p, uint32_t opts);
ecma_value_t
ecma_builtin_helper_array_concat_value (ecma_object_t *obj_p, uint32_t *length_p, ecma_value_t value);
uint32_t
ecma_builtin_helper_array_index_normalize (ecma_number_t index, uint32_t length);
ecma_builtin_helper_array_index_normalize (ecma_number_t index, uint32_t length, bool last_index_of);
uint32_t
ecma_builtin_helper_string_index_normalize (ecma_number_t index, uint32_t length, bool nan_to_zero);
ecma_value_t
@@ -1275,7 +1275,7 @@ ecma_builtin_string_prototype_object_slice (ecma_string_t *get_string_val, /**<
return ECMA_VALUE_ERROR;
}
start = ecma_builtin_helper_array_index_normalize (start_num, len);
start = ecma_builtin_helper_array_index_normalize (start_num, len, false);
/* 5. 7. */
if (ecma_is_value_undefined (arg2))
@@ -1291,10 +1291,9 @@ ecma_builtin_string_prototype_object_slice (ecma_string_t *get_string_val, /**<
return ECMA_VALUE_ERROR;
}
end = ecma_builtin_helper_array_index_normalize (end_num, len);
end = ecma_builtin_helper_array_index_normalize (end_num, len, false);
}
JERRY_ASSERT (start <= len && end <= len);
/* 8-9. */
@@ -16,6 +16,7 @@
#include "ecma-builtin-helpers.h"
#include "ecma-builtin-typedarray-helpers.h"
#include "ecma-builtins.h"
#include "ecma-comparison.h"
#include "ecma-exceptions.h"
#include "ecma-globals.h"
#include "ecma-helpers.h"
@@ -1249,7 +1250,7 @@ ecma_builtin_typedarray_prototype_subarray (ecma_value_t this_arg, /**< this arg
/* 7. relativeBegin */
ECMA_OP_TO_NUMBER_TRY_CATCH (relative_begin, begin, ret_value);
begin_index_uint32 = ecma_builtin_helper_array_index_normalize (relative_begin, src_length);
begin_index_uint32 = ecma_builtin_helper_array_index_normalize (relative_begin, src_length, false);
if (ecma_is_value_undefined (end))
{
@@ -1260,7 +1261,7 @@ ecma_builtin_typedarray_prototype_subarray (ecma_value_t this_arg, /**< this arg
/* 10. relativeEnd */
ECMA_OP_TO_NUMBER_TRY_CATCH (relative_end, end, ret_value);
end_index_uint32 = ecma_builtin_helper_array_index_normalize (relative_end, src_length);
end_index_uint32 = ecma_builtin_helper_array_index_normalize (relative_end, src_length, false);
ECMA_OP_TO_NUMBER_FINALIZE (relative_end);
}
@@ -1341,7 +1342,7 @@ ecma_builtin_typedarray_prototype_fill (ecma_value_t this_arg, /**< this argumen
uint32_t begin_index_uint32 = 0, end_index_uint32 = 0;
ECMA_OP_TO_NUMBER_TRY_CATCH (relative_begin, begin, ret_value);
begin_index_uint32 = ecma_builtin_helper_array_index_normalize (relative_begin, length);
begin_index_uint32 = ecma_builtin_helper_array_index_normalize (relative_begin, length, false);
if (ecma_is_value_undefined (end))
{
@@ -1351,7 +1352,7 @@ ecma_builtin_typedarray_prototype_fill (ecma_value_t this_arg, /**< this argumen
{
ECMA_OP_TO_NUMBER_TRY_CATCH (relative_end, end, ret_value);
end_index_uint32 = ecma_builtin_helper_array_index_normalize (relative_end, length);
end_index_uint32 = ecma_builtin_helper_array_index_normalize (relative_end, length, false);
ECMA_OP_TO_NUMBER_FINALIZE (relative_end);
}
@@ -1687,6 +1688,115 @@ ecma_builtin_typedarray_prototype_find_index (ecma_value_t this_arg, /**< this a
return ecma_builtin_typedarray_prototype_find_helper (this_arg, predicate, predicate_this_arg, false);
} /* ecma_builtin_typedarray_prototype_find_index */
/**
* The %TypedArray%.prototype object's 'indexOf' and 'lastIndexOf' routine helper
*
* See also:
* ECMA-262 v6, 22.2.3.13, 22.2.3.16
*
* @return ecma value
* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_builtin_typedarray_prototype_index_helper (ecma_value_t this_arg, /**< this argument */
const ecma_value_t args[], /**< arguments list */
ecma_length_t args_number, /**< number of arguments */
bool is_last_index_of) /**< true - lastIndexOf routine
false - indexOf routine */
{
if (!ecma_is_typedarray (this_arg))
{
return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a TypedArray."));
}
ecma_object_t *typedarray_p = ecma_get_object_from_value (this_arg);
uint32_t length = ecma_typedarray_get_length (typedarray_p);
lit_magic_string_id_t class_id = ecma_object_get_class_name (typedarray_p);
lit_utf8_byte_t *typedarray_buffer_p = ecma_typedarray_get_buffer (typedarray_p);
uint8_t shift = ecma_typedarray_get_element_size_shift (typedarray_p);
uint8_t element_size = (uint8_t) (1 << shift);
uint32_t limit = length * element_size;
uint32_t from_index;
if (args_number == 0
|| length == 0)
{
return ecma_make_integer_value (-1);
}
if (args_number == 1)
{
from_index = is_last_index_of ? length - 1 : 0;
}
else
{
ecma_number_t num_var;
if (ECMA_IS_VALUE_ERROR (ecma_get_number (args[1], &num_var)))
{
return ECMA_VALUE_ERROR;
}
from_index = ecma_builtin_helper_array_index_normalize (num_var, length, is_last_index_of);
}
if (!ecma_is_value_number (args[0]))
{
return ecma_make_integer_value (-1);
}
ecma_number_t search_num = ecma_get_number_from_value (args[0]);
int32_t increment = is_last_index_of ? -element_size : element_size;
for (int32_t position = (int32_t) from_index * element_size;
(is_last_index_of ? position >= 0 : (uint32_t) position < limit);
position += increment)
{
ecma_number_t element_num = ecma_get_typedarray_element (typedarray_buffer_p + position, class_id);
if (search_num == element_num)
{
return ecma_make_number_value ((ecma_number_t) position / element_size);
}
}
return ecma_make_integer_value (-1);
} /* ecma_builtin_typedarray_prototype_index_helper */
/**
* The %TypedArray%.prototype object's 'indexOf' routine
*
* See also:
* ECMA-262 v6, 22.2.3.13
*
* @return ecma value
* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_builtin_typedarray_prototype_index_of (ecma_value_t this_arg, /**< this argument */
const ecma_value_t args[], /**< arguments list */
ecma_length_t args_number) /**< number of arguments */
{
return ecma_builtin_typedarray_prototype_index_helper (this_arg, args, args_number, false);
} /* ecma_builtin_typedarray_prototype_index_of */
/**
* The %TypedArray%.prototype object's 'lastIndexOf' routine
*
* See also:
* ECMA-262 v6, 22.2.3.16
*
* @return ecma value
* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_builtin_typedarray_prototype_last_index_of (ecma_value_t this_arg, /**< this argument */
const ecma_value_t args[], /**< arguments list */
ecma_length_t args_number) /**< number of arguments */
{
return ecma_builtin_typedarray_prototype_index_helper (this_arg, args, args_number, true);
} /* ecma_builtin_typedarray_prototype_last_index_of */
/**
* @}
* @}
@@ -70,6 +70,8 @@ ROUTINE (LIT_MAGIC_STRING_FILL, ecma_builtin_typedarray_prototype_fill, 3, 1)
ROUTINE (LIT_MAGIC_STRING_SORT, ecma_builtin_typedarray_prototype_sort, 1, 1)
ROUTINE (LIT_MAGIC_STRING_FIND, ecma_builtin_typedarray_prototype_find, 2, 1)
ROUTINE (LIT_MAGIC_STRING_FIND_INDEX, ecma_builtin_typedarray_prototype_find_index, 2, 1)
ROUTINE (LIT_MAGIC_STRING_INDEX_OF_UL, ecma_builtin_typedarray_prototype_index_of, NON_FIXED, 1)
ROUTINE (LIT_MAGIC_STRING_LAST_INDEX_OF_UL, ecma_builtin_typedarray_prototype_last_index_of, NON_FIXED, 1)
#if ENABLED (JERRY_ES2015_BUILTIN_ITERATOR)
+4 -2
View File
@@ -385,7 +385,8 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_TIME_UL, "getTime")
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_YEAR_UL, "getYear")
#endif
#if ENABLED (JERRY_BUILTIN_ARRAY) \
|| ENABLED (JERRY_BUILTIN_STRING)
|| ENABLED (JERRY_BUILTIN_STRING) \
|| ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY)
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INDEX_OF_UL, "indexOf")
#endif
#if ENABLED (JERRY_BUILTIN_ARRAY)
@@ -604,7 +605,8 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UTC_MONTH_UL, "getUTCMonth")
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_HAS_INSTANCE, "hasInstance")
#endif
#if ENABLED (JERRY_BUILTIN_ARRAY) \
|| ENABLED (JERRY_BUILTIN_STRING)
|| ENABLED (JERRY_BUILTIN_STRING) \
|| ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY)
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LAST_INDEX_OF_UL, "lastIndexOf")
#endif
#if ENABLED (JERRY_BUILTIN_ARRAY) \
@@ -0,0 +1,104 @@
/* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var normal_typedarrays = [
new Uint8Array([0, 1, 2, 3, 0, 5, 6]),
new Uint16Array([0, 1, 2, 3, 0, 5, 6]),
new Uint32Array([0, 1, 2, 3, 0, 5, 6]),
new Float32Array([0, 1, 2, 3, 0, 5, 6]),
new Float64Array([0, 1, 2, 3, 0, 5, 6]),
new Int8Array([0, 1, 2, 3, 0, 5, 6]),
new Int16Array([0, 1, 2, 3, 0, 5, 6]),
new Int32Array([0, 1, 2, 3, 0, 5, 6])
];
normal_typedarrays.forEach(function(e){
try{
e.prototype.indexOf.call (undefined);
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
var index = e.indexOf(0);
assert(index === 0);
assert(e[index] === 0);
// Regular input
assert(e.indexOf(0, 1) === 4);
assert(e.indexOf(0, 5) === -1);
assert(e.indexOf(3, -2) === -1);
assert(e.indexOf(5, -6) === 5);
assert(e.indexOf(2, -2) === -1);
// Empty input
assert(e.indexOf() === -1);
// String input
assert(e.indexOf("foo") === -1);
assert(e.indexOf(3, "foo") === 3);
// Nan, +/-Infinity
assert(e.indexOf(0, NaN) === 0);
assert(e.indexOf(0, Infinity) === -1);
assert(e.indexOf(3, -Infinity) === 3);
// Checking behavior when fromIndex is undefined
e.set([1, 2]);
assert(e.indexOf(2, undefined) === 1);
assert(e.indexOf(2) === 1);
// Checking behavior when start index >= length
e.set([11, 22, 33, 44]);
assert(e.indexOf(44, 5) === -1);
var fromIndex = {
toString: function () {
return {};
},
valueOf: function () {
return {};
}
};
e.set([0, 1]);
try {
e.indexOf(1, fromIndex);
assert(false);
} catch (e) {
assert(e instanceof TypeError);
}
});
// Checking behavior when length is zero
var empty_typedarrays = [
new Uint8Array([]),
new Uint16Array([]),
new Uint32Array([]),
new Float32Array([]),
new Float64Array([]),
new Int8Array([]),
new Int16Array([]),
new Int32Array([])
];
empty_typedarrays.forEach(function(e){
assert(e.indexOf(0) === -1);
});
@@ -0,0 +1,102 @@
// Copyright JS Foundation and other contributors, http://js.foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
var normal_typedarrays = [
new Uint8Array([0, 1, 2, 3, 0, 5, 6]),
new Uint16Array([0, 1, 2, 3, 0, 5, 6]),
new Uint32Array([0, 1, 2, 3, 0, 5, 6]),
new Float32Array([0, 1, 2, 3, 0, 5, 6]),
new Float64Array([0, 1, 2, 3, 0, 5, 6]),
new Int8Array([0, 1, 2, 3, 0, 5, 6]),
new Int16Array([0, 1, 2, 3, 0, 5, 6]),
new Int32Array([0, 1, 2, 3, 0, 5, 6])
];
normal_typedarrays.forEach(function(e){
try{
e.prototype.lastIndexOf.call (undefined);
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
var index = e.lastIndexOf(0);
assert(index === 4);
assert(e[index] === 0);
// Regular input
assert(e.lastIndexOf(0, 3) === 0);
assert(e.lastIndexOf(0, -8) === -1);
assert(e.lastIndexOf(2) === 2);
assert(e.lastIndexOf(5, 3) === -1);
assert(e.lastIndexOf(3, 6) === 3);
// Empty input
assert(e.lastIndexOf() === -1);
// String input
assert(e.lastIndexOf("foo") === -1);
assert(e.lastIndexOf(0, "foo") === 0);
// Nan, +/-Infinity
assert(e.lastIndexOf(0, NaN) === 0);
assert(e.lastIndexOf(0, Infinity) === 4);
assert(e.lastIndexOf(0, -Infinity) === -1);
// Checking behavior when fromIndex is undefined
e.set([1, 2]);
assert(e.lastIndexOf(2, undefined) === -1);
assert(e.lastIndexOf(2) === 2);
// Checking behavior when start index >= length
e.set([11, 22, 33, 44]);
assert(e.lastIndexOf(44, 8) === 3);
var fromIndex = {
toString: function () {
return {};
},
valueOf: function () {
return {};
}
};
e.set([0, 1]);
try {
e.lastIndexOf(1, fromIndex);
assert(false);
} catch (e) {
assert(e instanceof TypeError);
}
});
// Checking behavior when length is zero
var empty_typedarrays = [
new Uint8Array([]),
new Uint16Array([]),
new Uint32Array([]),
new Float32Array([]),
new Float64Array([]),
new Int8Array([]),
new Int16Array([]),
new Int32Array([])
];
empty_typedarrays.forEach(function(e){
assert(e.lastIndexOf(0) === -1);
});