From 6389816f671c1497e5699610b9ee678887b1d31a Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Wed, 1 Jul 2020 12:40:39 +0200 Subject: [PATCH] Implement AsyncIteratorClose for for-await-of statement. (#3955) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/ecma/base/ecma-gc.c | 2 +- jerry-core/ecma/base/ecma-globals.h | 1 + .../operations/ecma-async-generator-object.c | 15 + jerry-core/ecma/operations/ecma-jobqueue.c | 7 + jerry-core/vm/vm-stack.c | 177 +++-- jerry-core/vm/vm-stack.h | 17 +- jerry-core/vm/vm.c | 229 ++++--- .../es.next/for-await-of-iterator-close.js | 623 ++++++++++++++++++ 8 files changed, 948 insertions(+), 123 deletions(-) create mode 100644 tests/jerry/es.next/for-await-of-iterator-close.js diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index cd39e7a75..8cc88d7d1 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -937,7 +937,7 @@ ecma_gc_free_executable_object (ecma_object_t *object_p) /**< object */ do { - context_top_p[-1] &= (uint32_t) ~(VM_CONTEXT_HAS_LEX_ENV | VM_CONTEXT_CLOSE_ITERATOR); + context_top_p[-1] &= (uint32_t) ~VM_CONTEXT_HAS_LEX_ENV; uint32_t offsets = vm_get_context_value_offsets (context_top_p); diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index da535e936..f7b40b4c8 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -1884,6 +1884,7 @@ typedef enum ECMA_AWAIT_YIELD_OPERATION, /**< wait for the generator operation (next/throw/return) */ ECMA_AWAIT_YIELD_CLOSE, /**< wait for the result of iterator close operation */ /* After adding new ECMA_AWAIT_YIELD items, the ECMA_AWAIT_YIELD_END should be updated. */ + ECMA_AWAIT_FOR_CLOSE, /**< wait for a close iterator result object of for-await-of statement */ ECMA_AWAIT_FOR_NEXT, /**< wait for an iterator result object of for-await-of statement */ } ecma_await_states_t; diff --git a/jerry-core/ecma/operations/ecma-async-generator-object.c b/jerry-core/ecma/operations/ecma-async-generator-object.c index 41c517470..65761432c 100644 --- a/jerry-core/ecma/operations/ecma-async-generator-object.c +++ b/jerry-core/ecma/operations/ecma-async-generator-object.c @@ -450,10 +450,24 @@ ecma_await_continue (vm_executable_object_t *executable_object_p, /**< executabl ecma_free_value (value); return ecma_raise_type_error (msg_p); } + case ECMA_AWAIT_FOR_CLOSE: + { + bool is_value_object = ecma_is_value_object (value); + ecma_free_value (value); + ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (executable_object_p); + + if (!is_value_object + && VM_GET_CONTEXT_TYPE (executable_object_p->frame_ctx.stack_top_p[-1]) != VM_CONTEXT_FINALLY_THROW) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator return() result is not object")); + } + return ECMA_VALUE_EMPTY; + } default: { JERRY_ASSERT (state == ECMA_AWAIT_FOR_NEXT); JERRY_ASSERT (VM_GET_CONTEXT_TYPE (executable_object_p->frame_ctx.stack_top_p[-1]) == VM_CONTEXT_FOR_AWAIT_OF); + JERRY_ASSERT (!(executable_object_p->frame_ctx.stack_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR)); if (!ecma_is_value_object (value)) { @@ -491,6 +505,7 @@ ecma_await_continue (vm_executable_object_t *executable_object_p, /**< executabl /* It seems browsers call Await(result) here, although the standard does not * requests to do so. The following code might follow browsers in the future. */ ecma_deref_if_object (result); + stack_top_p[-1] |= VM_CONTEXT_CLOSE_ITERATOR; stack_top_p[-2] = result; ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (executable_object_p); return ECMA_VALUE_EMPTY; diff --git a/jerry-core/ecma/operations/ecma-jobqueue.c b/jerry-core/ecma/operations/ecma-jobqueue.c index 54dd85155..8b41af355 100644 --- a/jerry-core/ecma/operations/ecma-jobqueue.c +++ b/jerry-core/ecma/operations/ecma-jobqueue.c @@ -22,6 +22,7 @@ #include "ecma-promise-object.h" #include "jcontext.h" #include "opcodes.h" +#include "vm-stack.h" #if ENABLED (JERRY_BUILTIN_PROMISE) @@ -280,6 +281,12 @@ ecma_process_promise_async_reaction_job (ecma_job_promise_async_reaction_t *job_ || ecma_is_value_object (executable_object_p->frame_ctx.stack_top_p[-1])); executable_object_p->frame_ctx.stack_top_p--; } + else if (ECMA_AWAIT_GET_STATE (executable_object_p) == ECMA_AWAIT_FOR_CLOSE + && VM_GET_CONTEXT_TYPE (executable_object_p->frame_ctx.stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW) + { + ecma_free_value (job_p->argument); + job_p->argument = ecma_copy_value (executable_object_p->frame_ctx.stack_top_p[-2]); + } /* Exception: Abort iterators, clear all status. */ executable_object_p->extended_object.u.class_prop.extra_info &= ECMA_AWAIT_CLEAR_MASK; diff --git a/jerry-core/vm/vm-stack.c b/jerry-core/vm/vm-stack.c index d45dd20ad..f3031a865 100644 --- a/jerry-core/vm/vm-stack.c +++ b/jerry-core/vm/vm-stack.c @@ -14,11 +14,16 @@ */ #include "ecma-alloc.h" +#include "ecma-exceptions.h" +#include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-helpers.h" +#include "ecma-iterator-object.h" +#include "ecma-objects.h" +#include "ecma-promise-object.h" +#include "jcontext.h" #include "vm-defines.h" #include "vm-stack.h" -#include "ecma-iterator-object.h" /** \addtogroup vm Virtual machine * @{ @@ -84,16 +89,10 @@ vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ case VM_CONTEXT_FOR_OF: case VM_CONTEXT_FOR_AWAIT_OF: { - ecma_value_t iterator = vm_stack_top_p[-3]; ecma_free_value (vm_stack_top_p[-2]); + ecma_free_value (vm_stack_top_p[-3]); ecma_free_value (vm_stack_top_p[-4]); - if (context_info & VM_CONTEXT_CLOSE_ITERATOR) - { - ecma_op_iterator_close (iterator); - } - ecma_free_value (iterator); - VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); vm_stack_top_p -= PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION; break; @@ -158,20 +157,29 @@ vm_decode_branch_offset (const uint8_t *branch_offset_p, /**< start offset of by return branch_offset; } /* vm_decode_branch_offset */ +#if ENABLED (JERRY_ESNEXT) + +/** + * Byte code which resumes an executable object with throw + */ +static const uint8_t vm_stack_resume_executable_object_with_context_end[1] = +{ + CBC_CONTEXT_END +}; + +#endif /* ENABLED (JERRY_ESNEXT) */ + /** * Find a finally up to the end position. * - * @return true - if 'finally' found, - * false - otherwise + * @return value specified in vm_stack_found_type */ -bool +vm_stack_found_type vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ - ecma_value_t **vm_stack_top_ref_p, /**< current stack top */ + ecma_value_t *stack_top_p, /**< current stack top */ vm_stack_context_type_t finally_type, /**< searching this finally */ uint32_t search_limit) /**< search up-to this byte code */ { - ecma_value_t *vm_stack_top_p = *vm_stack_top_ref_p; - JERRY_ASSERT (finally_type <= VM_CONTEXT_FINALLY_RETURN); if (finally_type != VM_CONTEXT_FINALLY_JUMP) @@ -182,15 +190,15 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ while (frame_ctx_p->context_depth > 0) { vm_stack_context_type_t context_type; - uint32_t context_end = VM_GET_CONTEXT_END (vm_stack_top_p[-1]); + uint32_t context_end = VM_GET_CONTEXT_END (stack_top_p[-1]); if (search_limit < context_end) { - *vm_stack_top_ref_p = vm_stack_top_p; - return false; + frame_ctx_p->stack_top_p = stack_top_p; + return VM_CONTEXT_FOUND_EXPECTED; } - context_type = VM_GET_CONTEXT_TYPE (vm_stack_top_p[-1]); + context_type = VM_GET_CONTEXT_TYPE (stack_top_p[-1]); if (context_type == VM_CONTEXT_TRY || context_type == VM_CONTEXT_CATCH) { const uint8_t *byte_code_p; @@ -199,12 +207,12 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ if (search_limit == context_end) { - *vm_stack_top_ref_p = vm_stack_top_p; - return false; + frame_ctx_p->stack_top_p = stack_top_p; + return VM_CONTEXT_FOUND_EXPECTED; } #if ENABLED (JERRY_ESNEXT) - if (vm_stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) + if (stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) { ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); @@ -230,13 +238,12 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ { branch_offset += (uint32_t) (byte_code_p - frame_ctx_p->byte_code_start_p); - vm_stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_CATCH, branch_offset); + stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_CATCH, branch_offset); byte_code_p += 2 + branch_offset_length; frame_ctx_p->byte_code_p = byte_code_p; - - *vm_stack_top_ref_p = vm_stack_top_p; - return true; + frame_ctx_p->stack_top_p = stack_top_p; + return VM_CONTEXT_FOUND_FINALLY; } byte_code_p += branch_offset; @@ -244,7 +251,7 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ if (*byte_code_p == CBC_CONTEXT_END) { VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); - vm_stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; + stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; continue; } } @@ -254,7 +261,7 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ JERRY_ASSERT (context_type == VM_CONTEXT_CATCH); #if !ENABLED (JERRY_ESNEXT) - if (vm_stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) + if (stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) { ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); @@ -266,7 +273,7 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ if (byte_code_p[0] == CBC_CONTEXT_END) { VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); - vm_stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; + stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; continue; } } @@ -274,18 +281,17 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ JERRY_ASSERT (byte_code_p[0] == CBC_EXT_OPCODE); VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FINALLY_CONTEXT_EXTRA_STACK_ALLOCATION); - vm_stack_top_p += PARSER_FINALLY_CONTEXT_EXTRA_STACK_ALLOCATION; + stack_top_p += PARSER_FINALLY_CONTEXT_EXTRA_STACK_ALLOCATION; #if ENABLED (JERRY_ESNEXT) if (JERRY_UNLIKELY (byte_code_p[1] == CBC_EXT_ASYNC_EXIT)) { branch_offset = (uint32_t) (byte_code_p - frame_ctx_p->byte_code_start_p); - vm_stack_top_p[-1] = VM_CREATE_CONTEXT ((uint32_t) finally_type, branch_offset); + stack_top_p[-1] = VM_CREATE_CONTEXT ((uint32_t) finally_type, branch_offset); frame_ctx_p->byte_code_p = byte_code_p; - - *vm_stack_top_ref_p = vm_stack_top_p; - return true; + frame_ctx_p->stack_top_p = stack_top_p; + return VM_CONTEXT_FOUND_FINALLY; } #endif /* ENABLED (JERRY_ESNEXT) */ @@ -298,20 +304,111 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ branch_offset += (uint32_t) (byte_code_p - frame_ctx_p->byte_code_start_p); - vm_stack_top_p[-1] = VM_CREATE_CONTEXT ((uint32_t) finally_type, branch_offset); + stack_top_p[-1] = VM_CREATE_CONTEXT ((uint32_t) finally_type, branch_offset); byte_code_p += 2 + branch_offset_length; frame_ctx_p->byte_code_p = byte_code_p; - - *vm_stack_top_ref_p = vm_stack_top_p; - return true; + frame_ctx_p->stack_top_p = stack_top_p; + return VM_CONTEXT_FOUND_FINALLY; } +#if ENABLED (JERRY_ESNEXT) + else if (stack_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR) + { + JERRY_ASSERT (context_type == VM_CONTEXT_FOR_OF || context_type == VM_CONTEXT_FOR_AWAIT_OF); + JERRY_ASSERT (finally_type == VM_CONTEXT_FINALLY_THROW || !jcontext_has_pending_exception ()); - vm_stack_top_p = vm_stack_context_abort (frame_ctx_p, vm_stack_top_p); + ecma_value_t exception = ECMA_VALUE_UNDEFINED; + if (finally_type == VM_CONTEXT_FINALLY_THROW) + { + exception = jcontext_take_exception (); + } + + ecma_value_t iterator = stack_top_p[-3]; + ecma_value_t result = ecma_op_get_method_by_magic_id (iterator, LIT_MAGIC_STRING_RETURN); + + if (!ECMA_IS_VALUE_ERROR (result) && !ecma_is_value_undefined (result)) + { + if (!ecma_is_value_object (result) || !ecma_op_is_callable (result)) + { + ecma_free_value (result); + result = ecma_raise_type_error (ECMA_ERR_MSG ("Iterator return() is not callable")); + } + else + { + ecma_object_t *return_obj_p = ecma_get_object_from_value (result); + result = ecma_op_function_call (return_obj_p, iterator, NULL, 0); + ecma_deref_object (return_obj_p); + + if (context_type == VM_CONTEXT_FOR_AWAIT_OF && !ECMA_IS_VALUE_ERROR (result)) + { + ecma_extended_object_t *async_generator_object_p = VM_GET_EXECUTABLE_OBJECT (frame_ctx_p); + + result = ecma_promise_async_await (async_generator_object_p, result); + + if (!ECMA_IS_VALUE_ERROR (result)) + { + uint16_t extra_flags = (ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD + | (ECMA_AWAIT_FOR_CLOSE << ECMA_AWAIT_STATE_SHIFT)); + async_generator_object_p->u.class_prop.extra_info |= extra_flags; + + stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p); + + VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FINALLY_CONTEXT_STACK_ALLOCATION); + stack_top_p += PARSER_FINALLY_CONTEXT_STACK_ALLOCATION; + + stack_top_p[-1] = VM_CREATE_CONTEXT ((uint32_t) finally_type, context_end); + if (finally_type == VM_CONTEXT_FINALLY_THROW) + { + stack_top_p[-2] = exception; + } + + frame_ctx_p->call_operation = VM_EXEC_RETURN; + frame_ctx_p->byte_code_p = vm_stack_resume_executable_object_with_context_end; + frame_ctx_p->stack_top_p = stack_top_p; + return VM_CONTEXT_FOUND_AWAIT; + } + } + + if (!ECMA_IS_VALUE_ERROR (result)) + { + bool is_object = ecma_is_value_object (result); + + ecma_free_value (result); + result = ECMA_VALUE_UNDEFINED; + + if (!is_object) + { + result = ecma_raise_type_error (ECMA_ERR_MSG ("Iterator return() result is not object")); + } + } + } + } + + JERRY_ASSERT (ECMA_IS_VALUE_ERROR (result) || result == ECMA_VALUE_UNDEFINED); + + if (ECMA_IS_VALUE_ERROR (result)) + { + if (finally_type != VM_CONTEXT_FINALLY_THROW) + { + frame_ctx_p->stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p); + return VM_CONTEXT_FOUND_ERROR; + } + + ecma_free_value (jcontext_take_exception ()); + jcontext_raise_exception (exception); + } + else if (finally_type == VM_CONTEXT_FINALLY_THROW) + { + jcontext_raise_exception (exception); + } + } +#endif /* ENABLED (JERRY_ESNEXT) */ + + stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p); } - *vm_stack_top_ref_p = vm_stack_top_p; - return false; + frame_ctx_p->stack_top_p = stack_top_p; + return VM_CONTEXT_FOUND_EXPECTED; } /* vm_stack_find_finally */ #if ENABLED (JERRY_ESNEXT) diff --git a/jerry-core/vm/vm-stack.h b/jerry-core/vm/vm-stack.h index 33b493fd9..49b33ef3c 100644 --- a/jerry-core/vm/vm-stack.h +++ b/jerry-core/vm/vm-stack.h @@ -78,6 +78,19 @@ typedef enum #endif /* ENABLED (JERRY_ESNEXT) */ } vm_stack_context_type_t; +/** + * Return types for vm_stack_find_finally. + */ +typedef enum +{ + VM_CONTEXT_FOUND_FINALLY, /**< found finally */ +#if ENABLED (JERRY_ESNEXT) + VM_CONTEXT_FOUND_ERROR, /**< found an error */ + VM_CONTEXT_FOUND_AWAIT, /**< found an await operation */ +#endif /* ENABLED (JERRY_ESNEXT) */ + VM_CONTEXT_FOUND_EXPECTED, /**< found the type specified in finally_type */ +} vm_stack_found_type; + /** * Checks whether the context type is a finally type. */ @@ -100,8 +113,8 @@ typedef enum #define VM_CONTEXT_GET_NEXT_OFFSET(offsets) (-((int32_t) ((offsets) & ((1 << VM_CONTEXT_OFFSET_SHIFT) - 1)))) ecma_value_t *vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, ecma_value_t *vm_stack_top_p); -bool vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, ecma_value_t **vm_stack_top_ref_p, - vm_stack_context_type_t finally_type, uint32_t search_limit); +vm_stack_found_type vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, ecma_value_t *stack_top_p, + vm_stack_context_type_t finally_type, uint32_t search_limit); uint32_t vm_get_context_value_offsets (ecma_value_t *context_item_p); void vm_ref_lex_env_chain (ecma_object_t *lex_env_p, uint16_t context_depth, ecma_value_t *context_end_p, bool do_ref); diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 176780b9f..52c54d23a 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -3818,6 +3818,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ ecma_value_t *context_top_p = VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth; JERRY_ASSERT (VM_GET_CONTEXT_TYPE (context_top_p[-1]) == VM_CONTEXT_FOR_OF || VM_GET_CONTEXT_TYPE (context_top_p[-1]) == VM_CONTEXT_FOR_AWAIT_OF); + JERRY_ASSERT (context_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR); *stack_top_p++ = context_top_p[-2]; context_top_p[-2] = ECMA_VALUE_UNDEFINED; @@ -3827,7 +3828,9 @@ 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); JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FOR_OF); + JERRY_ASSERT (stack_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR); + stack_top_p[-1] &= (uint32_t) ~VM_CONTEXT_CLOSE_ITERATOR; result = ecma_op_iterator_step (stack_top_p[-3], stack_top_p[-4]); if (ECMA_IS_VALUE_ERROR (result)) @@ -3855,6 +3858,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } JERRY_ASSERT (stack_top_p[-2] == ECMA_VALUE_UNDEFINED); + stack_top_p[-1] |= VM_CONTEXT_CLOSE_ITERATOR; stack_top_p[-2] = next_value; byte_code_p = byte_code_start_p + branch_offset; continue; @@ -3934,7 +3938,9 @@ 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); JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FOR_AWAIT_OF); + JERRY_ASSERT (stack_top_p[-1] & VM_CONTEXT_CLOSE_ITERATOR); + stack_top_p[-1] &= (uint32_t) ~VM_CONTEXT_CLOSE_ITERATOR; result = ecma_op_iterator_next (stack_top_p[-3], stack_top_p[-4], ECMA_VALUE_EMPTY); if (ECMA_IS_VALUE_ERROR (result)) @@ -4056,18 +4062,40 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ uint32_t jump_target = *stack_top_p; - if (vm_stack_find_finally (frame_ctx_p, - &stack_top_p, - VM_CONTEXT_FINALLY_JUMP, - jump_target)) + vm_stack_found_type type = vm_stack_find_finally (frame_ctx_p, + stack_top_p, + VM_CONTEXT_FINALLY_JUMP, + jump_target); + stack_top_p = frame_ctx_p->stack_top_p; + switch (type) { - JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP); - byte_code_p = frame_ctx_p->byte_code_p; - stack_top_p[-2] = jump_target; - } - else - { - byte_code_p = frame_ctx_p->byte_code_start_p + jump_target; + case VM_CONTEXT_FOUND_FINALLY: + { + byte_code_p = frame_ctx_p->byte_code_p; + + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP); + stack_top_p[-2] = jump_target; + break; + } +#if ENABLED (JERRY_ESNEXT) + case VM_CONTEXT_FOUND_ERROR: + { + JERRY_ASSERT (jcontext_has_pending_exception ()); + result = ECMA_VALUE_ERROR; + goto error; + } + case VM_CONTEXT_FOUND_AWAIT: + { + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP); + stack_top_p[-2] = jump_target; + return ECMA_VALUE_UNDEFINED; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + default: + { + byte_code_p = frame_ctx_p->byte_code_start_p + jump_target; + break; + } } JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); @@ -4080,27 +4108,41 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p); - if (vm_stack_find_finally (frame_ctx_p, - &stack_top_p, - VM_CONTEXT_FINALLY_JUMP, - (uint32_t) branch_offset)) + vm_stack_found_type type = vm_stack_find_finally (frame_ctx_p, + stack_top_p, + VM_CONTEXT_FINALLY_JUMP, + (uint32_t) branch_offset); + stack_top_p = frame_ctx_p->stack_top_p; + switch (type) { - JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP); - byte_code_p = frame_ctx_p->byte_code_p; - stack_top_p[-2] = (uint32_t) branch_offset; - } - else - { - byte_code_p = frame_ctx_p->byte_code_start_p + branch_offset; - } + case VM_CONTEXT_FOUND_FINALLY: + { + byte_code_p = frame_ctx_p->byte_code_p; + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP); + stack_top_p[-2] = (uint32_t) branch_offset; + break; + } #if ENABLED (JERRY_ESNEXT) - if (jcontext_has_pending_exception ()) - { - result = ECMA_VALUE_ERROR; - goto error; - } + case VM_CONTEXT_FOUND_ERROR: + { + JERRY_ASSERT (jcontext_has_pending_exception ()); + result = ECMA_VALUE_ERROR; + goto error; + } + case VM_CONTEXT_FOUND_AWAIT: + { + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP); + stack_top_p[-2] = (uint32_t) branch_offset; + return ECMA_VALUE_UNDEFINED; + } #endif /* ENABLED (JERRY_ESNEXT) */ + default: + { + byte_code_p = frame_ctx_p->byte_code_start_p + branch_offset; + break; + } + } JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); continue; @@ -4361,7 +4403,6 @@ error: if (frame_ctx_p->context_depth == 0) { /* In most cases there is no context. */ - ecma_fast_free_value (frame_ctx_p->block_result); frame_ctx_p->call_operation = VM_NO_EXEC_OP; return result; @@ -4369,64 +4410,92 @@ error: if (!ECMA_IS_VALUE_ERROR (result)) { - if (vm_stack_find_finally (frame_ctx_p, - &stack_top_p, - VM_CONTEXT_FINALLY_RETURN, - 0)) + switch (vm_stack_find_finally (frame_ctx_p, + stack_top_p, + VM_CONTEXT_FINALLY_RETURN, + 0)) { - JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_RETURN); - JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); - -#if ENABLED (JERRY_ESNEXT) - if (jcontext_has_pending_exception ()) + case VM_CONTEXT_FOUND_FINALLY: { - stack_top_p[-1] = (ecma_value_t) (stack_top_p[-1] - VM_CONTEXT_FINALLY_RETURN + VM_CONTEXT_FINALLY_THROW); - ecma_free_value (result); - result = jcontext_take_exception (); - } -#endif /* ENABLED (JERRY_ESNEXT) */ + stack_top_p = frame_ctx_p->stack_top_p; + byte_code_p = frame_ctx_p->byte_code_p; - byte_code_p = frame_ctx_p->byte_code_p; - stack_top_p[-2] = result; - continue; - } - -#if ENABLED (JERRY_ESNEXT) - if (jcontext_has_pending_exception ()) - { - ecma_free_value (result); - result = ECMA_VALUE_ERROR; - } -#endif /* ENABLED (JERRY_ESNEXT) */ - } - else if (jcontext_has_pending_exception () && !jcontext_has_pending_abort ()) - { - if (vm_stack_find_finally (frame_ctx_p, - &stack_top_p, - VM_CONTEXT_FINALLY_THROW, - 0)) - { - JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); - JERRY_ASSERT (!(stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV)); - -#if ENABLED (JERRY_DEBUGGER) - JERRY_DEBUGGER_CLEAR_FLAGS (JERRY_DEBUGGER_VM_EXCEPTION_THROWN); -#endif /* ENABLED (JERRY_DEBUGGER) */ - - result = jcontext_take_exception (); - - byte_code_p = frame_ctx_p->byte_code_p; - - if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW) - { + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_RETURN); + JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); stack_top_p[-2] = result; continue; } +#if ENABLED (JERRY_ESNEXT) + case VM_CONTEXT_FOUND_ERROR: + { + JERRY_ASSERT (jcontext_has_pending_exception ()); - JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_CATCH); + ecma_free_value (result); + stack_top_p = frame_ctx_p->stack_top_p; + result = ECMA_VALUE_ERROR; + break; + } + case VM_CONTEXT_FOUND_AWAIT: + { + stack_top_p = frame_ctx_p->stack_top_p; - *stack_top_p++ = result; - continue; + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_RETURN); + stack_top_p[-2] = result; + return ECMA_VALUE_UNDEFINED; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + default: + { + goto finish; + } + } + } + + JERRY_ASSERT (jcontext_has_pending_exception ()); + + if (!jcontext_has_pending_abort ()) + { + switch (vm_stack_find_finally (frame_ctx_p, + stack_top_p, + VM_CONTEXT_FINALLY_THROW, + 0)) + { + case VM_CONTEXT_FOUND_FINALLY: + { + stack_top_p = frame_ctx_p->stack_top_p; + byte_code_p = frame_ctx_p->byte_code_p; + + JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); + JERRY_ASSERT (!(stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV)); + +#if ENABLED (JERRY_DEBUGGER) + JERRY_DEBUGGER_CLEAR_FLAGS (JERRY_DEBUGGER_VM_EXCEPTION_THROWN); +#endif /* ENABLED (JERRY_DEBUGGER) */ + + result = jcontext_take_exception (); + + if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW) + { + stack_top_p[-2] = result; + continue; + } + + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_CATCH); + + *stack_top_p++ = result; + continue; + } +#if ENABLED (JERRY_ESNEXT) + case VM_CONTEXT_FOUND_AWAIT: + { + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (frame_ctx_p->stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW); + return ECMA_VALUE_UNDEFINED; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + default: + { + break; + } } } else @@ -4440,9 +4509,9 @@ error: while (frame_ctx_p->context_depth > 0); } +finish: ecma_free_value (frame_ctx_p->block_result); frame_ctx_p->call_operation = VM_NO_EXEC_OP; - return result; } } /* vm_loop */ diff --git a/tests/jerry/es.next/for-await-of-iterator-close.js b/tests/jerry/es.next/for-await-of-iterator-close.js new file mode 100644 index 000000000..f576da339 --- /dev/null +++ b/tests/jerry/es.next/for-await-of-iterator-close.js @@ -0,0 +1,623 @@ +// 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. + +var successCount = 0; + +// Test 1 + +var asyncIter1 = { + [Symbol.asyncIterator]() { + var idx = 0; + + return { + next() { + assert(++idx === 1) + return Promise.resolve({ value: "Val", done: false }) + } + /* No return() function */ + } + } +} + +async function f1() { + for await (var v of asyncIter1) { + assert(v === "Val") + break + } + successCount++ + +exit: + for await (var v of asyncIter1) { + for await (var v of asyncIter1) { + assert(v === "Val") + break exit + } + assert(false) + } + successCount++ + + try { + for await (var v of asyncIter1) { + assert(v === "Val") + throw 3.75 + } + assert(false) + } catch (e) { + assert(e === 3.75) + successCount++ + } + + try { + for await (var v of asyncIter1) { + assert(v === "Val") + return {} + } + assert(false) + } finally { + successCount++ + } + assert(false) +} + +f1() + +// Test 2 + +var o2 = {} +var returnCount2 = 0 +var asyncIter2 = { + [Symbol.asyncIterator]() { + var idx = 0; + + return { + next() { + assert(++idx === 1) + return Promise.resolve({ value: o2, done: false }) + }, + return(...v) { + assert(v.length === 0) + returnCount2++ + return Promise.resolve({}) + } + } + } +} + +async function f2() { + for await (var v of asyncIter2) { + assert(v === o2) + break + } + successCount++ + +exit: + for await (var v of asyncIter2) { + for await (var v of asyncIter2) { + assert(v === o2) + break exit + } + assert(false) + } + successCount++ + + try { + for await (var v of asyncIter2) { + assert(v === o2) + throw o2 + } + assert(false) + } catch (e) { + assert(e === o2) + successCount++ + } + + try { + for await (var v of asyncIter2) { + assert(v === o2) + return "Ret" + } + assert(false) + } finally { + successCount++ + } + assert(false) + print("OK") +} + +f2() + +// Test 3 + +var asyncIter3 = { + [Symbol.asyncIterator]() { + var idx = 0; + + return { + next() { + assert(++idx === 1) + return Promise.resolve({ value: -4.5, done: false }) + }, + return() { + throw "Error" + } + } + } +} + +async function *f3() { + try { + for await (var v of asyncIter3) { + assert(v === -4.5) + break + } + assert(false) + } catch (e) { + assert(e === "Error") + successCount++ + } + + try { + for await (var v of asyncIter3) { + assert(v === -4.5) + return + } + assert(false) + } catch (e) { + assert(e === "Error") + successCount++ + } + + try { + for await (var v of asyncIter3) { + assert(v === -4.5) + throw "Exit" + } + assert(false) + } catch (e) { + assert(e === "Exit") + successCount++ + } +} + +f3().next() + +// Test 4 + +var o4 = {} +var asyncIter4 = { + [Symbol.asyncIterator]() { + var idx = 0; + + return { + next() { + assert(++idx === 1) + return Promise.resolve({ value: -4.5, done: false }) + }, + get return() { + throw o4 + } + } + } +} + +async function *f4() { + try { + for await (var v of asyncIter4) { + assert(v === -4.5) + break + } + assert(false) + } catch (e) { + assert(e === o4) + successCount++ + } + + try { + for await (var v of asyncIter4) { + assert(v === -4.5) + return + } + assert(false) + } catch (e) { + assert(e === o4) + successCount++ + } + + try { + for await (var v of asyncIter4) { + assert(v === -4.5) + throw 9.25 + } + assert(false) + } catch (e) { + assert(e === 9.25) + successCount++ + } +} + +f4().next() + +// Test 5 + +var asyncIter5 = { + [Symbol.asyncIterator]() { + var idx = 0; + + return { + next() { + assert(++idx === 1) + return Promise.resolve({ value: -4.5, done: false }) + }, + get return() { + return "Not callable" + } + } + } +} + +async function f5() { + try { + for await (var v of asyncIter5) { + assert(v === -4.5) + break + } + assert(false) + } catch (e) { + assert(e instanceof TypeError) + successCount++ + } + + try { + for await (var v of asyncIter5) { + assert(v === -4.5) + return + } + assert(false) + } catch (e) { + assert(e instanceof TypeError) + successCount++ + } + + var o = {} + try { + for await (var v of asyncIter5) { + assert(v === -4.5) + throw o + } + assert(false) + } catch (e) { + assert(e === o) + successCount++ + } +} + +f5() + +// Test 6 + +var asyncIter6 = { + [Symbol.asyncIterator]() { + var idx = 0; + + return { + next() { + assert(++idx === 1) + return Promise.resolve({ value: -4.5, done: false }) + }, + return() { + return Promise.resolve(4.5) + } + } + } +} + +async function f6() { + try { + for await (var v of asyncIter6) { + assert(v === -4.5) + break + } + assert(false) + } catch (e) { + assert(e instanceof TypeError) + successCount++ + } + + try { + for await (var v of asyncIter6) { + assert(v === -4.5) + return + } + assert(false) + } catch (e) { + assert(e instanceof TypeError) + successCount++ + } + + try { + for await (var v of asyncIter6) { + assert(v === -4.5) + throw true + } + assert(false) + } catch (e) { + assert(e === true) + successCount++ + } +} + +f6() + +// Test 7 + +var asyncIter7 = { + [Symbol.asyncIterator]() { + var idx = 0; + + return { + next() { + assert(++idx === 1) + return Promise.resolve({ value: -4.5, done: false }) + }, + return() { + return Promise.reject("Rejected") + } + } + } +} + +async function f7() { + try { + for await (var v of asyncIter7) { + assert(v === -4.5) + break + } + assert(false) + } catch (e) { + assert(e === "Rejected") + successCount++ + } + + try { + for await (var v of asyncIter7) { + assert(v === -4.5) + return + } + assert(false) + } catch (e) { + assert(e === "Rejected") + successCount++ + } + + try { + for await (var v of asyncIter7) { + assert(v === -4.5) + throw true + } + assert(false) + } catch (e) { + assert(e === true) + successCount++ + } +} + +f7() + +// Test 8 + +var asyncIter8 = { + [Symbol.asyncIterator]() { + var idx = 0; + + return { + next() { + assert(++idx === 1) + return Promise.resolve({ value: -4.5, done: false }) + }, + return() { + return {} + } + } + } +} + +async function f8() { + for await (var v of asyncIter8) { + assert(v === -4.5) + break + } + successCount++ + + try { + for await (var v of asyncIter8) { + assert(v === -4.5) + throw null + } + assert(false) + } catch (e) { + assert(e === null) + successCount++ + } + + try { + for await (var v of asyncIter8) { + assert(v === -4.5) + return + } + assert(false) + } finally { + successCount++ + } +} + +f8() + +// Test 9 + +var asyncIter9 = { + [Symbol.asyncIterator]() { + var idx = 0; + + return { + next() { + assert(++idx === 1) + return Promise.resolve({ value: -4.5, done: false }) + }, + return() { + throw "Except" + } + } + } +} + +async function f9() { + try { + for await (var v of asyncIter9) { + assert(v === -4.5) + break + } + assert(false) + } catch (e) { + assert(e === "Except") + successCount++ + } + + try { + for await (var v of asyncIter9) { + assert(v === -4.5) + throw 7.5 + } + assert(false) + } catch (e) { + assert(e === 7.5) + successCount++ + } + + try { + for await (var v of asyncIter9) { + assert(v === -4.5) + for await (var v of asyncIter9) { + assert(v === -4.5) + for await (var v of asyncIter9) { + assert(v === -4.5) + throw "Leave" + } + assert(false) + } + assert(false) + } + assert(false) + } catch (e) { + assert(e === "Leave") + successCount++ + } + + try { + for await (var v of asyncIter9) { + assert(v === -4.5) + return + } + assert(false) + } catch (e) { + assert(e === "Except") + successCount++ + } +} + +f9() + +// Test 10 + +var asyncIter10 = { + [Symbol.asyncIterator]() { + var idx = 0; + + return { + next() { + throw "NoNext" + }, + return() { + assert(false) + }, + throw() { + assert(false) + } + } + } +} + +async function f10() { + try { + try { + for await (var v of asyncIter10) { + assert(false) + } + assert(false) + } finally { + successCount++ + } + } catch (e) { + assert(e === "NoNext") + successCount++ + } +} + +f10() + +// Test 11 + +var asyncIter11 = { + [Symbol.asyncIterator]() { + var idx = 0; + + return { + next() { + if (++idx < 3) + return Promise.resolve({ value: -4.5, done: false }) + throw "NoNext" + }, + return() { + assert(false) + }, + throw() { + assert(false) + } + } + } +} + +async function f11() { + try { + try { + for await (var v of asyncIter11) { + assert(v === -4.5) + } + assert(false) + } finally { + successCount++ + } + } catch (e) { + assert(e === "NoNext") + successCount++ + } +} + +f11() + +// END + +function __checkAsync() { + assert(returnCount2 === 5) + assert(successCount === 34) +}