diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 95f085ed7..65aba2395 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -66,16 +66,16 @@ #if ENABLED (JERRY_ES2015) /** - * Checks whether the current opcode is a super constructor call + * CBC_NO_RESULT_OPERATION for ext opcodes */ -#define CBC_SUPER_CALL_OPERATION(opcode) \ +#define CBC_EXT_NO_RESULT_OPERATION(opcode) \ ((opcode) >= PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL) \ - && (opcode) <= PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL_BLOCK)) + && (opcode) <= PARSER_TO_EXT_OPCODE (CBC_EXT_SPREAD_CALL_PROP_BLOCK)) #else /* !ENABLED (JERRY_ES2015) */ /** - * Checks whether the current opcode is a super constructor call + * CBC_NO_RESULT_OPERATION for ext opcodes */ -#define CBC_SUPER_CALL_OPERATION(opcode) false +#define CBC_EXT_NO_RESULT_OPERATION(opcode) false #endif /* ENABLED (JERRY_ES2015) */ /* Debug macro. */ @@ -84,9 +84,9 @@ /* Debug macro. */ #define CBC_SAME_ARGS(op1, op2) \ - (CBC_SUPER_CALL_OPERATION (op1) ? ((cbc_ext_flags[PARSER_GET_EXT_OPCODE (op1)] & CBC_ARG_TYPES) \ - == (cbc_ext_flags[PARSER_GET_EXT_OPCODE (op2)] & CBC_ARG_TYPES)) \ - : ((cbc_flags[op1] & CBC_ARG_TYPES) == (cbc_flags[op2] & CBC_ARG_TYPES))) + (CBC_EXT_NO_RESULT_OPERATION (op1) ? ((cbc_ext_flags[PARSER_GET_EXT_OPCODE (op1)] & CBC_ARG_TYPES) \ + == (cbc_ext_flags[PARSER_GET_EXT_OPCODE (op2)] & CBC_ARG_TYPES)) \ + : ((cbc_flags[op1] & CBC_ARG_TYPES) == (cbc_flags[op2] & CBC_ARG_TYPES))) #define CBC_UNARY_OPERATION(name, group) \ CBC_OPCODE (name, CBC_NO_FLAG, 0, \ @@ -140,7 +140,7 @@ * cannot be true for an opcode which has a result */ #define CBC_NO_RESULT_OPERATION(opcode) \ - (((opcode) >= CBC_PRE_INCR && (opcode) < CBC_END) || CBC_SUPER_CALL_OPERATION ((opcode))) + (((opcode) >= CBC_PRE_INCR && (opcode) < CBC_END) || CBC_EXT_NO_RESULT_OPERATION ((opcode))) /** * Branch instructions are organized in group of 8 opcodes. @@ -598,6 +598,24 @@ VM_OC_SUPER_CALL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_SUPER_CALL_BLOCK, CBC_HAS_POP_STACK_BYTE_ARG, -1, \ VM_OC_SUPER_CALL | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_EXT_SPREAD_SUPER_CALL, CBC_HAS_POP_STACK_BYTE_ARG, -1, \ + VM_OC_SUPER_CALL) \ + CBC_OPCODE (CBC_EXT_SPREAD_SUPER_CALL_PUSH_RESULT, CBC_HAS_POP_STACK_BYTE_ARG, 0, \ + VM_OC_SUPER_CALL | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_SPREAD_SUPER_CALL_BLOCK, CBC_HAS_POP_STACK_BYTE_ARG, -1, \ + VM_OC_SUPER_CALL | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_EXT_SPREAD_CALL, CBC_HAS_POP_STACK_BYTE_ARG, -1, \ + VM_OC_SPREAD_ARGUMENTS) \ + CBC_OPCODE (CBC_EXT_SPREAD_CALL_PUSH_RESULT, CBC_HAS_POP_STACK_BYTE_ARG, 0, \ + VM_OC_SPREAD_ARGUMENTS | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_SPREAD_CALL_BLOCK, CBC_HAS_POP_STACK_BYTE_ARG, -1, \ + VM_OC_SPREAD_ARGUMENTS | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_EXT_SPREAD_CALL_PROP, CBC_HAS_POP_STACK_BYTE_ARG, -3, \ + VM_OC_SPREAD_ARGUMENTS) \ + CBC_OPCODE (CBC_EXT_SPREAD_CALL_PROP_PUSH_RESULT, CBC_HAS_POP_STACK_BYTE_ARG, -2, \ + VM_OC_SPREAD_ARGUMENTS | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_SPREAD_CALL_PROP_BLOCK, CBC_HAS_POP_STACK_BYTE_ARG, -3, \ + VM_OC_SPREAD_ARGUMENTS | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_EXT_PUSH_CONSTRUCTOR_SUPER, CBC_NO_FLAG, 1, \ VM_OC_PUSH_CONSTRUCTOR_SUPER | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_PUSH_CONSTRUCTOR_SUPER_PROP, CBC_NO_FLAG, 1, \ @@ -632,6 +650,8 @@ VM_OC_REST_INITIALIZER) \ CBC_OPCODE (CBC_EXT_INITIALIZER_PUSH_PROP_LITERAL, CBC_HAS_LITERAL_ARG, 1, \ VM_OC_INITIALIZER_PUSH_PROP | VM_OC_GET_LITERAL) \ + CBC_OPCODE (CBC_EXT_SPREAD_NEW, CBC_HAS_POP_STACK_BYTE_ARG, 0, \ + VM_OC_SPREAD_ARGUMENTS | VM_OC_PUT_STACK) \ \ /* 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 7431b84df..9233eda88 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -1638,6 +1638,10 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ lexer_next_token (context_p); +#if ENABLED (JERRY_ES2015) + bool spread_arguments = false; +#endif /* ENABLED (JERRY_ES2015) */ + if (context_p->token.type != LEXER_RIGHT_PAREN) { while (true) @@ -1647,8 +1651,24 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); } +#if ENABLED (JERRY_ES2015) + bool is_spread = false; + if (context_p->token.type == LEXER_THREE_DOTS) + { + spread_arguments = true; + 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) { break; @@ -1688,6 +1708,33 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ parser_emit_cbc_ext (context_p, CBC_EXT_SUPER_PROP_CALL); context_p->status_flags &= (uint32_t) ~PARSER_CLASS_SUPER_PROP_REFERENCE; } + + if (spread_arguments) + { + uint16_t spread_opcode; + + if (opcode == CBC_CALL) + { + spread_opcode = CBC_EXT_SPREAD_CALL; + } + else if (opcode == CBC_CALL_PROP) + { + spread_opcode = CBC_EXT_SPREAD_CALL_PROP; + } + else if (opcode == CBC_NEW) + { + spread_opcode = CBC_EXT_SPREAD_NEW; + } + else + { + /* opcode is unchanged */ + JERRY_ASSERT (opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL)); + spread_opcode = CBC_EXT_SPREAD_SUPER_CALL; + } + + parser_emit_cbc_ext_call (context_p, spread_opcode, call_arguments); + continue; + } #endif /* ENABLED (JERRY_ES2015) */ if (call_arguments <= 1) diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 95a29b2fa..85655bf62 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -599,6 +599,8 @@ void parser_set_continues_to_current_position (parser_context_t *context_p, pars parser_emit_cbc_literal ((context_p), PARSER_TO_EXT_OPCODE (opcode), (literal_index)) #define parser_emit_cbc_ext_call(context_p, opcode, call_arguments) \ parser_emit_cbc_call ((context_p), PARSER_TO_EXT_OPCODE (opcode), (call_arguments)) +#define parser_emit_cbc_ext_call(context_p, opcode, call_arguments) \ + parser_emit_cbc_call ((context_p), PARSER_TO_EXT_OPCODE (opcode), (call_arguments)) #define parser_emit_cbc_ext_forward_branch(context_p, opcode, branch_p) \ parser_emit_cbc_forward_branch ((context_p), PARSER_TO_EXT_OPCODE (opcode), (branch_p)) #define parser_emit_cbc_ext_backward_branch(context_p, opcode, offset) \ diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index ed0bec039..0366f12e1 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -443,8 +443,14 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ #endif /* ENABLED (JERRY_ES2015) */ case LEXER_COMMA: { - /* Elision. */ - if (stack_top != SCAN_STACK_ARRAY_LITERAL) + /* Elision or spread arguments */ +#if ENABLED (JERRY_ES2015) + bool raise_error = (stack_top != SCAN_STACK_PAREN_EXPRESSION && stack_top != SCAN_STACK_ARRAY_LITERAL); +#else /* !ENABLED (JERRY_ES2015) */ + bool raise_error = stack_top != SCAN_STACK_ARRAY_LITERAL; +#endif /* ENABLED (JERRY_ES2015) */ + + if (raise_error) { scanner_raise_error (context_p); } diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index 8d2d07cca..686170669 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -382,6 +382,84 @@ opfunc_append_to_spread_array (ecma_value_t *stack_top_p, /**< current stack top return ECMA_VALUE_EMPTY; } /* opfunc_append_to_spread_array */ + +/** + * Spread function call/construct arguments into an ecma-collection + * + * @return NULL - if the operation failed + * pointer to the ecma-collection with the spreaded arguments, otherwise + */ +ecma_collection_t * JERRY_ATTR_NOINLINE +opfunc_spread_arguments (ecma_value_t *arguments_list_p, /**< arguments list */ + uint8_t arguments_list_len) /**< number of arguments */ +{ + ecma_collection_t *buff_p = ecma_new_collection (); + + for (uint8_t i = 0; i < arguments_list_len; i++) + { + ecma_value_t arg = arguments_list_p[i]; + + if (!ecma_op_is_spread_object (arg)) + { + ecma_collection_push_back (buff_p, arg); + continue; + } + + ecma_value_t ret_value = ECMA_VALUE_ERROR; + ecma_object_t *spread_object_p = ecma_get_object_from_value (arguments_list_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)) + { + 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_collection_push_back (buff_p, value); + } + } + + ecma_free_value (iterator); + ecma_free_value (spread_value); + + if (ECMA_IS_VALUE_ERROR (ret_value)) + { + for (uint32_t k = i; k < arguments_list_len; k++) + { + ecma_free_value (arguments_list_p[k]); + } + + ecma_collection_free (buff_p); + return NULL; + } + + ecma_deref_object (spread_object_p); + } + + return buff_p; +} /* opfunc_spread_arguments */ #endif /* ENABLED (JERRY_ES2015) */ /** diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 82a2b3694..5b05e1c8c 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -104,6 +104,11 @@ opfunc_for_in (ecma_value_t left_value, ecma_value_t *result_obj_p); ecma_value_t opfunc_append_array (ecma_value_t *stack_top_p, uint16_t values_length); +#if ENABLED (JERRY_ES2015) +ecma_collection_t * +opfunc_spread_arguments (ecma_value_t *arguments_list_p, uint8_t argument_list_len); +#endif /* ENABLED (JERRY_ES2015) */ + /** * @} * @} diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 4dabf6388..73c3e1fbf 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -478,24 +478,40 @@ vm_super_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ uint8_t *byte_code_p = frame_ctx_p->byte_code_p + 3; uint8_t opcode = byte_code_p[-2]; - uint32_t arguments_list_len = byte_code_p[-1]; + uint32_t arguments_list_len; - ecma_value_t *stack_top_p = frame_ctx_p->stack_top_p - arguments_list_len; + bool spread_arguments = opcode >= CBC_EXT_SPREAD_SUPER_CALL; - ecma_value_t func_value = stack_top_p[-1]; + ecma_collection_t *collection_p = NULL; + ecma_value_t *arguments_p; + + if (spread_arguments) + { + ecma_value_t collection = *(--frame_ctx_p->stack_top_p); + collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, collection); + arguments_p = collection_p->buffer_p; + arguments_list_len = collection_p->item_count; + } + else + { + arguments_list_len = byte_code_p[-1]; + arguments_p = frame_ctx_p->stack_top_p; + } + + ecma_value_t func_value = *(--frame_ctx_p->stack_top_p); ecma_value_t completion_value; ecma_op_set_super_called (frame_ctx_p->lex_env_p); ecma_value_t this_value = ecma_op_get_class_this_binding (frame_ctx_p->lex_env_p); if (!ecma_is_constructor (func_value)) { - completion_value = ecma_raise_type_error ("Class extends value is not a constructor."); + completion_value = ecma_raise_type_error (ECMA_ERR_MSG ("Class extends value is not a constructor.")); } else { completion_value = ecma_op_function_construct (ecma_get_object_from_value (func_value), this_value, - stack_top_p, + arguments_p, arguments_list_len); if (this_value != completion_value && ecma_is_value_object (completion_value)) @@ -508,9 +524,16 @@ vm_super_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ /* Free registers. */ for (uint32_t i = 0; i < arguments_list_len; i++) { - ecma_fast_free_value (stack_top_p[i]); + ecma_fast_free_value (arguments_p[i]); } + if (collection_p != NULL) + { + ecma_collection_destroy (collection_p); + } + + ecma_free_value (func_value); + if (JERRY_UNLIKELY (ECMA_IS_VALUE_ERROR (completion_value))) { #if ENABLED (JERRY_DEBUGGER) @@ -521,7 +544,6 @@ vm_super_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ else { frame_ctx_p->byte_code_p = byte_code_p; - ecma_free_value (*(--stack_top_p)); uint32_t opcode_data = vm_decode_table[(CBC_END + 1) + opcode]; if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))) @@ -530,7 +552,7 @@ vm_super_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } else if (opcode_data & VM_OC_PUT_STACK) { - *stack_top_p++ = completion_value; + *frame_ctx_p->stack_top_p++ = completion_value; } else { @@ -538,9 +560,102 @@ vm_super_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ frame_ctx_p->block_result = completion_value; } } - - frame_ctx_p->stack_top_p = stack_top_p; } /* vm_super_call */ + +/** + * Perform one of the following call/construct operation with spreaded argument list + * - f(...args) + * - o.f(...args) + * - new O(...args) + */ +static void +vm_spread_operation (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ +{ + JERRY_ASSERT (frame_ctx_p->byte_code_p[0] == CBC_EXT_OPCODE); + + uint8_t opcode = frame_ctx_p->byte_code_p[1]; + ecma_value_t completion_value; + ecma_value_t collection = *(--frame_ctx_p->stack_top_p); + + ecma_collection_t *collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, collection); + ecma_value_t func_value = *(--frame_ctx_p->stack_top_p); + bool is_call_prop = opcode >= CBC_EXT_SPREAD_CALL_PROP; + + if (frame_ctx_p->byte_code_p[1] == CBC_EXT_SPREAD_NEW) + { + if (!ecma_is_value_object (func_value) + || !ecma_object_is_constructor (ecma_get_object_from_value (func_value))) + { + completion_value = ecma_raise_type_error (ECMA_ERR_MSG ("Expected a constructor.")); + } + else + { + ecma_object_t *constructor_obj_p = ecma_get_object_from_value (func_value); + + completion_value = ecma_op_function_construct (constructor_obj_p, + ECMA_VALUE_UNDEFINED, + collection_p->buffer_p, + collection_p->item_count); + } + } + else + { + ecma_value_t this_value = is_call_prop ? frame_ctx_p->stack_top_p[-2] : ECMA_VALUE_UNDEFINED; + + if (!ecma_is_value_object (func_value) + || !ecma_op_object_is_callable (ecma_get_object_from_value (func_value))) + { + completion_value = ecma_raise_type_error (ECMA_ERR_MSG ("Expected a function.")); + } + else + { + ecma_object_t *func_obj_p = ecma_get_object_from_value (func_value); + + completion_value = ecma_op_function_call (func_obj_p, + this_value, + collection_p->buffer_p, + collection_p->item_count); + } + + if (is_call_prop) + { + ecma_free_value (*(--frame_ctx_p->stack_top_p)); + ecma_free_value (*(--frame_ctx_p->stack_top_p)); + } + } + + ecma_collection_free (collection_p); + ecma_free_value (func_value); + + if (JERRY_UNLIKELY (ECMA_IS_VALUE_ERROR (completion_value))) + { +#if ENABLED (JERRY_DEBUGGER) + JERRY_CONTEXT (debugger_exception_byte_code_p) = frame_ctx_p->byte_code_p; +#endif /* ENABLED (JERRY_DEBUGGER) */ + frame_ctx_p->byte_code_p = (uint8_t *) vm_error_byte_code_p; + } + else + { + uint32_t opcode_data = vm_decode_table[(CBC_END + 1) + opcode]; + + if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))) + { + ecma_fast_free_value (completion_value); + } + else if (opcode_data & VM_OC_PUT_STACK) + { + *frame_ctx_p->stack_top_p++ = completion_value; + } + else + { + ecma_fast_free_value (frame_ctx_p->block_result); + frame_ctx_p->block_result = completion_value; + } + + /* EXT_OPCODE, SPREAD_OPCODE, BYTE_ARG */ + frame_ctx_p->byte_code_p += 3; + } +} /* vm_spread_operation */ #endif /* ENABLED (JERRY_ES2015) */ /** @@ -1465,6 +1580,23 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ #if ENABLED (JERRY_ES2015) case VM_OC_SUPER_CALL: { + uint8_t arguments_list_len = *byte_code_p++; + stack_top_p -= arguments_list_len; + + if (opcode >= CBC_EXT_SPREAD_SUPER_CALL) + { + ecma_collection_t *arguments_p = opfunc_spread_arguments (stack_top_p, arguments_list_len); + + if (JERRY_UNLIKELY (arguments_p == NULL)) + { + result = ECMA_VALUE_ERROR; + goto error; + } + + stack_top_p++; + ECMA_SET_INTERNAL_VALUE_POINTER (stack_top_p[-1], arguments_p); + } + frame_ctx_p->call_operation = VM_EXEC_SUPER_CALL; frame_ctx_p->byte_code_p = byte_code_start_p; frame_ctx_p->stack_top_p = stack_top_p; @@ -1894,6 +2026,27 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ *stack_top_p++ = result; goto free_left_value; } + case VM_OC_SPREAD_ARGUMENTS: + { + uint8_t arguments_list_len = *byte_code_p++; + stack_top_p -= arguments_list_len; + + ecma_collection_t *arguments_p = opfunc_spread_arguments (stack_top_p, arguments_list_len); + + if (JERRY_UNLIKELY (arguments_p == NULL)) + { + result = ECMA_VALUE_ERROR; + goto error; + } + + stack_top_p++; + ECMA_SET_INTERNAL_VALUE_POINTER (stack_top_p[-1], arguments_p); + + frame_ctx_p->call_operation = VM_EXEC_SPREAD_OP; + frame_ctx_p->byte_code_p = byte_code_start_p; + frame_ctx_p->stack_top_p = stack_top_p; + return ECMA_VALUE_UNDEFINED; + } #endif /* ENABLED (JERRY_ES2015) */ case VM_OC_PUSH_ELISON: { @@ -3859,6 +4012,11 @@ vm_execute (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ vm_super_call (frame_ctx_p); break; } + case VM_EXEC_SPREAD_OP: + { + vm_spread_operation (frame_ctx_p); + break; + } #endif /* ENABLED (JERRY_ES2015) */ case VM_EXEC_CONSTRUCT: { diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index b9ba0aa27..82f38e7cb 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -1,3 +1,4 @@ + /* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -252,6 +253,7 @@ typedef enum VM_OC_DEFAULT_INITIALIZER, /**< default initializer inside a pattern */ VM_OC_REST_INITIALIZER, /**< create rest object inside an array pattern */ VM_OC_INITIALIZER_PUSH_PROP, /**< push property for object initializer */ + VM_OC_SPREAD_ARGUMENTS, /**< perform function call/construct with spreaded arguments */ #endif /* ENABLED (JERRY_ES2015) */ VM_OC_NONE, /**< a special opcode for unsupported byte codes */ } vm_oc_types; @@ -300,7 +302,9 @@ typedef enum VM_OC_DEFAULT_INITIALIZER = VM_OC_NONE, /**< default initializer inside a pattern */ VM_OC_REST_INITIALIZER = VM_OC_NONE, /**< create rest object inside an array pattern */ VM_OC_INITIALIZER_PUSH_PROP = VM_OC_NONE, /**< push property for object initializer */ + VM_OC_SPREAD_ARGUMENTS = VM_OC_NONE, /**< perform function call/construct with spreaded arguments */ #endif /* !ENABLED (JERRY_ES2015) */ + VM_OC_UNUSED = VM_OC_NONE /**< placeholder if the list is empty */ } vm_oc_unused_types; @@ -381,6 +385,7 @@ typedef enum VM_NO_EXEC_OP, /**< do nothing */ VM_EXEC_CALL, /**< invoke a function */ VM_EXEC_SUPER_CALL, /**< invoke a function through 'super' keyword */ + VM_EXEC_SPREAD_OP, /**< call/construct operation with spreaded argument list */ VM_EXEC_CONSTRUCT, /**< construct a new object */ } vm_call_operation; diff --git a/tests/jerry/es2015/argument-spread.js b/tests/jerry/es2015/argument-spread.js new file mode 100644 index 000000000..c0bf7eb64 --- /dev/null +++ b/tests/jerry/es2015/argument-spread.js @@ -0,0 +1,100 @@ +// 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. + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax + +function mustThrow(str) { + try { + eval(str); + assert(false); + } catch (e) { + assert(e instanceof TypeError); + } +} + +function assertArrayEqual(actual, expected) { + assert(actual.length === expected.length); + + for (var i = 0; i < actual.length; i++) { + assert(actual[i] === expected[i]); + } +} + +// Spread syntax +function sum(x, y, z) { + return x + y + z; +} + +const numbers = [1, 2, 3]; + +assert(sum(...numbers) === 6); + +// Replace apply() +function myFunction (v, w, x, y, z) { + return v + w + x + y + z; +} +var args = [0, 1]; + +assert(myFunction (-1, ...args, 2, ...[3]) == 5); + +// Apply for new +var dateFields = [1970, 0, 1]; +var d = new Date(...dateFields); + +assert(d.toString().substring(0, 24) === "Thu Jan 01 1970 00:00:00"); + +function applyAndNew(constructor, args) { + function partial () { + return constructor.apply (this, args); + }; + if (typeof constructor.prototype === "object") { + partial.prototype = Object.create(constructor.prototype); + } + return partial; +} + +function myConstructor () { + assertArrayEqual(arguments, myArguments); + this.prop1 = "val1"; + this.prop2 = "val2"; +}; + +var myArguments = ["hi", "how", "are", "you", "mr", null]; +var myConstructorWithArguments = applyAndNew(myConstructor, myArguments); + +var obj = new myConstructorWithArguments; +assert(Object.keys(obj).length === 2); +assert(obj.prop1 === "val1"); +assert(obj.prop2 === "val2"); + +// Test spread prop call +var o = { f(a,b,c) { return a + b + c }, + g(a,b) { throw new TypeError ("5") } + }; + +assert (o.f(...["a", "b", "c"]) === "abc"); + +mustThrow ("o.g (...[1,2])") + +// Test spread super call +class MyArray extends Array { + constructor(...args) { + super(...args); + } +} + +var array = new MyArray(1, 2, 3); +assertArrayEqual(array, [1,2,3]); +assert(array instanceof MyArray); +assert(array instanceof Array);