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 58ce516f7a..f82d2ce527 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp @@ -469,6 +469,105 @@ ecma_builtin_array_prototype_object_to_string (ecma_value_t this_arg) /**< this return return_value; } /* ecma_builtin_array_prototype_object_to_string */ + +/** + * The Array.prototype object's 'toLocaleString' routine + * + * See also: + * ECMA-262 v5, 15.4.4.3 + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_array_prototype_object_to_locale_string (const ecma_value_t this_arg) /**< this argument */ +{ + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + /* 1. */ + ECMA_TRY_CATCH (obj_value, + ecma_op_to_object (this_arg), + ret_value); + + ecma_object_t *obj_p = ecma_get_object_from_completion_value (obj_value); + + ecma_string_t *length_magic_string_p = ecma_get_magic_string (ECMA_MAGIC_STRING_LENGTH); + + /* 2. */ + ECMA_TRY_CATCH (length_value, + ecma_op_object_get (obj_p, length_magic_string_p), + ret_value); + + /* 3. */ + ECMA_OP_TO_NUMBER_TRY_CATCH (length_number, + length_value, + ret_value); + + uint32_t length = ecma_number_to_uint32 (length_number); + + /* 4. Implementation-defined: set the separator to a single comma character */ + ecma_string_t *separator_string_p = ecma_get_magic_string (ECMA_MAGIC_STRING_COMMA_CHAR); + + /* 5. */ + if (length == 0) + { + ecma_string_t *empty_string_p = ecma_get_magic_string (ECMA_MAGIC_STRING__EMPTY); + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (empty_string_p)); + } + else + { + /* 7-8. */ + ECMA_TRY_CATCH (first_value, + ecma_builtin_helper_get_to_locale_string_at_index (obj_p, 0), + ret_value); + + ecma_string_t *return_string_p = ecma_copy_or_ref_ecma_string (ecma_get_string_from_value (first_value)); + + /* 9-10. */ + for (uint32_t k = 1; ecma_is_completion_value_empty (ret_value) && (k < length); ++k) + { + ecma_string_t *part_string_p = ecma_concat_ecma_strings (return_string_p, separator_string_p); + + ECMA_TRY_CATCH (next_string_value, + ecma_builtin_helper_get_to_locale_string_at_index (obj_p, k), + ret_value); + + ecma_string_t *next_string_p = ecma_get_string_from_completion_value (next_string_value); + + ecma_deref_ecma_string (return_string_p); + + return_string_p = ecma_concat_ecma_strings (part_string_p, next_string_p); + + ECMA_FINALIZE (next_string_value); + + ecma_deref_ecma_string (part_string_p); + } + + if (ecma_is_completion_value_empty (ret_value)) + { + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (return_string_p)); + } + else + { + ecma_deref_ecma_string (return_string_p); + } + + ECMA_FINALIZE (first_value); + } + + ecma_deref_ecma_string (separator_string_p); + + ECMA_OP_TO_NUMBER_FINALIZE (length_number); + + ECMA_FINALIZE (length_value); + + ecma_deref_ecma_string (length_magic_string_p); + + ECMA_FINALIZE (obj_value); + + return ret_value; +} /* ecma_builtin_array_prototype_object_to_locale_string */ + /** * The Array.prototype object's 'pop' 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 cafd328e3b..7700f01881 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 @@ -62,6 +62,7 @@ ROUTINE (ECMA_MAGIC_STRING_FOR_EACH_UL, ecma_builtin_array_prototype_object_for_ ROUTINE (ECMA_MAGIC_STRING_CONCAT, ecma_builtin_array_prototype_object_concat, NON_FIXED, 1) ROUTINE (ECMA_MAGIC_STRING_JOIN, ecma_builtin_array_prototype_join, 1, 1) ROUTINE (ECMA_MAGIC_STRING_TO_STRING_UL, ecma_builtin_array_prototype_object_to_string, 0, 0) +ROUTINE (ECMA_MAGIC_STRING_TO_LOCALE_STRING_UL, ecma_builtin_array_prototype_object_to_locale_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_REVERSE, ecma_builtin_array_prototype_object_reverse, 0, 0) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp index e51e9a59a9..9bb38c27dc 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp @@ -18,9 +18,11 @@ #include "ecma-builtins.h" #include "ecma-conversion.h" +#include "ecma-function-object.h" #include "ecma-exceptions.h" #include "ecma-helpers.h" #include "ecma-objects.h" +#include "ecma-try-catch-macro.h" /** \addtogroup ecma ECMA * @{ @@ -122,6 +124,71 @@ ecma_builtin_helper_object_to_string (const ecma_value_t this_arg) /**< this arg return ecma_make_normal_completion_value (ecma_make_string_value (ret_string_p)); } /* ecma_builtin_helper_object_to_string */ +/** + * The Array.prototype's 'toLocaleString' single element operation routine + * + * See also: + * ECMA-262 v5, 15.4.4.3 steps 6-8 and 10.b-d + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +ecma_completion_value_t +ecma_builtin_helper_get_to_locale_string_at_index (ecma_object_t *obj_p, /** < this object */ + uint32_t index) /** < array index */ +{ + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + ecma_string_t *index_string_p = ecma_new_ecma_string_from_uint32 (index); + + ECMA_TRY_CATCH (index_value, + ecma_op_object_get (obj_p, index_string_p), + ret_value); + + if (ecma_is_value_undefined (index_value) || ecma_is_value_null (index_value)) + { + ecma_string_t *return_string_p = ecma_get_magic_string (ECMA_MAGIC_STRING__EMPTY); + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (return_string_p)); + } + else + { + ECMA_TRY_CATCH (index_obj_value, + ecma_op_to_object (index_value), + ret_value); + + ecma_object_t *index_obj_p = ecma_get_object_from_value (index_obj_value); + ecma_string_t *locale_string_magic_string_p = ecma_get_magic_string (ECMA_MAGIC_STRING_TO_LOCALE_STRING_UL); + + ECMA_TRY_CATCH (to_locale_value, + ecma_op_object_get (index_obj_p, locale_string_magic_string_p), + ret_value); + + if (ecma_op_is_callable (to_locale_value)) + { + ecma_object_t *locale_func_obj_p = ecma_get_object_from_value (to_locale_value); + ret_value = ecma_op_function_call (locale_func_obj_p, + ecma_make_object_value (index_obj_p), + NULL, + 0); + } + else + { + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); + } + + ECMA_FINALIZE (to_locale_value); + + ecma_deref_ecma_string (locale_string_magic_string_p); + + ECMA_FINALIZE (index_obj_value); + } + + ECMA_FINALIZE (index_value); + + ecma_deref_ecma_string (index_string_p); + + return ret_value; +} /* ecma_builtin_helper_get_to_locale_string_at_index */ + /** * @} * @} diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h index b773df79eb..54918129f0 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h @@ -27,6 +27,7 @@ */ extern ecma_completion_value_t ecma_builtin_helper_object_to_string (const ecma_value_t this_arg); +extern ecma_completion_value_t ecma_builtin_helper_get_to_locale_string_at_index (ecma_object_t *obj_p, uint32_t index); /** * @} diff --git a/tests/jerry/array_prototype_tolocalestring.js b/tests/jerry/array_prototype_tolocalestring.js new file mode 100644 index 0000000000..6f30aaed37 --- /dev/null +++ b/tests/jerry/array_prototype_tolocalestring.js @@ -0,0 +1,52 @@ +// 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. + +assert ([].toLocaleString() === ""); +assert ([1].toLocaleString() === "1"); +assert ([1,2].toLocaleString() === "1,2"); +assert ([1,2,3].toLocaleString() === "1,2,3"); + +var test_ok = { + length: 1, + toLocaleString: function() { return "1"; } +}; + +assert ([3, test_ok, 4, test_ok].toLocaleString() === "3,1,4,1"); + + +var test_fail = { + toLocaleString: "FAIL" +}; + +try { + [test_fail].toLocaleString(); + assert (false); +} catch (e) { + assert (e instanceof TypeError); +} + + +var test_fail_call = { + toLocaleString: function() { throw new ReferenceError("foo"); } +}; + + +try { + [1, 2, test_fail_call].toLocaleString(); + assert (false); +} catch (e) { + assert (e.message === "foo"); + assert (e instanceof ReferenceError); +}