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 a43a7341c..000bdc7c7 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c @@ -76,6 +76,7 @@ enum ECMA_ARRAY_PROTOTYPE_VALUES, ECMA_ARRAY_PROTOTYPE_KEYS, ECMA_ARRAY_PROTOTYPE_SYMBOL_ITERATOR, + ECMA_ARRAY_PROTOTYPE_FILL, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-array-prototype.inc.h" @@ -1919,6 +1920,98 @@ ecma_builtin_array_reduce_from (ecma_value_t callbackfn, /**< routine's 1st argu } /* ecma_builtin_array_reduce_from */ #if ENABLED (JERRY_ES2015_BUILTIN) +/** + * The Array.prototype object's 'fill' routine + * + * Note: this method only supports length up to uint32, instead of max_safe_integer + * + * See also: + * ECMA-262 v6, 22.1.3.6 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_array_prototype_fill (ecma_value_t value, /**< value */ + ecma_value_t start_val, /**< start value */ + ecma_value_t end_val, /**< end value */ + ecma_object_t *obj_p, /**< array object */ + uint32_t len) /**< array object's length */ +{ + /* 5 */ + ecma_number_t start; + ecma_value_t to_number = ecma_op_to_integer (start_val, &start); + + /* 6 */ + if (ECMA_IS_VALUE_ERROR (to_number)) + { + return to_number; + } + + ecma_number_t k; + + /* 7 */ + if (ecma_number_is_negative (start)) + { + k = JERRY_MAX (((ecma_number_t) len + start), 0); + } + else + { + k = JERRY_MIN (start, (ecma_number_t) len); + } + + /* 8 */ + ecma_number_t relative_end; + + if (ecma_is_value_undefined (end_val)) + { + relative_end = (ecma_number_t) len; + } + else + { + to_number = ecma_op_to_integer (end_val, &relative_end); + + if (ECMA_IS_VALUE_ERROR (to_number)) + { + return to_number; + } + } + + /* 10 */ + ecma_number_t final; + if (relative_end < 0) + { + final = JERRY_MAX (((ecma_number_t) len + relative_end), 0); + } + else + { + final = JERRY_MIN (relative_end, (ecma_number_t) len); + } + + /* 11 */ + while (k < final) + { + /* 11.a */ + ecma_string_t *pk = ecma_new_ecma_string_from_number (k); + + /* 11.b */ + ecma_value_t put_val = ecma_op_object_put (obj_p, pk, value, true); + ecma_deref_ecma_string (pk); + + /* 11. c */ + if (ECMA_IS_VALUE_ERROR (put_val)) + { + return put_val; + } + + /* 11.d */ + k++; + } + + ecma_ref_object (obj_p); + return ecma_make_object_value (obj_p); +} /* ecma_builtin_array_prototype_fill */ + /** * The Array.prototype object's 'find' and 'findIndex' routine * @@ -2249,6 +2342,15 @@ ecma_builtin_array_prototype_dispatch_routine (uint16_t builtin_routine_id, /**< length); break; } + case ECMA_ARRAY_PROTOTYPE_FILL: + { + ret_value = ecma_builtin_array_prototype_fill (routine_arg_1, + routine_arg_2, + arguments_list_p[2], + obj_p, + length); + break; + } #endif /* ENABLED (JERRY_ES2015_BUILTIN) */ default: { 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 ad86d85e7..18126ba3e 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 @@ -65,6 +65,7 @@ ROUTINE (LIT_MAGIC_STRING_REDUCE_RIGHT_UL, ECMA_ARRAY_PROTOTYPE_REDUCE_RIGHT, NO #if ENABLED (JERRY_ES2015_BUILTIN) 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) #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 be61e7810..9213b85cb 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -128,11 +128,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EVAL, "eval") #if ENABLED (JERRY_BUILTIN_REGEXP) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EXEC, "exec") #endif -#if ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY) -LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FILL, "fill") -#endif #if ENABLED (JERRY_BUILTIN_ARRAY) && ENABLED (JERRY_ES2015_BUILTIN) \ || ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY) +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FILL, "fill") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FIND, "find") #endif #if ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY) diff --git a/tests/jerry/es2015/array-prototype-fill.js b/tests/jerry/es2015/array-prototype-fill.js new file mode 100644 index 000000000..67f17cb5b --- /dev/null +++ b/tests/jerry/es2015/array-prototype-fill.js @@ -0,0 +1,177 @@ +// 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. + +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +function assertArrayEquals (array1, array2) { + if (array1.length !== array2.length) { + return false; + } + + for (var i = 0; i < array1.length; i++) { + if (array1[i] !== array2[i]) { + return false; + } + } + + return true; +} + +assert (1 === Array.prototype.fill.length); + +assert (assertArrayEquals ([].fill (8), [])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (), [undefined, undefined, undefined, undefined, undefined])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (8), [8, 8, 8, 8, 8])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (8, 1), [0, 8, 8, 8, 8])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (8, 10), [0, 0, 0, 0, 0])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (8, -5), [8, 8, 8, 8, 8])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (8, 1, 4), [0, 8, 8, 8, 0])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (8, 1, -1), [0, 8, 8, 8, 0])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (8, 1, 42), [0, 8, 8, 8, 8])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (8, -3, 42), [0, 0, 8, 8, 8])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (8, -3, 4), [0, 0, 8, 8, 0])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (8, -2, -1), [0, 0, 0, 8, 0])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (8, -1, -3), [0, 0, 0, 0, 0])); +assert (assertArrayEquals ([0, 0, 0, 0, 0].fill (8, undefined, 4), [8, 8, 8, 8, 0])); +assert (assertArrayEquals ([ , , , , 0].fill (8, 1, 3), [, 8, 8, , 0])); + + +// If the range is empty, the array is not actually modified and +// should not throw, even when applied to a frozen object. +assert (assertArrayEquals (Object.freeze ([1, 2, 3]).fill (0, 0, 0), [1, 2, 3])); + +// Test exceptions +try { + Object.freeze ([0]).fill (); + assert (false); +} catch (e) { + assert (e instanceof TypeError); +} +try { + Array.prototype.fill.call (null) + assert (false); +} catch (e) { + assert (e instanceof TypeError); +} +try { + Array.prototype.fill.call (undefined) + assert (false); +} catch (e) { + assert (e instanceof TypeError); +} + +function TestFillObjectWithAccessors () { + var kLength = 5; + + var log = []; + + var object = { + length: kLength, + get 1 () { + log.push ("get 1"); + return this.foo; + }, + + set 1 (val) { + log.push ("set 1 " + val); + this.foo = val; + } + }; + + Array.prototype.fill.call (object, 42); + + assert (kLength === object.length); + assert (assertArrayEquals (["set 1 42"], log)); + + for (var i = 0; i < kLength; ++i) { + assert (42 === object[i]); + } +} +TestFillObjectWithAccessors (); + +function TestFillObjectWithMaxNumberLength () { + var kMaxSafeInt = Math.pow (2, 32) - 1; + var object = {}; + object.length = kMaxSafeInt; + + Array.prototype.fill.call (object, 42, Math.pow (2, 32) - 4); + + assert (kMaxSafeInt === object.length); + assert (42 === object[kMaxSafeInt - 3]); + assert (42 === object[kMaxSafeInt - 2]); + assert (42 === object[kMaxSafeInt - 1]); +} +TestFillObjectWithMaxNumberLength (); + +function TestFillObjectWithPrototypeAccessors () { + var kLength = 5; + var log = []; + var proto = { + get 1 () { + log.push ("get 0"); + return this.foo; + }, + + set 1 (val) { + log.push ("set 1 " + val); + this.foo = val; + } + }; + + var object = { 0:0, 2:2, length: kLength}; + Object.setPrototypeOf (object, proto); + + Array.prototype.fill.call (object, "42"); + + assert (kLength === object.length); + assert (assertArrayEquals (["set 1 42"], log)); + assert (object.hasOwnProperty (0) == true); + assert (object.hasOwnProperty (1) == false); + assert (object.hasOwnProperty (2) == true); + assert (object.hasOwnProperty (3) == true); + assert (object.hasOwnProperty (4) == true); + + for (var i = 0; i < kLength; ++i) { + assert ("42" === object[i]); + } +} +TestFillObjectWithPrototypeAccessors (); + +function TestFillSealedObject () { + var object = { length: 42 }; + Object.seal (object); + + try { + Array.prototype.fill.call (object); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } +} +TestFillSealedObject (); + +function TestFillFrozenObject () { + var object = { length: 42 }; + Object.freeze (object); + + try { + Array.prototype.fill.call (object); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } +} +TestFillFrozenObject ();