From 0bfd6a904ad84412d01d21f1198f53c03f7b60f3 Mon Sep 17 00:00:00 2001 From: Peter Gal Date: Tue, 12 May 2015 16:09:16 +0200 Subject: [PATCH] Implement Array.prototype.join JerryScript-DCO-1.0-Signed-off-by: Peter Gal pgal.u-szeged@partner.samsung.com --- jerry-core/ecma/base/ecma-magic-strings.inc.h | 1 + .../ecma-builtin-array-prototype.cpp | 103 ++++++++++++++++++ .../ecma-builtin-array-prototype.inc.h | 1 + .../ecma/operations/ecma-array-prototype.cpp | 102 +++++++++++++++++ .../ecma/operations/ecma-array-prototype.h | 37 +++++++ tests/jerry/array_prototype_join.js | 77 +++++++++++++ 6 files changed, 321 insertions(+) create mode 100644 jerry-core/ecma/operations/ecma-array-prototype.cpp create mode 100644 jerry-core/ecma/operations/ecma-array-prototype.h create mode 100644 tests/jerry/array_prototype_join.js diff --git a/jerry-core/ecma/base/ecma-magic-strings.inc.h b/jerry-core/ecma/base/ecma-magic-strings.inc.h index 7302fbf6ed..81f31ac2ce 100644 --- a/jerry-core/ecma/base/ecma-magic-strings.inc.h +++ b/jerry-core/ecma/base/ecma-magic-strings.inc.h @@ -205,6 +205,7 @@ ECMA_MAGIC_STRING_DEF (ECMA_MAGIC_STRING_MESSAGE, "message") ECMA_MAGIC_STRING_DEF (ECMA_MAGIC_STRING_LEFT_SQUARE_CHAR, "[") ECMA_MAGIC_STRING_DEF (ECMA_MAGIC_STRING_RIGHT_SQUARE_CHAR, "]") ECMA_MAGIC_STRING_DEF (ECMA_MAGIC_STRING_COLON_CHAR, ":") +ECMA_MAGIC_STRING_DEF (ECMA_MAGIC_STRING_COMMA_CHAR, ",") ECMA_MAGIC_STRING_DEF (ECMA_MAGIC_STRING_SPACE_CHAR, " ") ECMA_MAGIC_STRING_DEF (ECMA_MAGIC_STRING__EMPTY, "") 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..7c8e7320fe 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-array-prototype.h" #include "ecma-builtins.h" #include "ecma-comparison.h" #include "ecma-conversion.h" @@ -168,6 +169,108 @@ ecma_builtin_array_prototype_object_for_each (ecma_value_t this_arg, /**< this a return ret_value; } /* ecma_builtin_array_prototype_object_for_each */ +/** + * The Array.prototype object's 'join' routine + * + * See also: + * ECMA-262 v5, 15.4.4.5 + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_array_prototype_join (const ecma_value_t this_arg, /**< this argument */ + const ecma_value_t separator_arg) /** < separator 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_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); + + ecma_number_t *length_p = ecma_get_number_from_value (length_value); + + /* 3. */ + uint32_t length = ecma_number_to_uint32 (*length_p); + + /* 4-5. */ + ECMA_TRY_CATCH (separator_value, + ecma_op_array_get_separator_string (separator_arg), + ret_value); + + if (length == 0) + { + /* 6. */ + 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 + { + ecma_string_t *separator_string_p = ecma_get_string_from_value (separator_value); + + /* 7-8. */ + ECMA_TRY_CATCH (first_value, + ecma_op_array_get_to_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) + { + /* 10.a */ + ecma_string_t *part_string_p = ecma_concat_ecma_strings (return_string_p, separator_string_p); + + /* 10.b, 10.c */ + ECMA_TRY_CATCH (next_string_value, + ecma_op_array_get_to_string_at_index (obj_p, k), + ret_value); + + ecma_string_t *next_string_p = ecma_get_string_from_value (next_string_value); + + ecma_deref_ecma_string (return_string_p); + + /* 10.d */ + 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_FINALIZE (separator_value); + + ECMA_FINALIZE (length_value); + + ecma_deref_ecma_string (length_magic_string_p); + + ECMA_FINALIZE (obj_value); + + return ret_value; +} /* ecma_builtin_array_prototype_join */ + /** * The Array.prototype object's 'toString' 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 7718f7a877..e1e8a90856 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 @@ -59,6 +59,7 @@ NUMBER_VALUE (ECMA_MAGIC_STRING_LENGTH, /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (ECMA_MAGIC_STRING_FOR_EACH_UL, ecma_builtin_array_prototype_object_for_each, 2, 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_POP, ecma_builtin_array_prototype_object_pop, 0, 0) ROUTINE (ECMA_MAGIC_STRING_PUSH, ecma_builtin_array_prototype_object_push, NON_FIXED, 1) diff --git a/jerry-core/ecma/operations/ecma-array-prototype.cpp b/jerry-core/ecma/operations/ecma-array-prototype.cpp new file mode 100644 index 0000000000..702cb6fdc6 --- /dev/null +++ b/jerry-core/ecma/operations/ecma-array-prototype.cpp @@ -0,0 +1,102 @@ +/* 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-array-prototype.h" + +#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 + * @{ + * + * \addtogroup ecmabuiltins + * @{ + * + * \addtogroup arrayprototype ECMA Array.prototype object built-in operations + * @{ + */ + +/** + * The Array.prototype.toString's separator creation routine + * + * See also: + * ECMA-262 v5.1, 15.4.4.2 4th step + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +ecma_completion_value_t +ecma_op_array_get_separator_string (ecma_value_t separator) +{ + if (ecma_is_value_undefined (separator)) + { + ecma_string_t *comma_string_p = ecma_get_magic_string (ECMA_MAGIC_STRING_COMMA_CHAR); + return ecma_make_normal_completion_value (ecma_make_string_value (comma_string_p)); + } + else + { + return ecma_op_to_string (separator); + } +} /* ecma_op_array_get_separator_string */ + +/** + * The Array.prototype's 'toString' single element operation routine + * + * See also: + * ECMA-262 v5.1, 15.4.4.2 + * + * @return ecma_completion_value_t value + * Returned value must be freed with ecma_free_completion_value. + */ +ecma_completion_value_t +ecma_op_array_get_to_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 *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 + { + ret_value = ecma_op_to_string (index_value); + } + + ECMA_FINALIZE (index_value) + + ecma_deref_ecma_string (index_string_p); + + return ret_value; +} /* ecma_op_array_get_to_string_at_index */ + +/** + * @} + * @} + * @} + */ diff --git a/jerry-core/ecma/operations/ecma-array-prototype.h b/jerry-core/ecma/operations/ecma-array-prototype.h new file mode 100644 index 0000000000..b0550ec00e --- /dev/null +++ b/jerry-core/ecma/operations/ecma-array-prototype.h @@ -0,0 +1,37 @@ +/* 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_ARRAY_PROTOTYPE_H +#define ECMA_ARRAY_PROTOTYPE_H + +#include "ecma-globals.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmaarrayprototypeops ECMA array prototype's operations + * @{ + */ + +extern ecma_completion_value_t ecma_op_array_get_separator_string (ecma_value_t separator); +extern ecma_completion_value_t ecma_op_array_get_to_string_at_index (ecma_object_t *obj_p, uint32_t index); + +/** + * @} + * @} + */ + +#endif /* !ECMA_ARRAY_PROPERTY_H */ diff --git a/tests/jerry/array_prototype_join.js b/tests/jerry/array_prototype_join.js new file mode 100644 index 0000000000..74edaa8927 --- /dev/null +++ b/tests/jerry/array_prototype_join.js @@ -0,0 +1,77 @@ +// 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 ([].join() === ""); +assert ([1].join() === "1"); +assert ([1, 2].join() === "1,2"); + + +assert ([].join('--') === ""); +assert ([1].join("--") === "1"); +assert ([1, 2].join('--') === "1--2"); + +assert ([1,2,3].join({toString: function() { return "--"; }}) === "1--2--3"); + + +// Join should use 'length' to as the number of elements int the array. +var lst = [1,2,3,4]; +lst.length = 3; +assert (lst.join() === [1,2,3].join()); + +// Checking behavior when unable to get length. +var obj = {}; +Object.defineProperty(obj, 'length', { 'get' : function () {throw new ReferenceError ("foo"); } }); +obj.join = Array.prototype.join; + +try { + obj.join(); + // Should not be reached. + assert (false); +} catch (e) { + assert (e.message === "foo"); + assert (e instanceof ReferenceError); +} + +// Check join argument fail. +try { + [1,2,3].join({toString: function() { throw new ReferenceError ("foo"); }}); + // Should not be reached. + assert (false); +} catch (e) { + assert (e.message === "foo"); + assert (e instanceof ReferenceError); +} + +// Check single join element fail. +try { + [1, 2, {toString: function() { throw new ReferenceError ("foo"); }}, 4].join(); + // Should not be reached. + assert (false); +} catch (e) { + assert (e.message === "foo"); + assert (e instanceof ReferenceError); +} + +// Check join on different object. +var obj_2 = {}; +obj_2.length = 3; +obj_2[0] = 1; +obj_2[1] = 2; +obj_2[2] = 3; +obj_2[3] = 4; + +obj_2.join = Array.prototype.join; + +assert (obj_2.join() === "1,2,3");