From 1a4972fc3f03ebbcab5054350bda8b0bc66c2b0c Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Thu, 5 Dec 2019 13:50:53 +0100 Subject: [PATCH] Implement yield* operation in generator functions. (#3407) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/ecma/base/ecma-gc.c | 6 + jerry-core/ecma/base/ecma-globals.h | 2 + .../ecma-builtin-generator-prototype.c | 129 +++++--- .../ecma/operations/ecma-iterator-object.c | 280 ++++++++++++------ .../ecma/operations/ecma-iterator-object.h | 16 +- jerry-core/include/jerryscript-snapshot.h | 2 +- jerry-core/parser/js/byte-code.h | 2 + jerry-core/parser/js/js-parser-expr.c | 10 +- jerry-core/parser/js/js-scanner.c | 4 + jerry-core/vm/opcodes.c | 24 +- jerry-core/vm/vm.c | 21 +- .../jerry/es2015/generator-yield-iterator.js | 121 ++++++++ tests/unit-core/test-snapshot.c | 2 +- 13 files changed, 476 insertions(+), 143 deletions(-) create mode 100644 tests/jerry/es2015/generator-yield-iterator.js diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index f02bafee2..e7273ea4f 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -377,6 +377,12 @@ ecma_gc_mark_executable_object (ecma_object_t *object_p) /**< object */ return; } + if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_GENERATOR_ITERATE_AND_YIELD) + { + ecma_value_t iterator = executable_object_p->extended_object.u.class_prop.u.value; + ecma_gc_set_object_visited (ecma_get_object_from_value (iterator)); + } + ecma_gc_set_object_visited (executable_object_p->frame_ctx.lex_env_p); if (ecma_is_value_object (executable_object_p->frame_ctx.this_binding)) diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 011e96baf..8d75a315b 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -1761,6 +1761,8 @@ typedef enum { ECMA_EXECUTABLE_OBJECT_COMPLETED = (1u << 0), /**< executable object is completed and cannot be resumed */ ECMA_EXECUTABLE_OBJECT_RUNNING = (1u << 1), /**< executable object is currently running */ + /* Generator specific flags. */ + ECMA_GENERATOR_ITERATE_AND_YIELD = (1u << 2), /**< the generator performs a yield* operation */ } ecma_executable_object_flags_t; /** diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c index 43a2b4ba2..44277709d 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c @@ -15,9 +15,11 @@ #include "ecma-builtins.h" #include "ecma-exceptions.h" +#include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-iterator-object.h" +#include "jcontext.h" #include "opcodes.h" #include "vm-defines.h" @@ -40,16 +42,6 @@ * @{ */ -/** - * Generator resume execution flags. - */ -typedef enum -{ - ECMA_GENERATOR_NEXT, /**< generator should continue its execution */ - ECMA_GENERATOR_RETURN, /**< generator should perform a return operation */ - ECMA_GENERATOR_THROW, /**< generator should perform a throw operation */ -} ecma_generator_resume_mode_t; - /** * Byte code sequence which returns from the generator. */ @@ -75,7 +67,7 @@ static const uint8_t ecma_builtin_generator_prototype_throw[1] = static ecma_value_t ecma_builtin_generator_prototype_object_do (ecma_value_t this_arg, /**< this argument */ ecma_value_t arg, /**< argument */ - ecma_generator_resume_mode_t resume_mode) /**< resume mode */ + ecma_iterator_command_type_t resume_mode) /**< resume mode */ { vm_executable_object_t *executable_object_p = NULL; @@ -109,27 +101,98 @@ ecma_builtin_generator_prototype_object_do (ecma_value_t this_arg, /**< this arg return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); } - if (resume_mode == ECMA_GENERATOR_RETURN) + arg = ecma_copy_value (arg); + + while (true) { - executable_object_p->frame_ctx.byte_code_p = ecma_builtin_generator_prototype_return; + if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_GENERATOR_ITERATE_AND_YIELD) + { + ecma_value_t iterator = executable_object_p->extended_object.u.class_prop.u.value; + + bool done = false; + ecma_value_t result = ecma_op_iterator_do (resume_mode, iterator, arg, &done); + ecma_free_value (arg); + + if (ECMA_IS_VALUE_ERROR (result)) + { + arg = result; + } + else if (done) + { + arg = ecma_op_iterator_value (result); + ecma_free_value (result); + if (resume_mode == ECMA_ITERATOR_THROW) + { + /* This part is changed in the newest ECMAScript standard. + * It was ECMA_ITERATOR_RETURN in ES2015, but I think it was a typo. */ + resume_mode = ECMA_ITERATOR_NEXT; + } + } + else + { + return result; + } + + executable_object_p->extended_object.u.class_prop.extra_info &= (uint16_t) ~ECMA_GENERATOR_ITERATE_AND_YIELD; + + if (ECMA_IS_VALUE_ERROR (arg)) + { + arg = JERRY_CONTEXT (error_value); + JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_EXCEPTION; + resume_mode = ECMA_ITERATOR_THROW; + } + } + + if (resume_mode == ECMA_ITERATOR_RETURN) + { + executable_object_p->frame_ctx.byte_code_p = ecma_builtin_generator_prototype_return; + } + else if (resume_mode == ECMA_ITERATOR_THROW) + { + executable_object_p->frame_ctx.byte_code_p = ecma_builtin_generator_prototype_throw; + } + + ecma_value_t value = opfunc_resume_executable_object (executable_object_p, arg); + + if (ECMA_IS_VALUE_ERROR (value)) + { + return value; + } + + bool done = (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_COMPLETED); + + if (!done) + { + const uint8_t *byte_code_p = executable_object_p->frame_ctx.byte_code_p; + + JERRY_ASSERT (byte_code_p[-2] == CBC_EXT_OPCODE + && (byte_code_p[-1] == CBC_EXT_YIELD || byte_code_p[-1] == CBC_EXT_YIELD_ITERATOR)); + + if (byte_code_p[-1] == CBC_EXT_YIELD_ITERATOR) + { + ecma_value_t iterator = ecma_op_get_iterator (value, ECMA_VALUE_EMPTY); + ecma_free_value (value); + + if (ECMA_IS_VALUE_ERROR (iterator)) + { + resume_mode = ECMA_ITERATOR_THROW; + arg = JERRY_CONTEXT (error_value); + JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_EXCEPTION; + continue; + } + + ecma_deref_object (ecma_get_object_from_value (iterator)); + executable_object_p->extended_object.u.class_prop.extra_info |= ECMA_GENERATOR_ITERATE_AND_YIELD; + executable_object_p->extended_object.u.class_prop.u.value = iterator; + arg = ECMA_VALUE_UNDEFINED; + continue; + } + } + + ecma_value_t result = ecma_create_iter_result_object (value, ecma_make_boolean_value (done)); + ecma_fast_free_value (value); + return result; } - else if (resume_mode == ECMA_GENERATOR_THROW) - { - executable_object_p->frame_ctx.byte_code_p = ecma_builtin_generator_prototype_throw; - } - - ecma_value_t value = opfunc_resume_executable_object (executable_object_p, arg); - - if (JERRY_UNLIKELY (ECMA_IS_VALUE_ERROR (value))) - { - return value; - } - - bool done = (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_COMPLETED); - ecma_value_t result = ecma_create_iter_result_object (value, ecma_make_boolean_value (done)); - - ecma_fast_free_value (value); - return result; } /* ecma_builtin_generator_prototype_object_do */ /** @@ -145,7 +208,7 @@ static ecma_value_t ecma_builtin_generator_prototype_object_next (ecma_value_t this_arg, /**< this argument */ ecma_value_t next_arg) /**< next argument */ { - return ecma_builtin_generator_prototype_object_do (this_arg, next_arg, ECMA_GENERATOR_NEXT); + return ecma_builtin_generator_prototype_object_do (this_arg, next_arg, ECMA_ITERATOR_NEXT); } /* ecma_builtin_generator_prototype_object_next */ /** @@ -161,7 +224,7 @@ static ecma_value_t ecma_builtin_generator_prototype_object_return (ecma_value_t this_arg, /**< this argument */ ecma_value_t return_arg) /**< return argument */ { - return ecma_builtin_generator_prototype_object_do (this_arg, return_arg, ECMA_GENERATOR_RETURN); + return ecma_builtin_generator_prototype_object_do (this_arg, return_arg, ECMA_ITERATOR_RETURN); } /* ecma_builtin_generator_prototype_object_return */ /** @@ -177,7 +240,7 @@ static ecma_value_t ecma_builtin_generator_prototype_object_throw (ecma_value_t this_arg, /**< this argument */ ecma_value_t throw_arg) /**< throw argument */ { - return ecma_builtin_generator_prototype_object_do (this_arg, throw_arg, ECMA_GENERATOR_THROW); + return ecma_builtin_generator_prototype_object_do (this_arg, throw_arg, ECMA_ITERATOR_THROW); } /* ecma_builtin_generator_prototype_object_throw */ /** diff --git a/jerry-core/ecma/operations/ecma-iterator-object.c b/jerry-core/ecma/operations/ecma-iterator-object.c index 518fd6183..3b4709c04 100644 --- a/jerry-core/ecma/operations/ecma-iterator-object.c +++ b/jerry-core/ecma/operations/ecma-iterator-object.c @@ -248,20 +248,20 @@ ecma_op_iterator_next (ecma_value_t iterator, /**< iterator value */ /* 1 - 2. */ ecma_object_t *obj_p = ecma_get_object_from_value (iterator); - ecma_value_t next = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_NEXT); + ecma_value_t func_next = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_NEXT); - if (ECMA_IS_VALUE_ERROR (next)) + if (ECMA_IS_VALUE_ERROR (func_next)) { - return next; + return func_next; } - if (!ecma_is_value_object (next) || !ecma_op_is_callable (next)) + if (!ecma_is_value_object (func_next) || !ecma_op_is_callable (func_next)) { - ecma_free_value (next); - return ecma_raise_type_error (ECMA_ERR_MSG ("Next is not callable.")); + ecma_free_value (func_next); + return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator next() is not callable.")); } - ecma_object_t *next_obj_p = ecma_get_object_from_value (next); + ecma_object_t *next_obj_p = ecma_get_object_from_value (func_next); bool has_value = !ecma_is_value_empty (value); @@ -275,58 +275,107 @@ ecma_op_iterator_next (ecma_value_t iterator, /**< iterator value */ result = ecma_op_function_call (next_obj_p, iterator, NULL, 0); } - ecma_free_value (next); - - /* 3. */ - if (ECMA_IS_VALUE_ERROR (result)) - { - return result; - } - - /* 4. */ - if (!ecma_is_value_object (result)) - { - ecma_free_value (result); - return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator result is not an object.")); - } + ecma_free_value (func_next); /* 5. */ return result; } /* ecma_op_iterator_next */ /** - * IteratorComplete operation + * IteratorReturn operation * - * See also: ECMA-262 v6, 7.4.3 + * See also: ECMA-262 v6, 14.4.14 (last part) * * Note: * Returned value must be freed with ecma_free_value. * - * @return ECMA_VALUE_{FALSE, TRUE} - if success + * @return iterator result object - if success * raised error - otherwise */ static ecma_value_t -ecma_op_iterator_complete (ecma_value_t iter_result) /**< iterator value */ +ecma_op_iterator_return (ecma_value_t iterator, /**< iterator value */ + ecma_value_t value) /**< the routines's value argument */ { - /* 1. */ - JERRY_ASSERT (ecma_is_value_object (iter_result)); + JERRY_ASSERT (ecma_is_value_object (iterator)); - /* 2. */ - ecma_object_t *obj_p = ecma_get_object_from_value (iter_result); + ecma_object_t *obj_p = ecma_get_object_from_value (iterator); + ecma_value_t func_return = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_RETURN); - ecma_value_t done = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_DONE); - - if (ECMA_IS_VALUE_ERROR (done)) + if (ECMA_IS_VALUE_ERROR (func_return)) { - return done; + return func_return; } - bool is_done = ecma_op_to_boolean (done); + if (func_return == ECMA_VALUE_UNDEFINED) + { + return ecma_create_iter_result_object (value, true); + } - ecma_free_value (done); + if (!ecma_is_value_object (func_return) || !ecma_op_is_callable (func_return)) + { + ecma_free_value (func_return); + return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator return() is not callable.")); + } - return ecma_make_boolean_value (is_done); -} /* ecma_op_iterator_complete */ + ecma_object_t *return_obj_p = ecma_get_object_from_value (func_return); + + ecma_value_t result = ecma_op_function_call (return_obj_p, iterator, &value, 1); + ecma_free_value (func_return); + + return result; +} /* ecma_op_iterator_return */ + +/** + * IteratorThrow operation + * + * See also: ECMA-262 v6, 14.4.14 (last part) + * + * Note: + * Returned value must be freed with ecma_free_value. + * + * @return iterator result object - if success + * raised error - otherwise + */ +static ecma_value_t +ecma_op_iterator_throw (ecma_value_t iterator, /**< iterator value */ + ecma_value_t value) /**< the routines's value argument */ +{ + JERRY_ASSERT (ecma_is_value_object (iterator)); + + ecma_object_t *obj_p = ecma_get_object_from_value (iterator); + ecma_value_t func_throw = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_THROW); + + if (ECMA_IS_VALUE_ERROR (func_throw)) + { + return func_throw; + } + + if (func_throw == ECMA_VALUE_UNDEFINED) + { + ecma_value_t result = ecma_op_iterator_close (iterator); + + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + + ecma_free_value (result); + return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator throw() is not available.")); + } + + if (!ecma_is_value_object (func_throw) || !ecma_op_is_callable (func_throw)) + { + ecma_free_value (func_throw); + return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator throw() is not callable.")); + } + + ecma_object_t *return_obj_p = ecma_get_object_from_value (func_throw); + + ecma_value_t result = ecma_op_function_call (return_obj_p, iterator, &value, 1); + ecma_free_value (func_throw); + + return result; +} /* ecma_op_iterator_throw */ /** * IteratorValue operation @@ -349,52 +398,6 @@ ecma_op_iterator_value (ecma_value_t iter_result) /**< iterator value */ return ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_VALUE); } /* ecma_op_iterator_value */ -/** - * IteratorStep operation - * - * See also: ECMA-262 v6, 7.4.5 - * - * Note: - * Returned value must be freed with ecma_free_value. - * - * @return iterator object or ECMA_VALUE_FALSE - if success - * raised error - otherwise - */ -ecma_value_t -ecma_op_iterator_step (ecma_value_t iterator) /**< iterator value */ -{ - /* 1. */ - ecma_value_t result = ecma_op_iterator_next (iterator, ECMA_VALUE_EMPTY); - - /* 2. */ - if (ECMA_IS_VALUE_ERROR (result)) - { - return result; - } - - /* 3. */ - ecma_value_t done = ecma_op_iterator_complete (result); - - /* 4. */ - if (ECMA_IS_VALUE_ERROR (done)) - { - ecma_free_value (result); - return done; - } - - ecma_free_value (done); - - /* 5. */ - if (ecma_is_value_true (done)) - { - ecma_free_value (result); - return ECMA_VALUE_FALSE; - } - - /* 6. */ - return result; -} /* ecma_op_iterator_step */ - /** * IteratorClose operation * @@ -487,6 +490,117 @@ ecma_op_iterator_close (ecma_value_t iterator) /**< iterator value */ return ECMA_VALUE_ERROR; } /* ecma_op_iterator_close */ +/** + * IteratorStep operation + * + * See also: ECMA-262 v6, 7.4.5 + * + * Note: + * Returned value must be freed with ecma_free_value. + * + * @return iterator object or ECMA_VALUE_FALSE - if success + * raised error - otherwise + */ +ecma_value_t +ecma_op_iterator_step (ecma_value_t iterator) /**< iterator value */ +{ + /* 1. */ + ecma_value_t result = ecma_op_iterator_next (iterator, ECMA_VALUE_EMPTY); + + /* 2. */ + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + + if (!ecma_is_value_object (result)) + { + ecma_free_value (result); + return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator result is not an object.")); + } + + /* 3. */ + ecma_object_t *obj_p = ecma_get_object_from_value (result); + ecma_value_t done = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_DONE); + + /* 4. */ + if (ECMA_IS_VALUE_ERROR (done)) + { + ecma_free_value (result); + return done; + } + + bool is_done = ecma_op_to_boolean (done); + ecma_free_value (done); + + /* 5. */ + if (is_done) + { + ecma_free_value (result); + return ECMA_VALUE_FALSE; + } + + /* 6. */ + return result; +} /* ecma_op_iterator_step */ + +/** + * Perform a command specified by the command argument + * + * Note: + * Returned value must be freed with ecma_free_value. + * + * @return iterator object - if success + * raised error - otherwise + */ +ecma_value_t +ecma_op_iterator_do (ecma_iterator_command_type_t command, /**< command to be executed */ + ecma_value_t iterator, /**< iterator object */ + ecma_value_t value, /**< the routines's value argument */ + bool *done_p) /**< it contains the logical value of the done property */ +{ + ecma_value_t result; + + if (command == ECMA_ITERATOR_NEXT) + { + result = ecma_op_iterator_next (iterator, value); + } + else if (command == ECMA_ITERATOR_RETURN) + { + result = ecma_op_iterator_return (iterator, value); + } + else + { + JERRY_ASSERT (command == ECMA_ITERATOR_THROW); + result = ecma_op_iterator_throw (iterator, value); + } + + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + + if (!ecma_is_value_object (result)) + { + ecma_free_value (result); + return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator result is not an object.")); + } + + ecma_object_t *obj_p = ecma_get_object_from_value (result); + ecma_value_t done = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_DONE); + + if (ECMA_IS_VALUE_ERROR (done)) + { + ecma_free_value (result); + return done; + } + + *done_p = ecma_op_to_boolean (done); + ecma_free_value (done); + + return result; +} /* ecma_op_iterator_do */ + #endif /* ENABLED (JERRY_ES2015) */ /** diff --git a/jerry-core/ecma/operations/ecma-iterator-object.h b/jerry-core/ecma/operations/ecma-iterator-object.h index 3c02e5d01..70f46e19d 100644 --- a/jerry-core/ecma/operations/ecma-iterator-object.h +++ b/jerry-core/ecma/operations/ecma-iterator-object.h @@ -27,6 +27,16 @@ * @{ */ +/** + * Generator resume execution flags. + */ +typedef enum +{ + ECMA_ITERATOR_NEXT, /**< generator should continue its execution */ + ECMA_ITERATOR_RETURN, /**< generator should perform a return operation */ + ECMA_ITERATOR_THROW, /**< generator should perform a throw operation */ +} ecma_iterator_command_type_t; + /** * Maximum value of [[%Iterator%NextIndex]] until it can be stored * in an ecma pseudo array object structure element. @@ -49,11 +59,15 @@ ecma_op_get_iterator (ecma_value_t value, ecma_value_t method); ecma_value_t ecma_op_iterator_value (ecma_value_t iter_result); +ecma_value_t +ecma_op_iterator_close (ecma_value_t iterator); + ecma_value_t ecma_op_iterator_step (ecma_value_t iterator); ecma_value_t -ecma_op_iterator_close (ecma_value_t iterator); +ecma_op_iterator_do (ecma_iterator_command_type_t command, ecma_value_t iterator, + ecma_value_t value, bool *done_p); #endif /* ENABLED (JERRY_ES2015) */ diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index a79745879..c0f3c1093 100644 --- a/jerry-core/include/jerryscript-snapshot.h +++ b/jerry-core/include/jerryscript-snapshot.h @@ -30,7 +30,7 @@ extern "C" /** * Jerry snapshot format version. */ -#define JERRY_SNAPSHOT_VERSION (33u) +#define JERRY_SNAPSHOT_VERSION (34u) /** * Flags for jerry_generate_snapshot and jerry_generate_function_snapshot. diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index aaa693c0c..21addae4d 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -658,6 +658,8 @@ VM_OC_CREATE_GENERATOR) \ CBC_OPCODE (CBC_EXT_YIELD, CBC_NO_FLAG, 0, \ VM_OC_YIELD | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_EXT_YIELD_ITERATOR, CBC_NO_FLAG, 0, \ + VM_OC_YIELD | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_RETURN, CBC_NO_FLAG, -1, \ VM_OC_EXT_RETURN | VM_OC_GET_STACK) \ \ diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index be3233b46..b594f2ece 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -1563,8 +1563,16 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ parser_check_assignment_expr (context_p); lexer_next_token (context_p); + cbc_ext_opcode_t opcode = CBC_EXT_YIELD; + if (!lexer_check_yield_no_arg (context_p)) { + if (context_p->token.type == LEXER_MULTIPLY) + { + lexer_next_token (context_p); + opcode = CBC_EXT_YIELD_ITERATOR; + } + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); } else @@ -1572,7 +1580,7 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); } - parser_emit_cbc_ext (context_p, CBC_EXT_YIELD); + parser_emit_cbc_ext (context_p, opcode); return (context_p->token.type != LEXER_RIGHT_PAREN && context_p->token.type != LEXER_COMMA); diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index e2cfb5fe8..df60a83cb 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -648,6 +648,10 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; } + if (context_p->token.type == LEXER_MULTIPLY) + { + return SCAN_NEXT_TOKEN; + } return SCAN_KEEP_TOKEN; } #endif /* ENABLED (JERRY_ES2015) */ diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index fdd9fc4c6..37d2b31c2 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -319,23 +319,23 @@ opfunc_append_to_spread_array (ecma_value_t *stack_top_p, /**< current stack top { while (true) { - ecma_value_t next = ecma_op_iterator_step (iterator); + ecma_value_t next_value = ecma_op_iterator_step (iterator); - if (ECMA_IS_VALUE_ERROR (next)) + if (ECMA_IS_VALUE_ERROR (next_value)) { break; } - if (ecma_is_value_false (next)) + if (ecma_is_value_false (next_value)) { idx--; ret_value = ECMA_VALUE_EMPTY; break; } - ecma_value_t value = ecma_op_iterator_value (next); + ecma_value_t value = ecma_op_iterator_value (next_value); - ecma_free_value (next); + ecma_free_value (next_value); if (ECMA_IS_VALUE_ERROR (value)) { @@ -412,22 +412,22 @@ opfunc_spread_arguments (ecma_value_t *stack_top_p, /**< pointer to the current { while (true) { - ecma_value_t next = ecma_op_iterator_step (iterator); + ecma_value_t next_value = ecma_op_iterator_step (iterator); - if (ECMA_IS_VALUE_ERROR (next)) + if (ECMA_IS_VALUE_ERROR (next_value)) { break; } - if (ecma_is_value_false (next)) + if (ecma_is_value_false (next_value)) { ret_value = ECMA_VALUE_EMPTY; break; } - ecma_value_t value = ecma_op_iterator_value (next); + ecma_value_t value = ecma_op_iterator_value (next_value); - ecma_free_value (next); + ecma_free_value (next_value); if (ECMA_IS_VALUE_ERROR (value)) { @@ -615,7 +615,7 @@ opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ ecma_value_t opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /**< executable object */ - ecma_value_t value) /**< value pushed onto the stack */ + ecma_value_t value) /**< value pushed onto the stack (takes the reference) */ { const ecma_compiled_code_t *bytecode_header_p = executable_object_p->frame_ctx.bytecode_header_p; ecma_value_t *register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx); @@ -654,7 +654,7 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /* ecma_ref_if_object (*register_p++); } - *register_p++ = ecma_copy_value (value); + *register_p++ = value; executable_object_p->frame_ctx.stack_top_p = register_p; JERRY_ASSERT (ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED (executable_object_p->extended_object.u.class_prop.extra_info)); diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index ae70ab5c7..a137360b6 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -3423,16 +3423,16 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ goto error; } - ecma_value_t iterator_step = ecma_op_iterator_step (iterator); + ecma_value_t next_value = ecma_op_iterator_step (iterator); - if (ECMA_IS_VALUE_ERROR (iterator_step)) + if (ECMA_IS_VALUE_ERROR (next_value)) { ecma_free_value (iterator); - result = iterator_step; + result = next_value; goto error; } - if (ecma_is_value_false (iterator_step)) + if (ecma_is_value_false (next_value)) { ecma_free_value (iterator); byte_code_p = byte_code_start_p + branch_offset; @@ -3444,7 +3444,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION; stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_FOR_OF, branch_offset); - stack_top_p[-2] = iterator_step; + stack_top_p[-2] = next_value; stack_top_p[-3] = iterator; if (byte_code_p[0] == CBC_EXT_OPCODE && byte_code_p[1] == CBC_EXT_CLONE_CONTEXT) @@ -3474,18 +3474,18 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); - ecma_value_t iterator_step = ecma_op_iterator_step (stack_top_p[-3]); + ecma_value_t next_value = ecma_op_iterator_step (stack_top_p[-3]); - if (ECMA_IS_VALUE_ERROR (iterator_step)) + if (ECMA_IS_VALUE_ERROR (next_value)) { - result = iterator_step; + result = next_value; goto error; } - if (!ecma_is_value_false (iterator_step)) + if (!ecma_is_value_false (next_value)) { ecma_free_value (stack_top_p[-2]); - stack_top_p[-2] = iterator_step; + stack_top_p[-2] = next_value; byte_code_p = byte_code_start_p + branch_offset; continue; } @@ -3494,7 +3494,6 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ ecma_free_value (stack_top_p[-3]); VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); stack_top_p -= PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION; - continue; } #endif /* ENABLED (JERRY_ES2015) */ diff --git a/tests/jerry/es2015/generator-yield-iterator.js b/tests/jerry/es2015/generator-yield-iterator.js new file mode 100644 index 000000000..3b36d98d4 --- /dev/null +++ b/tests/jerry/es2015/generator-yield-iterator.js @@ -0,0 +1,121 @@ +/* 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 check_result(result, value, done) +{ + assert(result.value === value) + assert(result.done === done) +} + +function *gen1() { + yield 1 + yield *[2,3,4] + yield 5 +} + +var g = gen1() +check_result(g.next(), 1, false) +check_result(g.next(), 2, false) +check_result(g.next(), 3, false) +check_result(g.next(), 4, false) +check_result(g.next(), 5, false) +check_result(g.next(), undefined, true) + +function *gen2() { + yield * true +} + +try { + g = gen2() + g.next() + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +var t0 = 0, t1 = 0 + +function *gen3() { + function *f() { + try { + yield 5 + } finally { + t0 = 1 + } + } + + try { + yield *f() + } finally { + t1 = 1 + } +} + +g = gen3() +check_result(g.next(), 5, false) +check_result(g.return(13), 13, true) +assert(t0 === 1) +assert(t1 === 1) + +t0 = -1 +t1 = 0 + +function *gen4() { + function next(arg) + { + t0++; + + if (t0 === 0) + { + assert(arg === undefined); + return { value:2, done:false } + } + if (t0 === 1) + { + assert(arg === -3); + return { value:3, done:false } + } + assert(arg === -4); + return { value:4, done:true } + } + + var o = { [Symbol.iterator]() { return { next } } } + assert((yield *o) === 4) + return 5; +} + +g = gen4() +check_result(g.next(-2), 2, false) +check_result(g.next(-3), 3, false) +check_result(g.next(-4), 5, true) + +function *gen5() { + function *f() { + try { + yield 1 + assert(false) + } catch (e) { + assert(e === 10) + } + return 2 + } + + assert((yield *f()) === 2) + yield 3 +} + +g = gen5() +check_result(g.next(), 1, false) +check_result(g.throw(10), 3, false) diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c index 530e4bbe8..750870551 100644 --- a/tests/unit-core/test-snapshot.c +++ b/tests/unit-core/test-snapshot.c @@ -223,7 +223,7 @@ main (void) /* Check the snapshot data. Unused bytes should be filled with zeroes */ const uint8_t expected_data[] = { - 0x4A, 0x52, 0x52, 0x59, 0x21, 0x00, 0x00, 0x00, + 0x4A, 0x52, 0x52, 0x59, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x41, 0x00, 0x01, 0x00,