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 ff880dbe5d..40db2a3e2f 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp @@ -15,6 +15,7 @@ #include "ecma-alloc.h" #include "ecma-array-object.h" +#include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-comparison.h" #include "ecma-conversion.h" @@ -180,7 +181,42 @@ ecma_builtin_array_prototype_object_for_each (ecma_value_t this_arg, /**< this a static ecma_completion_value_t ecma_builtin_array_prototype_object_to_string (ecma_value_t this_arg) /**< this argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg); + ecma_completion_value_t return_value = ecma_make_empty_completion_value (); + + /* 1. */ + ECMA_TRY_CATCH (obj_this_value, + ecma_op_to_object (this_arg), + return_value); + + ecma_object_t *obj_p = ecma_get_object_from_value (obj_this_value); + + ecma_string_t *join_magic_string_p = ecma_get_magic_string (ECMA_MAGIC_STRING_JOIN); + + /* 2. */ + ECMA_TRY_CATCH (join_value, + ecma_op_object_get (obj_p, join_magic_string_p), + return_value); + + if (!ecma_op_is_callable (join_value)) + { + /* 3. */ + return_value = ecma_builtin_helper_object_to_string (this_arg); + } + else + { + /* 4. */ + ecma_object_t *join_func_obj_p = ecma_get_object_from_value (join_value); + + return_value = ecma_op_function_call (join_func_obj_p, this_arg, NULL, 0); + } + + ECMA_FINALIZE (join_value); + + ecma_deref_ecma_string (join_magic_string_p); + + ECMA_FINALIZE (obj_this_value); + + return return_value; } /* ecma_builtin_array_prototype_object_to_string */ /** diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp new file mode 100644 index 0000000000..e51e9a59a9 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp @@ -0,0 +1,129 @@ +/* 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. + */ + +#include "ecma-builtin-helpers.h" + +#include "ecma-builtins.h" +#include "ecma-conversion.h" +#include "ecma-exceptions.h" +#include "ecma-helpers.h" +#include "ecma-objects.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmabuiltinhelpers ECMA builtin helper operations + * @{ + */ + +/** + * Common implementation of the Object.prototype.toString routine + * + * See also: + * ECMA-262 v5, 15.2.4.2 + * + * Used by: + * - The Object.prototype.toString routine. + * - The Array.prototype.toString routine as fallback. + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ + +ecma_completion_value_t +ecma_builtin_helper_object_to_string (const ecma_value_t this_arg) /**< this argument */ +{ + ecma_magic_string_id_t type_string; + + if (ecma_is_value_undefined (this_arg)) + { + type_string = ECMA_MAGIC_STRING_UNDEFINED_UL; + } + else if (ecma_is_value_null (this_arg)) + { + type_string = ECMA_MAGIC_STRING_NULL_UL; + } + else + { + ecma_completion_value_t obj_this = ecma_op_to_object (this_arg); + + if (!ecma_is_completion_value_normal (obj_this)) + { + return obj_this; + } + + JERRY_ASSERT (ecma_is_value_object (ecma_get_completion_value_value (obj_this))); + + ecma_object_t *obj_p = ecma_get_object_from_completion_value (obj_this); + + ecma_property_t *class_prop_p = ecma_get_internal_property (obj_p, + ECMA_INTERNAL_PROPERTY_CLASS); + type_string = (ecma_magic_string_id_t) class_prop_p->u.internal_property.value; + + ecma_free_completion_value (obj_this); + } + + ecma_string_t *ret_string_p; + + /* Building string "[object #type#]" where type is 'Undefined', + 'Null' or one of possible object's classes. + The string with null character is maximum 19 characters long. */ + const ssize_t buffer_size = 19; + MEM_DEFINE_LOCAL_ARRAY (str_buffer, buffer_size, ecma_char_t); + + const ecma_char_t *left_square_zt_str_p = ecma_get_magic_string_zt (ECMA_MAGIC_STRING_LEFT_SQUARE_CHAR); + const ecma_char_t *object_zt_str_p = ecma_get_magic_string_zt (ECMA_MAGIC_STRING_OBJECT); + const ecma_char_t *space_zt_str_p = ecma_get_magic_string_zt (ECMA_MAGIC_STRING_SPACE_CHAR); + const ecma_char_t *type_name_zt_str_p = ecma_get_magic_string_zt (type_string); + const ecma_char_t *right_square_zt_str_p = ecma_get_magic_string_zt (ECMA_MAGIC_STRING_RIGHT_SQUARE_CHAR); + + ecma_char_t *buffer_ptr = str_buffer; + ssize_t buffer_size_left = buffer_size; + buffer_ptr = ecma_copy_zt_string_to_buffer (left_square_zt_str_p, + buffer_ptr, + buffer_size_left); + buffer_size_left = buffer_size - (buffer_ptr - str_buffer) * (ssize_t) sizeof (ecma_char_t); + buffer_ptr = ecma_copy_zt_string_to_buffer (object_zt_str_p, + buffer_ptr, + buffer_size_left); + buffer_size_left = buffer_size - (buffer_ptr - str_buffer) * (ssize_t) sizeof (ecma_char_t); + buffer_ptr = ecma_copy_zt_string_to_buffer (space_zt_str_p, + buffer_ptr, + buffer_size_left); + buffer_size_left = buffer_size - (buffer_ptr - str_buffer) * (ssize_t) sizeof (ecma_char_t); + buffer_ptr = ecma_copy_zt_string_to_buffer (type_name_zt_str_p, + buffer_ptr, + buffer_size_left); + buffer_size_left = buffer_size - (buffer_ptr - str_buffer) * (ssize_t) sizeof (ecma_char_t); + buffer_ptr = ecma_copy_zt_string_to_buffer (right_square_zt_str_p, + buffer_ptr, + buffer_size_left); + buffer_size_left = buffer_size - (buffer_ptr - str_buffer) * (ssize_t) sizeof (ecma_char_t); + + JERRY_ASSERT (buffer_size_left >= 0); + + ret_string_p = ecma_new_ecma_string (str_buffer); + + MEM_FINALIZE_LOCAL_ARRAY (str_buffer); + + return ecma_make_normal_completion_value (ecma_make_string_value (ret_string_p)); +} /* ecma_builtin_helper_object_to_string */ + +/** + * @} + * @} + * @} + */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h new file mode 100644 index 0000000000..b773df79eb --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h @@ -0,0 +1,36 @@ +/* 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. + */ + +#ifndef ECMA_OBJECT_PROTOTYPE_H +#define ECMA_OBJECT_PROTOTYPE_H + +#include "ecma-globals.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmabuiltinhelpers ECMA builtin helper operations + * @{ + */ + +extern ecma_completion_value_t ecma_builtin_helper_object_to_string (const ecma_value_t this_arg); + +/** + * @} + * @} + */ + +#endif /* !ECMA_OBJECT_PROPERTY_H */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.cpp index ed66fe449f..24785446dc 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.cpp @@ -14,6 +14,7 @@ */ #include "ecma-alloc.h" +#include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" @@ -55,80 +56,7 @@ static ecma_completion_value_t ecma_builtin_object_prototype_object_to_string (ecma_value_t this_arg) /**< this argument */ { - ecma_magic_string_id_t type_string; - - if (ecma_is_value_undefined (this_arg)) - { - type_string = ECMA_MAGIC_STRING_UNDEFINED_UL; - } - else if (ecma_is_value_null (this_arg)) - { - type_string = ECMA_MAGIC_STRING_NULL_UL; - } - else - { - ecma_completion_value_t obj_this = ecma_op_to_object (this_arg); - - if (!ecma_is_completion_value_normal (obj_this)) - { - return obj_this; - } - - JERRY_ASSERT (ecma_is_value_object (ecma_get_completion_value_value (obj_this))); - - ecma_object_t *obj_p = ecma_get_object_from_completion_value (obj_this); - - ecma_property_t *class_prop_p = ecma_get_internal_property (obj_p, - ECMA_INTERNAL_PROPERTY_CLASS); - type_string = (ecma_magic_string_id_t) class_prop_p->u.internal_property.value; - - ecma_free_completion_value (obj_this); - } - - ecma_string_t *ret_string_p; - - /* Building string "[object #type#]" where type is 'Undefined', - 'Null' or one of possible object's classes. - The string with null character is maximum 19 characters long. */ - const ssize_t buffer_size = 19; - MEM_DEFINE_LOCAL_ARRAY (str_buffer, buffer_size, ecma_char_t); - - const ecma_char_t *left_square_zt_str_p = ecma_get_magic_string_zt (ECMA_MAGIC_STRING_LEFT_SQUARE_CHAR); - const ecma_char_t *object_zt_str_p = ecma_get_magic_string_zt (ECMA_MAGIC_STRING_OBJECT); - const ecma_char_t *space_zt_str_p = ecma_get_magic_string_zt (ECMA_MAGIC_STRING_SPACE_CHAR); - const ecma_char_t *type_name_zt_str_p = ecma_get_magic_string_zt (type_string); - const ecma_char_t *right_square_zt_str_p = ecma_get_magic_string_zt (ECMA_MAGIC_STRING_RIGHT_SQUARE_CHAR); - - ecma_char_t *buffer_ptr = str_buffer; - ssize_t buffer_size_left = buffer_size; - buffer_ptr = ecma_copy_zt_string_to_buffer (left_square_zt_str_p, - buffer_ptr, - buffer_size_left); - buffer_size_left = buffer_size - (buffer_ptr - str_buffer) * (ssize_t) sizeof (ecma_char_t); - buffer_ptr = ecma_copy_zt_string_to_buffer (object_zt_str_p, - buffer_ptr, - buffer_size_left); - buffer_size_left = buffer_size - (buffer_ptr - str_buffer) * (ssize_t) sizeof (ecma_char_t); - buffer_ptr = ecma_copy_zt_string_to_buffer (space_zt_str_p, - buffer_ptr, - buffer_size_left); - buffer_size_left = buffer_size - (buffer_ptr - str_buffer) * (ssize_t) sizeof (ecma_char_t); - buffer_ptr = ecma_copy_zt_string_to_buffer (type_name_zt_str_p, - buffer_ptr, - buffer_size_left); - buffer_size_left = buffer_size - (buffer_ptr - str_buffer) * (ssize_t) sizeof (ecma_char_t); - buffer_ptr = ecma_copy_zt_string_to_buffer (right_square_zt_str_p, - buffer_ptr, - buffer_size_left); - buffer_size_left = buffer_size - (buffer_ptr - str_buffer) * (ssize_t) sizeof (ecma_char_t); - - JERRY_ASSERT (buffer_size_left >= 0); - - ret_string_p = ecma_new_ecma_string (str_buffer); - - MEM_FINALIZE_LOCAL_ARRAY (str_buffer); - - return ecma_make_normal_completion_value (ecma_make_string_value (ret_string_p)); + return ecma_builtin_helper_object_to_string (this_arg); } /* ecma_builtin_object_prototype_object_to_string */ /** diff --git a/tests/jerry/array_prototype_tostring.js b/tests/jerry/array_prototype_tostring.js new file mode 100644 index 0000000000..e728814ff7 --- /dev/null +++ b/tests/jerry/array_prototype_tostring.js @@ -0,0 +1,64 @@ +// 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. + +// Our own join method if the internal join is not implemented. +function join(sep) +{ + sep = sep ? sep : ","; + var result = ""; + + for (var i = 0; i < this.length; ++i) { + result += this[i]; + if (i + 1 < this.length) { + result += sep; + } + } + + return result; +} + +// Force fallback to object.prototype.toString() +Array.prototype.join = 1; + +assert ([1].toString() === "[object Array]"); + +Array.prototype.join = join; + +assert ([1, 2].toString() === "1,2"); + +var test = [1,2,3]; +test.join = function() { throw ReferenceError ("foo"); }; + +try { + test.toString(); + + assert (false); +} catch (e) { + assert (e.message === "foo"); + assert (e instanceof ReferenceError); +} + + +// Test if the join returns a ReferenceError +var arr = [1,2] +Object.defineProperty(arr, 'join', { 'get' : function () {throw new ReferenceError ("foo"); } }); +try { + arr.toString(); + + assert (false); +} catch (e) { + assert (e.message === "foo"); + assert (e instanceof ReferenceError); +}