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 0df54097b5..87b20271d7 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp @@ -2452,6 +2452,114 @@ ecma_builtin_array_prototype_object_splice (ecma_value_t this_arg, /**< this arg return ret_value; } /* ecma_builtin_array_prototype_object_splice */ +/** + * The Array.prototype object's 'map' routine + * + * See also: + * ECMA-262 v5, 15.4.4.19 + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_array_prototype_object_map (ecma_value_t this_arg, /**< this argument */ + ecma_value_t arg1, /**< callbackfn */ + ecma_value_t arg2) /**< thisArg */ +{ + 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); + + /* 4 */ + if (!ecma_op_is_callable (arg1)) + { + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); + } + else + { + ecma_number_t *num_p = ecma_alloc_number (); + ecma_object_t *func_object_p; + JERRY_ASSERT (ecma_is_value_object (arg1)); + func_object_p = ecma_get_object_from_value (arg1); + + /* 5: arg2 is simply used as T */ + + /* 6 */ + ecma_completion_value_t new_array = ecma_op_create_array_object (NULL, 0, false); + JERRY_ASSERT (ecma_is_completion_value_normal (new_array)); + ecma_object_t *new_array_p = ecma_get_object_from_completion_value (new_array); + + /* 7-8 */ + ecma_value_t current_index; + + for (uint32_t index = 0; index < len && ecma_is_completion_value_empty (ret_value); ++index) + { + /* 8a */ + ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (index); + /* 8b */ + if (ecma_op_object_get_property (obj_p, index_str_p) != NULL) + { + /* 8c-i */ + ECMA_TRY_CATCH (current_value, ecma_op_object_get (obj_p, index_str_p), ret_value); + /* 8c-ii */ + *num_p = ecma_uint32_to_number (index); + current_index = ecma_make_number_value (num_p); + ecma_value_t call_args[] = {current_value, current_index, obj_this}; + + ECMA_TRY_CATCH (mapped_value, ecma_op_function_call (func_object_p, arg2, call_args, 3), ret_value); + + /* 8c-iii + * By definition we should use [[DefineOwnProperty]] here, but since [[Put]] will create the + * same property that we need, we can use it for simplicity. No need for a try-catch block + * since it is called with is_throw = false. + * ecma_op_to_boolean always returns a simple value, so no need to free. + */ + ecma_completion_value_t put_comp_value = ecma_op_object_put (new_array_p, index_str_p, mapped_value, false); + JERRY_ASSERT (ecma_is_completion_value_normal_true (put_comp_value)); + ecma_free_completion_value (put_comp_value); + + ECMA_FINALIZE (mapped_value); + ECMA_FINALIZE (current_value); + } + + ecma_deref_ecma_string (index_str_p); + } + + if (ecma_is_completion_value_empty (ret_value)) + { + ret_value = new_array; + } + else + { + ecma_free_completion_value (new_array); + } + + ecma_dealloc_number (num_p); + } + + 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_map */ /** * The Array.prototype object's 'reduce' routine 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 1702e610fb..1b0cd7b03f 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 @@ -76,6 +76,7 @@ ROUTINE (ECMA_MAGIC_STRING_SOME, ecma_builtin_array_prototype_object_some, 2, 1) ROUTINE (ECMA_MAGIC_STRING_SLICE, ecma_builtin_array_prototype_object_slice, 2, 2) ROUTINE (ECMA_MAGIC_STRING_SPLICE, ecma_builtin_array_prototype_object_splice, NON_FIXED, 2) ROUTINE (ECMA_MAGIC_STRING_FILTER, ecma_builtin_array_prototype_object_filter, 2, 1) +ROUTINE (ECMA_MAGIC_STRING_MAP, ecma_builtin_array_prototype_object_map, 2, 1) ROUTINE (ECMA_MAGIC_STRING_REDUCE, ecma_builtin_array_prototype_object_reduce, 2, 1) #undef OBJECT_ID diff --git a/tests/jerry/array_prototype_map.js b/tests/jerry/array_prototype_map.js new file mode 100644 index 0000000000..cc2c5acc81 --- /dev/null +++ b/tests/jerry/array_prototype_map.js @@ -0,0 +1,101 @@ +// 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. + +// helper function - simple implementation +Array.prototype.equals = function (array) { + if (this.length != array.length) + return false; + + for (var i = 0; i < this.length; i++) { + if (this[i] instanceof Array && array[i] instanceof Array) { + if (!this[i].equals(array[i])) + return false; + } + else if (this[i] != array[i]) { + return false; + } + } + + return true; +} + +// check function type +try { + [0].map(new Object()); + assert(false); +} catch(e) { + assert(e instanceof TypeError); +} + +// various checks +assert ([1, 4, 9].map(Math.sqrt).equals([1, 2, 3])); + +assert (isNaN([1, 4, "X"].map(Number)[2])); + +var func = function(val, idx) { + return val + idx; +}; + +assert ([1, 4, 9].map(func).equals([1,5,11])); + +assert ([1, "X", 10].map(func).equals([1, "X1", 12])); + +var long_array = [0, 1]; +assert (long_array.map(func).equals([0,2])); + +long_array[100] = 1; +assert (long_array.map(func).equals([0,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,101])); + +// check behavior when unable to get length +var obj = {}; +Object.defineProperty(obj, 'length', { 'get' : function () {throw new ReferenceError ("foo"); } }); +obj.map = Array.prototype.map; + +try { + obj.map(func); + assert(false); +} catch (e) { + assert(e.message === "foo"); + assert(e instanceof ReferenceError); +} + +// check behavior when unable to get element +var obj = {} +obj.length = 1; +Object.defineProperty(obj, '0', { 'get' : function () {throw new ReferenceError ("foo"); } }); +obj.map = Array.prototype.map + +try { + obj.map(func); + assert(false); +} catch (e) { + assert(e.message === "foo"); + assert(e instanceof ReferenceError); +} + +// check thisArg +var thisArg = {add: 10}; +var func2 = function(value) { + return this.add + value; +} +assert([1,2].map(func2, thisArg).equals([11, 12])); + +// check passed Object +var array_example = [1,2]; +Object.defineProperty(array_example, 'const', { 'get' : function () {return "CT";} }); +var func3 = function(value, idx, thisobj) { + return value * idx + thisobj.const; +} +assert(array_example.map(func3).equals(["0CT", "2CT"]));