diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp index 28f873aad0..44120a3bdf 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp @@ -266,6 +266,103 @@ ecma_builtin_array_prototype_object_push (ecma_value_t this_arg, /**< this argum return ret_value; } /* ecma_builtin_array_prototype_object_push */ +/** + * The Array.prototype object's 'unshift' routine + * + * See also: + * ECMA-262 v5, 15.4.4.13 + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_array_prototype_object_unshift (ecma_value_t this_arg, /**< this argument */ + const ecma_value_t args[], /**< arguments list */ + ecma_length_t args_number) /**< number of arguments */ +{ + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + /* 1. */ + ECMA_TRY_CATCH (obj_this, + ecma_op_to_object (this_arg), + ret_value); + + ecma_object_t *obj_p = ecma_get_object_from_value (obj_this); + ecma_string_t *magic_string_length_p = ecma_get_magic_string (ECMA_MAGIC_STRING_LENGTH); + + /* 2. */ + ECMA_TRY_CATCH (len_value, + ecma_op_object_get (obj_p, magic_string_length_p), + ret_value); + + ECMA_OP_TO_NUMBER_TRY_CATCH (len_number, len_value, ret_value); + + /* 3. */ + uint32_t len = ecma_number_to_uint32 (len_number); + + /* 5. and 6. */ + for (uint32_t k = len; k > 0 && ecma_is_completion_value_empty (ret_value); k--) + { + /* 6.a */ + ecma_string_t *from_str_p = ecma_new_ecma_string_from_uint32 (k - 1); + /* 6.b */ + ecma_string_t *to_str_p = ecma_new_ecma_string_from_uint32 (k + args_number - 1); + + /* 6.c */ + if (ecma_op_object_get_property (obj_p, from_str_p) != NULL) + { + /* 6.d.i */ + ECMA_TRY_CATCH (get_value, ecma_op_object_get (obj_p, from_str_p), ret_value); + /* 6.d.ii */ + ECMA_TRY_CATCH (put_value, ecma_op_object_put (obj_p, to_str_p, get_value, true), ret_value); + + ECMA_FINALIZE (put_value); + ECMA_FINALIZE (get_value); + } + else + { + /* 6.e.i */ + ECMA_TRY_CATCH (del_value, ecma_op_object_delete (obj_p, to_str_p, true), ret_value); + ECMA_FINALIZE (del_value); + } + + ecma_deref_ecma_string (to_str_p); + ecma_deref_ecma_string (from_str_p); + } + + for (uint32_t arg_index = 0; + arg_index < args_number && ecma_is_completion_value_empty (ret_value); + arg_index++) + { + ecma_string_t *to_str_p = ecma_new_ecma_string_from_uint32 (arg_index); + /* 9.b */ + ECMA_TRY_CATCH (put_value, ecma_op_object_put (obj_p, to_str_p, args[arg_index], true), ret_value); + ECMA_FINALIZE (put_value); + ecma_deref_ecma_string (to_str_p); + } + + if (ecma_is_completion_value_empty (ret_value)) + { + /* 10. */ + ECMA_TRY_CATCH (set_length_value, + ecma_builtin_array_prototype_helper_set_length (obj_p, len + args_number), + ret_value); + + ecma_number_t *num_p = ecma_alloc_number (); + *num_p = ecma_uint32_to_number (len + args_number); + ret_value = ecma_make_normal_completion_value (ecma_make_number_value (num_p)); + + ECMA_FINALIZE (set_length_value); + } + + ECMA_OP_TO_NUMBER_FINALIZE (len_number); + ECMA_FINALIZE (len_value); + ecma_deref_ecma_string (magic_string_length_p); + ECMA_FINALIZE (obj_this); + + return ret_value; +} /* ecma_builtin_array_prototype_object_unshift */ + /** * @} * @} 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 dd7609f7ad..640e831a4a 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 @@ -61,6 +61,7 @@ NUMBER_VALUE (ECMA_MAGIC_STRING_LENGTH, ROUTINE (ECMA_MAGIC_STRING_TO_STRING_UL, ecma_builtin_array_prototype_object_to_string, 0, 0) ROUTINE (ECMA_MAGIC_STRING_POP, ecma_builtin_array_prototype_object_pop, 0, 0) ROUTINE (ECMA_MAGIC_STRING_PUSH, ecma_builtin_array_prototype_object_push, NON_FIXED, 1) +ROUTINE (ECMA_MAGIC_STRING_UNSHIFT, ecma_builtin_array_prototype_object_unshift, NON_FIXED, 1) #undef OBJECT_ID #undef SIMPLE_VALUE diff --git a/tests/jerry/array_prototype_unshift.js b/tests/jerry/array_prototype_unshift.js new file mode 100644 index 0000000000..420071f9e7 --- /dev/null +++ b/tests/jerry/array_prototype_unshift.js @@ -0,0 +1,104 @@ +// Copyright 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. +// 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 array = [] + +assert(array.length === 0); + +array.unshift("foo"); +assert(array.length === 1); +assert(array[0] === "foo"); + +array.unshift(new Array()) +assert(array.length === 2); +assert(array[0] instanceof Array); +assert(array[1] === "foo") + +array.unshift(Infinity); +assert(array.length === 3); +assert(array[0] === Infinity); +assert(array[1] instanceof Array); +assert(array[2] === "foo") + +array.unshift("bar", 0); +assert(array.length === 5); +assert(array[0] === "bar"); +assert(array[1] === 0); +assert(array[2] === Infinity); +assert(array[3] instanceof Array); +assert(array[4] === "foo") + + +// Checking behavior when no length property defined +var obj = { unshift : Array.prototype.unshift }; + +assert(obj.length === undefined); +obj.unshift(1,2,3); +assert(obj.length === 3); + +// Checking behavior when unable to get length +var obj = { unshift : Array.prototype.unshift }; +Object.defineProperty(obj, 'length', { 'get' : function () {throw new ReferenceError ("foo"); } }); + +try { + obj.unshift(1); + assert(false) +} catch (e) { + assert(e.message === "foo"); + assert(e instanceof ReferenceError); +} + +// Checking behavior when unable to set length +var obj = { unshift : Array.prototype.unshift }; +Object.defineProperty(obj, 'length', { 'set' : function () {throw new ReferenceError ("foo"); } }); + +try { + obj.unshift(2); + assert(false) +} catch (e) { + assert(e.message === "foo"); + assert(e instanceof ReferenceError); +} + +// Checking behavior when unable shift elements +var obj = { unshift : Array.prototype.unshift, length : 1 }; +Object.defineProperty(obj, '0', { 'get' : function () {throw new ReferenceError ("foo"); } }); + +try { + obj.unshift(3); + assert(false); +} catch (e) { + assert(e.message === "foo"); + assert(e instanceof ReferenceError); +} + +var obj = { unshift : Array.prototype.unshift, length : 1 }; +Object.defineProperty(obj, '0', { 'set' : function () {throw new ReferenceError ("foo"); } }); + +try { + obj.unshift(4); + assert(false); +} catch (e) { + assert(e.message === "foo"); + assert(e instanceof ReferenceError); +} + +// Checking behavior when a property is not defined +var obj = { '0' : "foo", '2' : "bar", length : 3, unshift : Array.prototype.unshift }; +assert(obj.unshift("baz") === 4); +assert(obj[0] === "baz"); +assert(obj[1] === "foo"); +assert(obj[2] === undefined); +assert(obj[3] === "bar"); \ No newline at end of file