diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 84ceafea2..bc1df2d7e 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -465,6 +465,17 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ break; } #endif /* ENABLED (JERRY_ES2015_BUILTIN_ITERATOR) */ +#if ENABLED (JERRY_ES2015) + case ECMA_PSEUDO_SPREAD_OBJECT: + { + ecma_value_t spread_value = ext_object_p->u.pseudo_array.u2.spread_value; + if (ecma_is_value_object (spread_value)) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (spread_value)); + } + break; + } +#endif /* ENABLED (JERRY_ES2015) */ default: { JERRY_ASSERT (ext_object_p->u.pseudo_array.type == ECMA_PSEUDO_ARRAY_ARGUMENTS); @@ -940,6 +951,15 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ return; } #endif /* ENABLED (JERRY_ES2015_BUILTIN_ITERATOR) */ +#if ENABLED (JERRY_ES2015) + case ECMA_PSEUDO_SPREAD_OBJECT: + { + ecma_value_t spread_value = ext_object_p->u.pseudo_array.u2.spread_value; + ecma_free_value_if_not_object (spread_value); + ecma_dealloc_extended_object (object_p, sizeof (ecma_extended_object_t)); + return; + } +#endif /* ENABLED (JERRY_ES2015) */ default: { JERRY_ASSERT (ext_object_p->u.pseudo_array.type == ECMA_PSEUDO_ARRAY_ARGUMENTS); diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 048120d12..6846e9849 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -629,7 +629,8 @@ typedef enum ECMA_PSEUDO_SET_ITERATOR = 4, /**< Set iterator object (ECMAScript v6, 23.2.5.1) */ ECMA_PSEUDO_MAP_ITERATOR = 5, /**< Map iterator object (ECMAScript v6, 23.1.5.1) */ ECMA_PSEUDO_STRING_ITERATOR = 6, /**< String iterator object (ECMAScript v6, 22.1.5.1) */ - ECMA_PSEUDO_ARRAY__MAX = ECMA_PSEUDO_STRING_ITERATOR /**< maximum value */ + ECMA_PSEUDO_SPREAD_OBJECT = 7, /**< spread object */ + ECMA_PSEUDO_ARRAY__MAX = ECMA_PSEUDO_SPREAD_OBJECT /**< maximum value */ } ecma_pseudo_array_type_t; /** @@ -865,6 +866,7 @@ typedef struct ecma_value_t lex_env_cp; /**< for arguments: lexical environment */ ecma_value_t arraybuffer; /**< for typedarray: internal arraybuffer */ ecma_value_t iterated_value; /**< for %Iterator%: [[IteratedObject]] property */ + ecma_value_t spread_value; /**< for spread object: spreaded element */ } u2; } pseudo_array; diff --git a/jerry-core/ecma/operations/ecma-spread-object.c b/jerry-core/ecma/operations/ecma-spread-object.c new file mode 100644 index 000000000..6340d0eb5 --- /dev/null +++ b/jerry-core/ecma/operations/ecma-spread-object.c @@ -0,0 +1,88 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * 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-spread-object.h" +#include "ecma-globals.h" +#include "ecma-helpers.h" +#include "ecma-objects.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmaspreadeanobject ECMA Spread object related routines + * @{ + */ + +/** + * Spread object creation operation. + * + * @return pseudo array object as an ecma value + * Returned value must be freed with ecma_free_value + */ +ecma_value_t +ecma_op_create_spread_object (ecma_value_t element) /**< value to spread */ +{ + ecma_object_t *object_p = ecma_create_object (NULL, + sizeof (ecma_extended_object_t), + ECMA_OBJECT_TYPE_PSEUDO_ARRAY); + + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + ext_object_p->u.pseudo_array.type = ECMA_PSEUDO_SPREAD_OBJECT; + ext_object_p->u.pseudo_array.u2.spread_value = ecma_copy_value_if_not_object (element); + + return ecma_make_object_value (object_p); +} /* ecma_op_create_spread_object */ + +/** + * Spread object creation operation. + * + * @return true - if the argument is a spread object + * false, otherwise + */ +bool +ecma_op_is_spread_object (ecma_value_t value) /**< value to check */ +{ + if (ecma_is_value_object (value)) + { + ecma_object_t *object_p = ecma_get_object_from_value (value); + + if (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_PSEUDO_ARRAY) + { + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + return ext_object_p->u.pseudo_array.type == ECMA_PSEUDO_SPREAD_OBJECT; + } + } + + return false; +} /* ecma_op_is_spread_object */ + +/** + * Get the referenced element by the spread object + * + * @return element referenced by the spread object as an ecma-value + */ +ecma_value_t +ecma_op_spread_object_get_spreaded_element (ecma_object_t *object_p) /**< spread object */ +{ + JERRY_ASSERT (ecma_op_is_spread_object (ecma_make_object_value (object_p))); + + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + return ecma_copy_value (ext_object_p->u.pseudo_array.u2.spread_value); +} /* ecma_op_spread_object_get_spreaded_element */ + +/** + * @} + * @} + */ diff --git a/jerry-core/ecma/operations/ecma-spread-object.h b/jerry-core/ecma/operations/ecma-spread-object.h new file mode 100644 index 000000000..44a5ee1a5 --- /dev/null +++ b/jerry-core/ecma/operations/ecma-spread-object.h @@ -0,0 +1,42 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * 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_SPREAD_OBJECT_H +#define ECMA_SPREAD_OBJECT_H + +#include "ecma-globals.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmaspreadobject ECMA Spread object related routines + * @{ + */ + +ecma_value_t +ecma_op_create_spread_object (ecma_value_t arg); + +bool +ecma_op_is_spread_object (ecma_value_t value); + +ecma_value_t +ecma_op_spread_object_get_spreaded_element (ecma_object_t *object_p); + +/** + * @} + * @} + */ + +#endif /* !ECMA_SPREAD_OBJECT_H */ diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 90b264267..828a68d95 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -608,6 +608,10 @@ VM_OC_CONSTRUCTOR_RET | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_ERROR, CBC_NO_FLAG, 0, \ VM_OC_ERROR) \ + CBC_OPCODE (CBC_EXT_CREATE_SPREAD_OBJECT, CBC_NO_FLAG, 0, \ + VM_OC_CREATE_SPREAD_OBJECT | VM_OC_GET_STACK | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_SPREAD_ARRAY_APPEND, CBC_HAS_POP_STACK_BYTE_ARG, 0, \ + VM_OC_APPEND_ARRAY) \ \ /* Last opcode (not a real opcode). */ \ CBC_OPCODE (CBC_EXT_END, CBC_NO_FLAG, 0, \ diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 66c389b13..da64dbae5 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -210,6 +210,7 @@ static void parser_parse_array_literal (parser_context_t *context_p) /**< context */ { uint32_t pushed_items = 0; + uint16_t opcode = (uint16_t) CBC_ARRAY_APPEND; JERRY_ASSERT (context_p->token.type == LEXER_LEFT_SQUARE); @@ -222,7 +223,7 @@ parser_parse_array_literal (parser_context_t *context_p) /**< context */ { if (pushed_items > 0) { - parser_emit_cbc_call (context_p, CBC_ARRAY_APPEND, pushed_items); + parser_emit_cbc_call (context_p, opcode, pushed_items); } return; } @@ -236,8 +237,25 @@ parser_parse_array_literal (parser_context_t *context_p) /**< context */ } else { +#if ENABLED (JERRY_ES2015) + bool is_spread = false; + if (context_p->token.type == LEXER_THREE_DOTS) + { + opcode = (uint16_t) (PARSER_TO_EXT_OPCODE (CBC_EXT_SPREAD_ARRAY_APPEND)); + is_spread = true; + lexer_next_token (context_p); + } +#endif /* ENABLED (JERRY_ES2015) */ + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); +#if ENABLED (JERRY_ES2015) + if (is_spread) + { + parser_emit_cbc_ext (context_p, CBC_EXT_CREATE_SPREAD_OBJECT); + } +#endif /* ENABLED (JERRY_ES2015) */ + if (context_p->token.type == LEXER_COMMA) { lexer_next_token (context_p); @@ -250,7 +268,10 @@ parser_parse_array_literal (parser_context_t *context_p) /**< context */ if (pushed_items >= 64) { - parser_emit_cbc_call (context_p, CBC_ARRAY_APPEND, pushed_items); + parser_emit_cbc_call (context_p, opcode, pushed_items); +#if ENABLED (JERRY_ES2015) + opcode = (uint16_t) CBC_ARRAY_APPEND; +#endif /* ENABLED (JERRY_ES2015) */ pushed_items = 0; } } diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 64389e450..7f45160e4 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -405,6 +405,9 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; break; } +#if ENABLED (JERRY_ES2015) + case LEXER_THREE_DOTS: +#endif /* ENABLED (JERRY_ES2015) */ case LEXER_COMMA: { if (stack_top != SCAN_STACK_SQUARE_BRACKETED_EXPRESSION) diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index be2087a69..f551481c2 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -16,14 +16,17 @@ #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtins.h" +#include "ecma-builtin-helpers.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" +#include "ecma-iterator-object.h" #include "ecma-lex-env.h" #include "ecma-objects.h" +#include "ecma-spread-object.h" #include "ecma-try-catch-macro.h" #include "jcontext.h" #include "opcodes.h" @@ -275,15 +278,129 @@ opfunc_for_in (ecma_value_t left_value, /**< left value */ return NULL; } /* opfunc_for_in */ +#if ENABLED (JERRY_ES2015) +/** + * 'VM_OC_APPEND_ARRAY' opcode handler specialized for spread objects + * + * @return ECMA_VALUE_ERROR - if the operation failed + * ECMA_VALUE_EMPTY, otherwise + */ +static ecma_value_t JERRY_ATTR_NOINLINE +opfunc_append_to_spread_array (ecma_value_t *stack_top_p, /**< current stack top */ + uint16_t values_length) /**< number of elements to set */ +{ + JERRY_ASSERT (!(values_length & OPFUNC_HAS_SPREAD_ELEMENT)); + + ecma_object_t *array_obj_p = ecma_get_object_from_value (stack_top_p[-1]); + JERRY_ASSERT (ecma_get_object_type (array_obj_p) == ECMA_OBJECT_TYPE_ARRAY); + + ecma_extended_object_t *ext_array_obj_p = (ecma_extended_object_t *) array_obj_p; + uint32_t old_length = ext_array_obj_p->u.array.length; + + for (uint32_t i = 0, j = old_length; i < values_length; i++) + { + if (ecma_is_value_array_hole (stack_top_p[i])) + { + continue; + } + if (ecma_op_is_spread_object (stack_top_p[i])) + { + ecma_value_t ret_value = ECMA_VALUE_ERROR; + ecma_object_t *spread_object_p = ecma_get_object_from_value (stack_top_p[i]); + ecma_value_t spread_value = ecma_op_spread_object_get_spreaded_element (spread_object_p); + + ecma_value_t iterator = ecma_op_get_iterator (spread_value, ECMA_VALUE_EMPTY); + + if (!ECMA_IS_VALUE_ERROR (iterator)) + { + while (true) + { + ecma_value_t next = ecma_op_iterator_step (iterator); + + if (ECMA_IS_VALUE_ERROR (next)) + { + break; + } + + if (ecma_is_value_false (next)) + { + j--; + ret_value = ECMA_VALUE_EMPTY; + break; + } + + ecma_value_t value = ecma_op_iterator_value (next); + + ecma_free_value (next); + + if (ECMA_IS_VALUE_ERROR (value)) + { + break; + } + + ecma_value_t put_comp; + put_comp = ecma_builtin_helper_def_prop_by_index (array_obj_p, + i + j, + value, + ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); + + j++; + JERRY_ASSERT (ecma_is_value_true (put_comp)); + ecma_free_value (value); + } + } + + ecma_free_value (iterator); + ecma_free_value (spread_value); + + if (ECMA_IS_VALUE_ERROR (ret_value)) + { + for (uint32_t k = i; k < values_length; k++) + { + ecma_free_value (stack_top_p[k]); + } + + return ret_value; + } + else + { + ecma_deref_object (spread_object_p); + } + } + else + { + ecma_value_t put_comp = ecma_builtin_helper_def_prop_by_index (array_obj_p, + i + j, + stack_top_p[i], + ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); + JERRY_ASSERT (ecma_is_value_true (put_comp)); + ecma_free_value (stack_top_p[i]); + } + } + + return ECMA_VALUE_EMPTY; +} /* opfunc_append_to_spread_array */ +#endif /* ENABLED (JERRY_ES2015) */ + /** * 'VM_OC_APPEND_ARRAY' opcode handler, for setting array object properties + * + * @return ECMA_VALUE_ERROR - if the operation failed + * ECMA_VALUE_EMPTY, otherwise */ -void JERRY_ATTR_NOINLINE +ecma_value_t JERRY_ATTR_NOINLINE opfunc_append_array (ecma_value_t *stack_top_p, /**< current stack top */ - uint8_t values_length) /**< number of elements to set */ + uint16_t values_length) /**< number of elements to set + * with potential OPFUNC_HAS_SPREAD_ELEMENT flag */ { - ecma_object_t *array_obj_p = ecma_get_object_from_value (stack_top_p[-1]); +#if ENABLED (JERRY_ES2015) + if (values_length >= OPFUNC_HAS_SPREAD_ELEMENT) + { + return opfunc_append_to_spread_array (stack_top_p, (uint16_t) (values_length & ~OPFUNC_HAS_SPREAD_ELEMENT)); + } +#endif /* ENABLED (JERRY_ES2015) */ + ecma_object_t *array_obj_p = ecma_get_object_from_value (stack_top_p[-1]); JERRY_ASSERT (ecma_get_object_type (array_obj_p) == ECMA_OBJECT_TYPE_ARRAY); ecma_extended_object_t *ext_array_obj_p = (ecma_extended_object_t *) array_obj_p; @@ -344,6 +461,8 @@ opfunc_append_array (ecma_value_t *stack_top_p, /**< current stack top */ ext_array_obj_p->u.array.length = old_length + values_length; } } + + return ECMA_VALUE_EMPTY; } /* opfunc_append_array */ /** diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 3e8bb8fc0..82a2b3694 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -51,6 +51,11 @@ typedef enum NUMBER_BITWISE_NOT, /**< bitwise NOT calculation */ } number_bitwise_logic_op; +/** + * The stack contains spread object during the upcoming APPEND_ARRAY operation + */ +#define OPFUNC_HAS_SPREAD_ELEMENT (1 << 8) + void vm_var_decl (ecma_object_t *lex_env_p, ecma_string_t *var_name_str_p, bool is_configurable_bindings); @@ -96,8 +101,8 @@ vm_op_delete_var (ecma_value_t name_literal, ecma_object_t *lex_env_p); ecma_collection_t * opfunc_for_in (ecma_value_t left_value, ecma_value_t *result_obj_p); -void -opfunc_append_array (ecma_value_t *stack_top_p, uint8_t values_length); +ecma_value_t +opfunc_append_array (ecma_value_t *stack_top_p, uint16_t values_length); /** * @} diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index ea0096205..9351d1195 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -31,6 +31,7 @@ #include "ecma-objects-general.h" #include "ecma-regexp-object.h" #include "ecma-try-catch-macro.h" +#include "ecma-spread-object.h" #include "jcontext.h" #include "opcodes.h" #include "vm.h" @@ -1774,6 +1775,11 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ goto error; } + case VM_OC_CREATE_SPREAD_OBJECT: + { + *stack_top_p++ = ecma_op_create_spread_object (left_value); + goto free_left_value; + } #endif /* ENABLED (JERRY_ES2015) */ case VM_OC_PUSH_ELISON: { @@ -1782,9 +1788,25 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } case VM_OC_APPEND_ARRAY: { - uint8_t values_length = *byte_code_p++; + uint16_t values_length = *byte_code_p++; stack_top_p -= values_length; - opfunc_append_array (stack_top_p, values_length); + +#if ENABLED (JERRY_ES2015) + if (*byte_code_start_p == CBC_EXT_OPCODE) + { + values_length = (uint16_t) (values_length | OPFUNC_HAS_SPREAD_ELEMENT); + } +#endif /* ENABLED (JERRY_ES2015) */ + result = opfunc_append_array (stack_top_p, values_length); + +#if ENABLED (JERRY_ES2015) + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } +#else /* !ENABLED (JERRY_ES2015) */ + JERRY_ASSERT (ecma_is_value_empty (result)); +#endif /* ENABLED (JERRY_ES2015) */ continue; } case VM_OC_PUSH_UNDEFINED_BASE: diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 0146f6e2a..e8badb54f 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -245,6 +245,7 @@ typedef enum VM_OC_PUSH_CONSTRUCTOR_SUPER, /**< push 'super' inside a class constructor */ VM_OC_PUSH_CONSTRUCTOR_THIS, /**< push 'this' inside a class constructor */ VM_OC_CONSTRUCTOR_RET, /**< explicit return from a class constructor */ + VM_OC_CREATE_SPREAD_OBJECT, /**< create spread object */ #endif /* ENABLED (JERRY_ES2015) */ VM_OC_NONE, /**< a special opcode for unsupported byte codes */ } vm_oc_types; @@ -284,6 +285,7 @@ typedef enum VM_OC_PUSH_CONSTRUCTOR_SUPER = VM_OC_NONE, /**< push 'super' inside a class constructor */ VM_OC_PUSH_CONSTRUCTOR_THIS = VM_OC_NONE, /**< push 'this' inside a class constructor */ VM_OC_CONSTRUCTOR_RET = VM_OC_NONE, /**< explicit return from a class constructor */ + VM_OC_CREATE_SPREAD_OBJECT = VM_OC_NONE, /**< create spread object */ #endif /* !ENABLED (JERRY_ES2015S) */ VM_OC_UNUSED = VM_OC_NONE /**< placeholder if the list is empty */ diff --git a/tests/jerry/es2015/array-spread.js b/tests/jerry/es2015/array-spread.js new file mode 100644 index 000000000..975d50a76 --- /dev/null +++ b/tests/jerry/es2015/array-spread.js @@ -0,0 +1,79 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// 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. + +function assertArrayEqual (actual, expected) { + assert (actual.length === expected.length); + + for (var i = 0; i < actual.length; i++) { + assert (actual[i] === expected[i]); + } +} + +function checkSyntax (str) { + try { + eval (str); + assert (false); + } catch (e) { + assert (e instanceof SyntaxError); + } +} + +function mustThrow (str) { + try { + eval (str); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } +} + +checkSyntax ("{...a}"); +checkSyntax ("...a"); +checkSyntax ("[...]"); +checkSyntax ("[...(...)]"); +checkSyntax ("[......]"); + +mustThrow ("[...5]"); +mustThrow ("[...5, 'foo', 'bar']"); +mustThrow ("[...{}]"); +mustThrow ("[...{ get [Symbol.iterator] () { throw new TypeError } }]"); +mustThrow ("[...{ [Symbol.iterator] () {} }, 'foo']"); +mustThrow ("[...{ [Symbol.iterator] () { return {} } }]"); +mustThrow ("[...{ [Symbol.iterator] () { return { next: 5 } } }]"); +mustThrow ("[...{ [Symbol.iterator] () { return { next: 5 } } }], 'foo'"); +mustThrow ("[...{ [Symbol.iterator] () { return { get next() { throw new TypeError } } } }]"); +mustThrow ("[...{ [Symbol.iterator] () { return { next () { } } } }]"); +mustThrow ("[...{ [Symbol.iterator] () { return { next () { } } } }, 'foo']"); +mustThrow ("[...{ [Symbol.iterator] () { return { next () { return { get value () { throw new TypeError } } } } } } ]"); +mustThrow ("[...{ [Symbol.iterator] () { return { next () { return { get done () { throw new TypeError } } } } } } ]"); + +var arr1 = [0, 1, 2]; +var arr2 = [3, 4, 5]; +var arr3 = [{}, {}, {}]; +var expected = [0, 1, 2, 3 ,4, 5]; + +assertArrayEqual ([...arr1, ...arr2], [0, 1, 2, 3 ,4, 5]); +assertArrayEqual ([...arr2, ...arr1], [3 ,4, 5, 0, 1, 2]); +assertArrayEqual ([...arr1, 9, 9, 9, ...arr2], [0, 1, 2, 9, 9, 9, 3 ,4, 5]); +assertArrayEqual ([...arr1, ...[...arr2]], [0, 1, 2, 3 ,4, 5]); +assertArrayEqual (["0" , "1", ...arr1, ...[...arr2]], ["0", "1", 0, 1, 2, 3 ,4, 5]); +assertArrayEqual ([...arr3], arr3); +assertArrayEqual ([..."foobar"], ["f", "o", "o", "b", "a" ,"r"]); +assertArrayEqual ([...(new Set([1, "foo", arr3]))], [1, "foo", arr3]); + +var holyArray = [,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,...arr1]; +assert (holyArray.length === 83); +assert (holyArray[82] === 2); +assert (holyArray[81] === 1); +assert (holyArray[80] === 0);