Implement Array.prototype's copyWithin method (#3269)

The algorithm is based on ECMA-262 v6, 22.1.3.3

JerryScript-DCO-1.0-Signed-off-by: Adam Szilagyi aszilagy@inf.u-szeged.hu
This commit is contained in:
Szilagyi Adam 2019-11-08 15:41:20 +01:00 committed by Robert Fancsik
parent dc458111ba
commit 798655a871
4 changed files with 220 additions and 2 deletions

View File

@ -77,6 +77,7 @@ enum
ECMA_ARRAY_PROTOTYPE_KEYS,
ECMA_ARRAY_PROTOTYPE_SYMBOL_ITERATOR,
ECMA_ARRAY_PROTOTYPE_FILL,
ECMA_ARRAY_PROTOTYPE_COPY_WITHIN,
};
#define BUILTIN_INC_HEADER_NAME "ecma-builtin-array-prototype.inc.h"
@ -2268,6 +2269,131 @@ ecma_builtin_array_prototype_object_find (ecma_value_t predicate, /**< callback
/* 9. */
return is_find ? ECMA_VALUE_UNDEFINED : ecma_make_integer_value (-1);
} /* ecma_builtin_array_prototype_object_find */
/**
* The Array.prototype object's 'copyWithin' routine
*
* See also:
* ECMA-262 v6, 22.1.3.3
*
* @return ecma value
* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_builtin_array_prototype_object_copy_within (const ecma_value_t args[], /**< arguments list */
ecma_length_t args_number, /**< number of arguments */
ecma_object_t *obj_p, /**< array object */
uint32_t len) /**< array object's length */
{
if (args_number == 0)
{
return ecma_copy_value (ecma_make_object_value (obj_p));
}
ecma_number_t target_num;
ecma_value_t error = ecma_op_to_integer (args[0], &target_num);
if (ECMA_IS_VALUE_ERROR (error))
{
return error;
}
uint32_t target = (uint32_t) (ecma_number_is_negative (target_num) ? JERRY_MAX (len + target_num, 0) :
JERRY_MIN (target_num, len));
uint32_t start = 0;
uint32_t end = len;
if (args_number > 1)
{
ecma_number_t start_num;
error = ecma_op_to_integer (args[1], &start_num);
if (ECMA_IS_VALUE_ERROR (error))
{
return error;
}
start = (uint32_t) (ecma_number_is_negative (start_num) ? JERRY_MAX (len + start_num, 0) :
JERRY_MIN (start_num, len));
if (args_number > 2)
{
if (ecma_is_value_undefined (args[2]))
{
end = len;
}
else
{
ecma_number_t end_num;
error = ecma_op_to_integer (args[2], &end_num);
if (ECMA_IS_VALUE_ERROR (error))
{
return error;
}
end = (uint32_t) (ecma_number_is_negative (end_num) ? JERRY_MAX (len + end_num, 0) :
JERRY_MIN (end_num, len));
}
}
}
ecma_free_value (error);
uint32_t count = JERRY_MIN (end - start, len - target);
bool forward = true;
if (start < target && target < start + count)
{
start = start + count - 1;
target = target + count - 1;
forward = false;
}
while (count > 0)
{
ecma_value_t get_value = ecma_op_object_find_by_uint32_index (obj_p, start);
if (ECMA_IS_VALUE_ERROR (get_value))
{
return get_value;
}
ecma_value_t op_value;
if (ecma_is_value_found (get_value))
{
op_value = ecma_op_object_put_by_uint32_index (obj_p, target, get_value, true);
}
else
{
op_value = ecma_op_object_delete_by_uint32_index (obj_p, target, true);
}
ecma_free_value (get_value);
if (ECMA_IS_VALUE_ERROR (op_value))
{
return op_value;
}
ecma_free_value (op_value);
if (forward)
{
start++;
target++;
}
else
{
start--;
target--;
}
count--;
}
return ecma_copy_value (ecma_make_object_value (obj_p));
} /* ecma_builtin_array_prototype_object_copy_within */
#endif /* ENABLED (JERRY_ES2015_BUILTIN) */
#if ENABLED (JERRY_ES2015_BUILTIN_ITERATOR)
@ -2498,6 +2624,14 @@ ecma_builtin_array_prototype_dispatch_routine (uint16_t builtin_routine_id, /**<
break;
}
#if ENABLED (JERRY_ES2015_BUILTIN)
case ECMA_ARRAY_PROTOTYPE_COPY_WITHIN:
{
ret_value = ecma_builtin_array_prototype_object_copy_within (arguments_list_p,
arguments_number,
obj_p,
length);
break;
}
case ECMA_ARRAY_PROTOTYPE_FIND:
case ECMA_ARRAY_PROTOTYPE_FIND_INDEX:
{

View File

@ -66,6 +66,7 @@ ROUTINE (LIT_MAGIC_STRING_REDUCE_RIGHT_UL, ECMA_ARRAY_PROTOTYPE_REDUCE_RIGHT, NO
ROUTINE (LIT_MAGIC_STRING_FIND, ECMA_ARRAY_PROTOTYPE_FIND, 2, 1)
ROUTINE (LIT_MAGIC_STRING_FIND_INDEX, ECMA_ARRAY_PROTOTYPE_FIND_INDEX, 2, 1)
ROUTINE (LIT_MAGIC_STRING_FILL, ECMA_ARRAY_PROTOTYPE_FILL, 3, 1)
ROUTINE (LIT_MAGIC_STRING_COPY_WITHIN, ECMA_ARRAY_PROTOTYPE_COPY_WITHIN, NON_FIXED, 2)
#endif /* ENABLED (JERRY_ES2015_BUILTIN) */
#if ENABLED (JERRY_ES2015_BUILTIN_ITERATOR)
ROUTINE (LIT_MAGIC_STRING_ENTRIES, ECMA_ARRAY_PROTOTYPE_ENTRIES, 0, 0)

View File

@ -564,7 +564,8 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BYTE_OFFSET_UL, "byteOffset")
#if ENABLED (JERRY_BUILTIN_STRING)
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CHAR_CODE_AT_UL, "charCodeAt")
#endif
#if ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY)
#if ENABLED (JERRY_BUILTIN_ARRAY) && ENABLED (JERRY_ES2015_BUILTIN) \
|| ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY)
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_COPY_WITHIN, "copyWithin")
#endif
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ENUMERABLE, "enumerable")
@ -832,7 +833,8 @@ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (10, LIT_MAGIC_STRING_TYPED_ARRAY_UL)
LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (10, LIT_MAGIC_STRING_BYTE_LENGTH_UL)
#elif ENABLED (JERRY_BUILTIN_STRING)
LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (10, LIT_MAGIC_STRING_CHAR_CODE_AT_UL)
#elif ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY)
#elif ENABLED (JERRY_BUILTIN_ARRAY) && ENABLED (JERRY_ES2015_BUILTIN) \
|| ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY)
LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (10, LIT_MAGIC_STRING_COPY_WITHIN)
#else
LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (10, LIT_MAGIC_STRING_ENUMERABLE)

View File

@ -0,0 +1,81 @@
// 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 obj = {};
// Checking behavior with normal inputs
var array = ["foo", 1, "bar", obj, 2, "baz"];
assert(array.copyWithin(2,0,6).toString() === "foo,1,foo,1,bar,[object Object]");
assert(array.copyWithin(0,1,3).toString() === "1,foo,foo,1,bar,[object Object]");
assert(array.copyWithin(3,0,4).toString() === "1,foo,foo,1,foo,foo");
// Checking behavior with default inputs
var array = ["foo", 1, "bar", obj, 2, "baz"];
assert(array.copyWithin().toString() === "foo,1,bar,[object Object],2,baz");
assert(array.copyWithin(2).toString() === "foo,1,foo,1,bar,[object Object]");
assert(array.copyWithin(1,4).toString() === "foo,bar,[object Object],1,bar,[object Object]");
// Checking behavior when argument is negative or bigger then length
var array = ["foo", 1, "bar", obj, 2, "baz"];
assert(array.copyWithin(12,3,-3).toString() === "foo,1,bar,[object Object],2,baz");
assert(array.copyWithin(-2,-4,3).toString() === "foo,1,bar,[object Object],bar,baz");
assert(array.copyWithin(1,-5,30).toString() === "foo,1,bar,[object Object],bar,baz");
// Checking behavior with undefined, NaN, +/- Infinity
var array = ["foo", 1, "bar", obj, 2, "baz"];
assert(array.copyWithin(undefined).toString() === "foo,1,bar,[object Object],2,baz");
assert(array.copyWithin(2, NaN).toString()=== "foo,1,foo,1,bar,[object Object]");
assert(array.copyWithin(2,undefined,5).toString() === "foo,1,foo,1,foo,1");
var array = ["foo", 1, "bar", obj, 2, "baz"];
assert(array.copyWithin(Infinity,2,NaN).toString() === "foo,1,bar,[object Object],2,baz");
assert(array.copyWithin(Infinity,-Infinity,4).toString()=== "foo,1,bar,[object Object],2,baz");
assert(array.copyWithin(NaN,0,3).toString() === "foo,1,bar,[object Object],2,baz");
// Checking behavior when no length property defined
var obj = { copyWithin : Array.prototype.copyWithin };
obj.copyWithin();
assert(obj.length === undefined);
// Checking behavior when unable to get length
var obj = { copyWithin : Array.prototype.copyWithin };
Object.defineProperty(obj, 'length', { 'get' : function () {throw new ReferenceError ("foo"); } });
try {
obj.copyWithin(1);
assert(false)
} catch (e) {
assert(e.message === "foo");
assert(e instanceof ReferenceError);
}
// Checking behavior when unable to get element
var obj = { copyWithin : Array.prototype.copyWithin, length : 5 };
Object.defineProperty(obj, '2', { 'get' : function () {throw new ReferenceError ("foo"); } });
try {
obj.copyWithin(2);
assert(false);
} catch (e) {
assert(e.message === "foo");
assert(e instanceof ReferenceError);
}
// Checking behavior when a property is not defined
var obj = { '0' : 2, '2' : "foo", length : 3, copyWithin : Array.prototype.copyWithin };
obj.copyWithin(1);
assert(obj[0] === 2);
assert(obj[1] === 2);