diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c index 2690637fb..0f703df4c 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c @@ -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: { diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.inc.h index 18126ba3e..de550346a 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.inc.h @@ -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) diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 40acfe228..367b57038 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -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) diff --git a/tests/jerry/es2015/array-prototype-copywithin.js b/tests/jerry/es2015/array-prototype-copywithin.js new file mode 100644 index 000000000..075b1364c --- /dev/null +++ b/tests/jerry/es2015/array-prototype-copywithin.js @@ -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);