diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index a203274e0..cd39e7a75 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -466,7 +466,36 @@ ecma_gc_mark_executable_object (ecma_object_t *object_p) /**< object */ register_p++; } - register_p += executable_object_p->frame_ctx.context_depth; + if (executable_object_p->frame_ctx.context_depth > 0) + { + ecma_value_t *context_end_p = register_p; + + register_p += executable_object_p->frame_ctx.context_depth; + + ecma_value_t *context_top_p = register_p; + + do + { + uint32_t offsets = vm_get_context_value_offsets (context_top_p); + + while (VM_CONTEXT_HAS_NEXT_OFFSET (offsets)) + { + int32_t offset = VM_CONTEXT_GET_NEXT_OFFSET (offsets); + + if (ecma_is_value_object (context_top_p[offset])) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (context_top_p[offset])); + } + + offsets >>= VM_CONTEXT_OFFSET_SHIFT; + } + + JERRY_ASSERT (context_top_p >= context_end_p + offsets); + context_top_p -= offsets; + } + while (context_top_p > context_end_p); + } + register_end_p = executable_object_p->frame_ctx.stack_top_p; while (register_p < register_end_p) diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 712495dc5..da535e936 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -1865,18 +1865,50 @@ 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_DO_AWAIT_OR_YIELD = (1u << 2), /**< the executable object performs + * an await or a yield* operation */ ECMA_ASYNC_GENERATOR_CALLED = (1u << 3), /**< the async generator was executed before */ /* This must be the last generator specific flag. */ - ECMA_ASYNC_YIELD_ITERATOR_STATE_SHIFT = 4, /**< shift for async yield iterator state */ + ECMA_AWAIT_STATE_SHIFT = 4, /**< shift for await states */ } ecma_executable_object_flags_t; +/** + * Async function states after an await is completed. + */ +typedef enum +{ + ECMA_AWAIT_YIELD_NEXT, /**< wait for an iterator result object */ + ECMA_AWAIT_YIELD_NEXT_RETURN, /**< wait for an iterator result object after a return operation */ + ECMA_AWAIT_YIELD_RETURN, /**< wait for the argument passed to return operation */ + ECMA_AWAIT_YIELD_NEXT_VALUE, /**< wait for the value property of an iterator result object */ + 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_NEXT, /**< wait for an iterator result object of for-await-of statement */ +} ecma_await_states_t; + /** * Checks whether the executable object is waiting for resuming. */ #define ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED(extra_info) \ (!((extra_info) & (ECMA_EXECUTABLE_OBJECT_COMPLETED | ECMA_EXECUTABLE_OBJECT_RUNNING))) +/** + * Last item of yield* related await states. + */ +#define ECMA_AWAIT_YIELD_END ECMA_AWAIT_YIELD_CLOSE + +/** + * Helper macro for ECMA_EXECUTABLE_OBJECT_RESUME_EXEC. + */ +#define ECMA_EXECUTABLE_OBJECT_RESUME_EXEC_MASK ((uint16_t) ~ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD) + +/** + * Resume execution of the byte code. + */ +#define ECMA_EXECUTABLE_OBJECT_RESUME_EXEC(executable_object_p) \ + ((executable_object_p)->extended_object.u.class_prop.extra_info &= ECMA_EXECUTABLE_OBJECT_RESUME_EXEC_MASK) + /** * Enqueued task of an AsyncGenerator. * 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 6db7e7a5c..6e206c8f1 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c @@ -91,7 +91,7 @@ ecma_builtin_generator_prototype_object_do (vm_executable_object_t *generator_ob while (true) { - if (generator_object_p->extended_object.u.class_prop.extra_info & ECMA_GENERATOR_ITERATE_AND_YIELD) + if (generator_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD) { ecma_value_t iterator = generator_object_p->frame_ctx.block_result; ecma_value_t next_method = generator_object_p->frame_ctx.stack_top_p[-1]; @@ -119,7 +119,7 @@ ecma_builtin_generator_prototype_object_do (vm_executable_object_t *generator_ob return result; } - generator_object_p->extended_object.u.class_prop.extra_info &= (uint16_t) ~ECMA_GENERATOR_ITERATE_AND_YIELD; + ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (generator_object_p); generator_object_p->frame_ctx.block_result = ECMA_VALUE_UNDEFINED; JERRY_ASSERT (generator_object_p->frame_ctx.stack_top_p[-1] == ECMA_VALUE_UNDEFINED @@ -173,7 +173,7 @@ ecma_builtin_generator_prototype_object_do (vm_executable_object_t *generator_ob } ecma_deref_object (ecma_get_object_from_value (iterator)); - generator_object_p->extended_object.u.class_prop.extra_info |= ECMA_GENERATOR_ITERATE_AND_YIELD; + generator_object_p->extended_object.u.class_prop.extra_info |= ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD; generator_object_p->frame_ctx.block_result = iterator; if (generator_object_p->frame_ctx.stack_top_p[0] != ECMA_VALUE_UNDEFINED) diff --git a/jerry-core/ecma/operations/ecma-async-generator-object.c b/jerry-core/ecma/operations/ecma-async-generator-object.c index 87f1ff519..41c517470 100644 --- a/jerry-core/ecma/operations/ecma-async-generator-object.c +++ b/jerry-core/ecma/operations/ecma-async-generator-object.c @@ -27,6 +27,7 @@ #include "jcontext.h" #include "opcodes.h" #include "vm.h" +#include "vm-stack.h" #if ENABLED (JERRY_ESNEXT) @@ -165,7 +166,7 @@ ecma_async_yield_throw (vm_executable_object_t *async_generator_object_p, /**< a return result; } - ECMA_ASYNC_YIELD_ITERATOR_CHANGE_STATE (async_generator_object_p, OPERATION, CLOSE); + ECMA_AWAIT_CHANGE_STATE (async_generator_object_p, YIELD_OPERATION, YIELD_CLOSE); return ECMA_VALUE_UNDEFINED; } @@ -179,7 +180,7 @@ ecma_async_yield_throw (vm_executable_object_t *async_generator_object_p, /**< a return result; } - ECMA_ASYNC_YIELD_ITERATOR_CHANGE_STATE (async_generator_object_p, OPERATION, NEXT); + ECMA_AWAIT_CHANGE_STATE (async_generator_object_p, YIELD_OPERATION, YIELD_NEXT); return ECMA_VALUE_UNDEFINED; } /* ecma_async_yield_throw */ @@ -197,7 +198,7 @@ ecma_async_generator_run (vm_executable_object_t *async_generator_object_p) /**< ecma_async_generator_task_t *task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, head); ecma_value_t result; - if (async_generator_object_p->extended_object.u.class_prop.extra_info & ECMA_GENERATOR_ITERATE_AND_YIELD) + if (async_generator_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD) { switch (task_p->operation_type) { @@ -219,7 +220,7 @@ ecma_async_generator_run (vm_executable_object_t *async_generator_object_p) /**< break; } - ECMA_ASYNC_YIELD_ITERATOR_CHANGE_STATE (async_generator_object_p, OPERATION, NEXT); + ECMA_AWAIT_CHANGE_STATE (async_generator_object_p, YIELD_OPERATION, YIELD_NEXT); break; } case ECMA_ASYNC_GENERATOR_DO_THROW: @@ -239,7 +240,7 @@ ecma_async_generator_run (vm_executable_object_t *async_generator_object_p) /**< break; } - ECMA_ASYNC_YIELD_ITERATOR_CHANGE_STATE (async_generator_object_p, OPERATION, RETURN); + ECMA_AWAIT_CHANGE_STATE (async_generator_object_p, YIELD_OPERATION, YIELD_RETURN); break; } } @@ -254,7 +255,7 @@ ecma_async_generator_run (vm_executable_object_t *async_generator_object_p) /**< JERRY_ASSERT (ECMA_IS_VALUE_ERROR (result)); - ECMA_ASYNC_YIELD_ITERATOR_END (async_generator_object_p); + async_generator_object_p->extended_object.u.class_prop.extra_info &= ECMA_AWAIT_CLEAR_MASK; async_generator_object_p->frame_ctx.block_result = ECMA_VALUE_UNDEFINED; async_generator_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; @@ -346,13 +347,15 @@ ecma_async_generator_finalize (vm_executable_object_t *async_generator_object_p, * @return an updated value for the value argument */ ecma_value_t -ecma_async_yield_continue_await (vm_executable_object_t *async_generator_object_p, /**< async generator */ - ecma_value_t value) /**< job value (takes reference) */ +ecma_await_continue (vm_executable_object_t *executable_object_p, /**< executable object */ + ecma_value_t value) /**< job value (takes reference) */ { - switch (ECMA_ASYNC_YIELD_ITERATOR_GET_STATE (async_generator_object_p)) + ecma_await_states_t state = (ecma_await_states_t) ECMA_AWAIT_GET_STATE (executable_object_p); + + switch (state) { - case ECMA_ASYNC_YIELD_ITERATOR_AWAIT_NEXT: - case ECMA_ASYNC_YIELD_ITERATOR_AWAIT_NEXT_RETURN: + case ECMA_AWAIT_YIELD_NEXT: + case ECMA_AWAIT_YIELD_NEXT_RETURN: { if (!ecma_is_value_object (value)) { @@ -381,23 +384,21 @@ ecma_async_yield_continue_await (vm_executable_object_t *async_generator_object_ if (!done) { - ECMA_ASYNC_YIELD_ITERATOR_SET_STATE (async_generator_object_p, NEXT_VALUE); - return ecma_promise_async_await ((ecma_extended_object_t *) async_generator_object_p, result); + ECMA_AWAIT_SET_STATE (executable_object_p, YIELD_NEXT_VALUE); + return ecma_promise_async_await ((ecma_extended_object_t *) executable_object_p, result); } - if (ECMA_ASYNC_YIELD_ITERATOR_GET_STATE (async_generator_object_p) == ECMA_ASYNC_YIELD_ITERATOR_AWAIT_NEXT) + ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (executable_object_p); + + if (state == ECMA_AWAIT_YIELD_NEXT_RETURN) { - ECMA_ASYNC_YIELD_ITERATOR_END (async_generator_object_p); - return result; + executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_return; } - - ECMA_ASYNC_YIELD_ITERATOR_END (async_generator_object_p); - async_generator_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_return; return result; } - case ECMA_ASYNC_YIELD_ITERATOR_AWAIT_RETURN: + case ECMA_AWAIT_YIELD_RETURN: { - ecma_object_t *obj_p = ecma_get_object_from_value (async_generator_object_p->frame_ctx.block_result); + ecma_object_t *obj_p = ecma_get_object_from_value (executable_object_p->frame_ctx.block_result); ecma_value_t result = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_RETURN); if (ECMA_IS_VALUE_ERROR (result)) @@ -408,13 +409,13 @@ ecma_async_yield_continue_await (vm_executable_object_t *async_generator_object_ if (result == ECMA_VALUE_UNDEFINED) { - ECMA_ASYNC_YIELD_ITERATOR_END (async_generator_object_p); - async_generator_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_return; + ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (executable_object_p); + executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_return; return value; } result = ecma_async_yield_call (result, - async_generator_object_p, + executable_object_p, value, ECMA_ERR_MSG ("Iterator return() is not callable.")); ecma_free_value (value); @@ -425,35 +426,92 @@ ecma_async_yield_continue_await (vm_executable_object_t *async_generator_object_ } JERRY_ASSERT (result == ECMA_VALUE_UNDEFINED); - ECMA_ASYNC_YIELD_ITERATOR_CHANGE_STATE (async_generator_object_p, RETURN, NEXT_RETURN); + ECMA_AWAIT_CHANGE_STATE (executable_object_p, YIELD_RETURN, YIELD_NEXT_RETURN); return ECMA_VALUE_UNDEFINED; } - case ECMA_ASYNC_YIELD_ITERATOR_AWAIT_NEXT_VALUE: + case ECMA_AWAIT_YIELD_NEXT_VALUE: { - ECMA_ASYNC_YIELD_ITERATOR_CHANGE_STATE (async_generator_object_p, NEXT_VALUE, OPERATION); - opfunc_async_generator_yield ((ecma_extended_object_t *) async_generator_object_p, value); + ECMA_AWAIT_CHANGE_STATE (executable_object_p, YIELD_NEXT_VALUE, YIELD_OPERATION); + opfunc_async_generator_yield ((ecma_extended_object_t *) executable_object_p, value); return ECMA_VALUE_UNDEFINED; } - case ECMA_ASYNC_YIELD_ITERATOR_AWAIT_OPERATION: + case ECMA_AWAIT_YIELD_OPERATION: { /* Currently this is always a throw exception case. */ - ecma_value_t result = ecma_async_yield_throw (async_generator_object_p, value); + ecma_value_t result = ecma_async_yield_throw (executable_object_p, value); ecma_free_value (value); return result; } - default: + case ECMA_AWAIT_YIELD_CLOSE: { - JERRY_ASSERT (ECMA_ASYNC_YIELD_ITERATOR_GET_STATE (async_generator_object_p) - == ECMA_ASYNC_YIELD_ITERATOR_AWAIT_CLOSE); - const char *msg_p = (ecma_is_value_object (value) ? ECMA_ERR_MSG ("Iterator throw() is not available.") : ECMA_ERR_MSG ("Value received by yield* is not Object.")); ecma_free_value (value); return ecma_raise_type_error (msg_p); } + 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); + + if (!ecma_is_value_object (value)) + { + ecma_free_value (value); + return ecma_raise_type_error (ECMA_ERR_MSG ("Value received by for-async-of is not Object.")); + } + + ecma_object_t *result_obj_p = ecma_get_object_from_value (value); + ecma_value_t result = ecma_op_object_get_by_magic_id (result_obj_p, LIT_MAGIC_STRING_DONE); + + if (ECMA_IS_VALUE_ERROR (result)) + { + ecma_deref_object (result_obj_p); + return result; + } + + bool done = ecma_op_to_boolean (result); + ecma_free_value (result); + + ecma_value_t *stack_top_p = executable_object_p->frame_ctx.stack_top_p; + JERRY_ASSERT (stack_top_p[-2] == ECMA_VALUE_UNDEFINED); + JERRY_ASSERT (ecma_is_value_object (stack_top_p[-3])); + JERRY_ASSERT (stack_top_p[-4] == ECMA_VALUE_UNDEFINED || ecma_is_value_object (stack_top_p[-4])); + + if (!done) + { + result = ecma_op_object_get_by_magic_id (result_obj_p, LIT_MAGIC_STRING_VALUE); + ecma_deref_object (result_obj_p); + + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + + /* 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[-2] = result; + ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (executable_object_p); + return ECMA_VALUE_EMPTY; + } + + ecma_deref_object (result_obj_p); + + /* This code jumps to the end regardless of the byte code which triggered this await. */ + uint32_t context_end = VM_GET_CONTEXT_END (stack_top_p[-1]); + executable_object_p->frame_ctx.byte_code_p = executable_object_p->frame_ctx.byte_code_start_p + context_end; + + VM_MINUS_EQUAL_U16 (executable_object_p->frame_ctx.context_depth, + PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION); + stack_top_p -= PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION; + executable_object_p->frame_ctx.stack_top_p = stack_top_p; + + ECMA_EXECUTABLE_OBJECT_RESUME_EXEC (executable_object_p); + return ECMA_VALUE_EMPTY; + } } -} /* ecma_async_yield_continue_await */ +} /* ecma_await_continue */ #endif /* ENABLED (JERRY_ESNEXT) */ diff --git a/jerry-core/ecma/operations/ecma-async-generator-object.h b/jerry-core/ecma/operations/ecma-async-generator-object.h index 0f7c7760b..081727c9d 100644 --- a/jerry-core/ecma/operations/ecma-async-generator-object.h +++ b/jerry-core/ecma/operations/ecma-async-generator-object.h @@ -38,67 +38,42 @@ typedef enum ECMA_ASYNC_GENERATOR_DO_RETURN, /**< async generator return operation */ } ecma_async_generator_operation_type_t; -/** - * AsyncGenerator yield iterator states. - */ -typedef enum -{ - ECMA_ASYNC_YIELD_ITERATOR_AWAIT_NEXT, /**< wait for an iterator result object */ - ECMA_ASYNC_YIELD_ITERATOR_AWAIT_NEXT_RETURN, /**< wait for an iterator result object after a return operation */ - ECMA_ASYNC_YIELD_ITERATOR_AWAIT_RETURN, /**< wait for the argument passed to return operation */ - ECMA_ASYNC_YIELD_ITERATOR_AWAIT_NEXT_VALUE, /**< wait for the value property of an iterator result object */ - ECMA_ASYNC_YIELD_ITERATOR_AWAIT_OPERATION, /**< wait for the generator operation (next/throw/return) */ - ECMA_ASYNC_YIELD_ITERATOR_AWAIT_CLOSE, /**< wait for the result of iterator close operation */ -} ecma_async_yield_iterator_states_t; - /** * Get the state of an async yield iterator. */ -#define ECMA_ASYNC_YIELD_ITERATOR_GET_STATE(async_generator_object_p) \ - ((async_generator_object_p)->extended_object.u.class_prop.extra_info >> ECMA_ASYNC_YIELD_ITERATOR_STATE_SHIFT) +#define ECMA_AWAIT_GET_STATE(async_generator_object_p) \ + ((async_generator_object_p)->extended_object.u.class_prop.extra_info >> ECMA_AWAIT_STATE_SHIFT) /** * Set the state of an async yield iterator. */ -#define ECMA_ASYNC_YIELD_ITERATOR_SET_STATE(async_generator_object_p, to) \ +#define ECMA_AWAIT_SET_STATE(async_generator_object_p, to) \ do \ { \ uint16_t extra_info = (async_generator_object_p)->extended_object.u.class_prop.extra_info; \ - extra_info &= ((1 << ECMA_ASYNC_YIELD_ITERATOR_STATE_SHIFT) - 1); \ - extra_info |= (ECMA_ASYNC_YIELD_ITERATOR_AWAIT_ ## to) << ECMA_ASYNC_YIELD_ITERATOR_STATE_SHIFT; \ + extra_info &= ((1 << ECMA_AWAIT_STATE_SHIFT) - 1); \ + extra_info |= (ECMA_AWAIT_ ## to) << ECMA_AWAIT_STATE_SHIFT; \ (async_generator_object_p)->extended_object.u.class_prop.extra_info = extra_info; \ } \ while (false) /** - * Helper value for ECMA_ASYNC_YIELD_ITERATOR_END. + * Mask for clearing all ASYNC_AWAIT status bits */ -#define ECMA_ASYNC_YIELD_ITERATOR_END_MASK \ - (((1 << ECMA_ASYNC_YIELD_ITERATOR_STATE_SHIFT) - 1) - ECMA_GENERATOR_ITERATE_AND_YIELD) +#define ECMA_AWAIT_CLEAR_MASK \ + (((1 << ECMA_AWAIT_STATE_SHIFT) - 1) - ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD) /** - * Return from yield iterator. + * Helper macro for ECMA_AWAIT_CHANGE_STATE. */ -#define ECMA_ASYNC_YIELD_ITERATOR_END(async_generator_object_p) \ - ((async_generator_object_p)->extended_object.u.class_prop.extra_info &= ECMA_ASYNC_YIELD_ITERATOR_END_MASK) - -/** - * Helper macro for ECMA_ASYNC_YIELD_ITERATOR_CHANGE_STATE. - */ -#define ECMA_ASYNC_YIELD_ITERATOR_CS1(from, to) \ - ((ECMA_ASYNC_YIELD_ITERATOR_AWAIT_ ## from) ^ (ECMA_ASYNC_YIELD_ITERATOR_AWAIT_ ## to)) - -/** - * Helper macro for ECMA_ASYNC_YIELD_ITERATOR_CHANGE_STATE. - */ -#define ECMA_ASYNC_YIELD_ITERATOR_CS2(from, to) \ - (ECMA_ASYNC_YIELD_ITERATOR_CS1(from, to) << ECMA_ASYNC_YIELD_ITERATOR_STATE_SHIFT) +#define ECMA_AWAIT_CS_HELPER(from, to) \ + (((ECMA_AWAIT_ ## from) ^ (ECMA_AWAIT_ ## to)) << ECMA_AWAIT_STATE_SHIFT) /** * Change the state of an async yield iterator. */ -#define ECMA_ASYNC_YIELD_ITERATOR_CHANGE_STATE(async_generator_object_p, from, to) \ - ((async_generator_object_p)->extended_object.u.class_prop.extra_info ^= ECMA_ASYNC_YIELD_ITERATOR_CS2 (from, to)) +#define ECMA_AWAIT_CHANGE_STATE(async_generator_object_p, from, to) \ + ((async_generator_object_p)->extended_object.u.class_prop.extra_info ^= ECMA_AWAIT_CS_HELPER (from, to)) ecma_value_t ecma_async_generator_enqueue (vm_executable_object_t *async_generator_object_p, ecma_async_generator_operation_type_t operation, ecma_value_t value); @@ -106,7 +81,7 @@ ecma_value_t ecma_async_generator_enqueue (vm_executable_object_t *async_generat void ecma_async_generator_run (vm_executable_object_t *async_generator_object_p); void ecma_async_generator_finalize (vm_executable_object_t *async_generator_object_p, ecma_value_t value); -ecma_value_t ecma_async_yield_continue_await (vm_executable_object_t *async_generator_object_p, ecma_value_t value); +ecma_value_t ecma_await_continue (vm_executable_object_t *async_generator_object_p, ecma_value_t value); #endif /* ENABLED (JERRY_ESNEXT) */ diff --git a/jerry-core/ecma/operations/ecma-jobqueue.c b/jerry-core/ecma/operations/ecma-jobqueue.c index b17cedbf3..54dd85155 100644 --- a/jerry-core/ecma/operations/ecma-jobqueue.c +++ b/jerry-core/ecma/operations/ecma-jobqueue.c @@ -260,40 +260,43 @@ ecma_process_promise_async_reaction_job (ecma_job_promise_async_reaction_t *job_ if (ecma_job_queue_get_type (&job_p->header) == ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED) { - if (!(executable_object_p->extended_object.u.class_prop.extra_info & ECMA_GENERATOR_ITERATE_AND_YIELD)) + if (!(executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD)) { executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; } - else if (ECMA_ASYNC_YIELD_ITERATOR_GET_STATE (executable_object_p) == ECMA_ASYNC_YIELD_ITERATOR_AWAIT_RETURN) + else if (ECMA_AWAIT_GET_STATE (executable_object_p) == ECMA_AWAIT_YIELD_RETURN) { /* Unlike other operations, return captures rejected promises as well. */ - ECMA_ASYNC_YIELD_ITERATOR_CHANGE_STATE (executable_object_p, RETURN, OPERATION); + ECMA_AWAIT_CHANGE_STATE (executable_object_p, YIELD_RETURN, YIELD_OPERATION); } else { + if (ECMA_AWAIT_GET_STATE (executable_object_p) <= ECMA_AWAIT_YIELD_END) + { + JERRY_ASSERT (ecma_is_value_object (executable_object_p->frame_ctx.block_result)); + executable_object_p->frame_ctx.block_result = ECMA_VALUE_UNDEFINED; + + JERRY_ASSERT (executable_object_p->frame_ctx.stack_top_p[-1] == ECMA_VALUE_UNDEFINED + || ecma_is_value_object (executable_object_p->frame_ctx.stack_top_p[-1])); + executable_object_p->frame_ctx.stack_top_p--; + } + /* Exception: Abort iterators, clear all status. */ - ECMA_ASYNC_YIELD_ITERATOR_END (executable_object_p); - - JERRY_ASSERT (ecma_is_value_object (executable_object_p->frame_ctx.block_result)); - executable_object_p->frame_ctx.block_result = ECMA_VALUE_UNDEFINED; + executable_object_p->extended_object.u.class_prop.extra_info &= ECMA_AWAIT_CLEAR_MASK; executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; - - JERRY_ASSERT (executable_object_p->frame_ctx.stack_top_p[-1] == ECMA_VALUE_UNDEFINED - || ecma_is_value_object (executable_object_p->frame_ctx.stack_top_p[-1])); - executable_object_p->frame_ctx.stack_top_p--; } } - if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_GENERATOR_ITERATE_AND_YIELD) + if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD) { - job_p->argument = ecma_async_yield_continue_await (executable_object_p, job_p->argument); + job_p->argument = ecma_await_continue (executable_object_p, job_p->argument); if (ECMA_IS_VALUE_ERROR (job_p->argument)) { job_p->argument = jcontext_take_exception (); executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; } - else if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_GENERATOR_ITERATE_AND_YIELD) + else if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD) { /* Continue iteration. */ JERRY_ASSERT (job_p->argument == ECMA_VALUE_UNDEFINED); @@ -302,15 +305,18 @@ ecma_process_promise_async_reaction_job (ecma_job_promise_async_reaction_t *job_ return ECMA_VALUE_UNDEFINED; } - /* End of yield*, clear all status. */ - ECMA_ASYNC_YIELD_ITERATOR_END (executable_object_p); + if (ECMA_AWAIT_GET_STATE (executable_object_p) <= ECMA_AWAIT_YIELD_END) + { + JERRY_ASSERT (ecma_is_value_object (executable_object_p->frame_ctx.block_result)); + executable_object_p->frame_ctx.block_result = ECMA_VALUE_UNDEFINED; - JERRY_ASSERT (ecma_is_value_object (executable_object_p->frame_ctx.block_result)); - executable_object_p->frame_ctx.block_result = ECMA_VALUE_UNDEFINED; + JERRY_ASSERT (executable_object_p->frame_ctx.stack_top_p[-1] == ECMA_VALUE_UNDEFINED + || ecma_is_value_object (executable_object_p->frame_ctx.stack_top_p[-1])); + executable_object_p->frame_ctx.stack_top_p--; + } - JERRY_ASSERT (executable_object_p->frame_ctx.stack_top_p[-1] == ECMA_VALUE_UNDEFINED - || ecma_is_value_object (executable_object_p->frame_ctx.stack_top_p[-1])); - executable_object_p->frame_ctx.stack_top_p--; + /* Clear all status. */ + executable_object_p->extended_object.u.class_prop.extra_info &= ECMA_AWAIT_CLEAR_MASK; } ecma_value_t result = opfunc_resume_executable_object (executable_object_p, job_p->argument); diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index ff4893bd2..7e6afac9c 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 (51u) +#define JERRY_SNAPSHOT_VERSION (52u) /** * Flags for jerry_generate_snapshot and jerry_generate_function_snapshot. diff --git a/jerry-core/parser/js/byte-code.c b/jerry-core/parser/js/byte-code.c index ef671057b..7ebe7fab5 100644 --- a/jerry-core/parser/js/byte-code.c +++ b/jerry-core/parser/js/byte-code.c @@ -27,7 +27,7 @@ JERRY_STATIC_ASSERT ((sizeof (cbc_uint16_arguments_t) % sizeof (jmem_cpointer_t) */ JERRY_STATIC_ASSERT (CBC_END == 238, number_of_cbc_opcodes_changed); -JERRY_STATIC_ASSERT (CBC_EXT_END == 121, +JERRY_STATIC_ASSERT (CBC_EXT_END == 127, number_of_cbc_ext_opcodes_changed); #if ENABLED (JERRY_PARSER) diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 4aa943640..4c612b80c 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -196,6 +196,8 @@ #define PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION 4 /* PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION must be <= 4 */ #define PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION 4 +/* PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION must be <= 4 */ +#define PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION 4 /* PARSER_WITH_CONTEXT_STACK_ALLOCATION must be <= 4 */ #define PARSER_WITH_CONTEXT_STACK_ALLOCATION 1 /* PARSER_BLOCK_CONTEXT_STACK_ALLOCATION must be <= 3 */ @@ -536,20 +538,28 @@ -1 + PARSER_WITH_CONTEXT_STACK_ALLOCATION, VM_OC_WITH) \ CBC_OPCODE (CBC_EXT_FOR_IN_GET_NEXT, CBC_NO_FLAG, 1, \ VM_OC_FOR_IN_GET_NEXT | VM_OC_PUT_STACK) \ - CBC_FORWARD_BRANCH (CBC_EXT_FOR_IN_CREATE_CONTEXT, \ - -1 + PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION, VM_OC_FOR_IN_CREATE_CONTEXT) \ + CBC_FORWARD_BRANCH (CBC_EXT_FOR_IN_INIT, \ + -1 + PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION, VM_OC_FOR_IN_INIT) \ CBC_OPCODE (CBC_EXT_SET_GETTER, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ VM_OC_SET_GETTER | VM_OC_NON_STATIC_FLAG | VM_OC_GET_LITERAL_LITERAL) \ CBC_BACKWARD_BRANCH (CBC_EXT_BRANCH_IF_FOR_IN_HAS_NEXT, 0, \ VM_OC_FOR_IN_HAS_NEXT) \ CBC_OPCODE (CBC_EXT_FOR_OF_GET_NEXT, CBC_NO_FLAG, 1, \ VM_OC_FOR_OF_GET_NEXT | VM_OC_PUT_STACK) \ - CBC_FORWARD_BRANCH (CBC_EXT_FOR_OF_CREATE_CONTEXT, \ - -1 + PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION, VM_OC_FOR_OF_CREATE_CONTEXT) \ + CBC_FORWARD_BRANCH (CBC_EXT_FOR_OF_INIT, \ + -1 + PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION, VM_OC_FOR_OF_INIT) \ CBC_OPCODE (CBC_EXT_PUSH_NAMED_FUNC_EXPRESSION, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 1, \ VM_OC_PUSH_NAMED_FUNC_EXPR | VM_OC_GET_LITERAL_LITERAL) \ CBC_BACKWARD_BRANCH (CBC_EXT_BRANCH_IF_FOR_OF_HAS_NEXT, 0, \ VM_OC_FOR_OF_HAS_NEXT) \ + CBC_OPCODE (CBC_EXT_CLONE_CONTEXT, CBC_NO_FLAG, 0, \ + VM_OC_CLONE_CONTEXT) \ + CBC_FORWARD_BRANCH (CBC_EXT_FOR_AWAIT_OF_INIT, \ + -1 + PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION, VM_OC_FOR_AWAIT_OF_INIT) \ + CBC_OPCODE (CBC_EXT_CLONE_FULL_CONTEXT, CBC_NO_FLAG, 0, \ + VM_OC_CLONE_CONTEXT) \ + CBC_BACKWARD_BRANCH (CBC_EXT_BRANCH_IF_FOR_AWAIT_OF_HAS_NEXT, 0, \ + VM_OC_FOR_AWAIT_OF_HAS_NEXT) \ CBC_OPCODE (CBC_EXT_SET_SETTER, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ VM_OC_SET_SETTER | VM_OC_NON_STATIC_FLAG | VM_OC_GET_LITERAL_LITERAL) \ CBC_FORWARD_BRANCH (CBC_EXT_TRY_CREATE_CONTEXT, PARSER_TRY_CONTEXT_STACK_ALLOCATION, \ @@ -588,10 +598,6 @@ VM_OC_STRING_CONCAT | VM_OC_GET_LITERAL_LITERAL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_GET_TAGGED_TEMPLATE_LITERAL, CBC_HAS_BYTE_ARG, 1, \ VM_OC_GET_TEMPLATE_OBJECT | VM_OC_PUT_STACK) \ - CBC_OPCODE (CBC_EXT_CLONE_CONTEXT, CBC_NO_FLAG, 0, \ - VM_OC_CLONE_CONTEXT) \ - CBC_OPCODE (CBC_EXT_CLONE_FULL_CONTEXT, CBC_NO_FLAG, 0, \ - VM_OC_CLONE_CONTEXT) \ CBC_OPCODE (CBC_EXT_LINE, CBC_NO_FLAG, 0, \ VM_OC_LINE) \ CBC_OPCODE (CBC_EXT_ERROR, CBC_NO_FLAG, 0, \ diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 2d3ef8ab0..24bf8a0e9 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -60,6 +60,7 @@ typedef enum PARSER_STATEMENT_FOR_IN, #if ENABLED (JERRY_ESNEXT) PARSER_STATEMENT_FOR_OF, + PARSER_STATEMENT_FOR_AWAIT_OF, #endif /* ENABLED (JERRY_ESNEXT) */ PARSER_STATEMENT_WITH, PARSER_STATEMENT_TRY, @@ -119,6 +120,8 @@ static const uint8_t parser_statement_flags[] = #if ENABLED (JERRY_ESNEXT) /* PARSER_STATEMENT_FOR_OF */ PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM | PARSER_STATM_CONTEXT_BREAK, + /* PARSER_STATEMENT_FOR_AWAIT_OF */ + PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM | PARSER_STATM_CONTEXT_BREAK, #endif /* ENABLED (JERRY_ESNEXT) */ /* PARSER_STATEMENT_WITH */ PARSER_STATM_CONTEXT_BREAK | PARSER_STATM_SINGLE_STATM, @@ -291,6 +294,8 @@ parser_statement_length (uint8_t type) /**< type of statement */ #if ENABLED (JERRY_ESNEXT) /* PARSER_STATEMENT_FOR_OF */ (uint8_t) (sizeof (parser_for_in_of_statement_t) + sizeof (parser_loop_statement_t) + 1), + /* PARSER_STATEMENT_FOR_AWAIT_OF */ + (uint8_t) (sizeof (parser_for_in_of_statement_t) + sizeof (parser_loop_statement_t) + 1), #endif /* ENABLED (JERRY_ESNEXT) */ /* PARSER_STATEMENT_WITH */ (uint8_t) (sizeof (parser_with_statement_t) + 1 + 1), @@ -1198,8 +1203,30 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FOR); lexer_next_token (context_p); +#if ENABLED (JERRY_ESNEXT) + bool is_for_await = false; + + if (context_p->token.type == LEXER_KEYW_AWAIT) + { + if (JERRY_UNLIKELY (context_p->token.lit_location.has_escape)) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); + } + lexer_next_token (context_p); + is_for_await = true; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + if (context_p->token.type != LEXER_LEFT_PAREN) { +#if ENABLED (JERRY_ESNEXT) + if (context_p->token.type == LEXER_LITERAL + && context_p->token.keyword_type == LEXER_KEYW_AWAIT + && !context_p->token.lit_location.has_escape) + { + parser_raise_error (context_p, PARSER_ERR_FOR_AWAIT_NO_ASYNC); + } +#endif /* ENABLED (JERRY_ESNEXT) */ parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); } @@ -1274,6 +1301,16 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ const uint8_t *source_end_p = context_p->source_p - 2; scanner_seek (context_p); + +#if ENABLED (JERRY_ESNEXT) + if (is_for_in && is_for_await) + { + context_p->token.line = context_p->line; + context_p->token.column = context_p->column - 2; + parser_raise_error (context_p, PARSER_ERR_FOR_AWAIT_NO_OF); + } +#endif /* ENABLED (JERRY_ESNEXT) */ + lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); @@ -1288,10 +1325,16 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ : PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ - parser_emit_cbc_ext_forward_branch (context_p, - is_for_in ? CBC_EXT_FOR_IN_CREATE_CONTEXT - : CBC_EXT_FOR_OF_CREATE_CONTEXT, - &for_in_of_statement.branch); + cbc_ext_opcode_t init_opcode = CBC_EXT_FOR_IN_INIT; + +#if ENABLED (JERRY_ESNEXT) + if (!is_for_in) + { + init_opcode = is_for_await ? CBC_EXT_FOR_AWAIT_OF_INIT : CBC_EXT_FOR_OF_INIT; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + + parser_emit_cbc_ext_forward_branch (context_p, init_opcode, &for_in_of_statement.branch); JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); for_in_of_statement.start_offset = context_p->byte_code_size; @@ -1443,12 +1486,17 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ parser_stack_push (context_p, &for_in_of_statement, sizeof (parser_for_in_of_statement_t)); parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t)); + + uint8_t for_type = PARSER_STATEMENT_FOR_IN; + #if ENABLED (JERRY_ESNEXT) - parser_stack_push_uint8 (context_p, is_for_in ? PARSER_STATEMENT_FOR_IN - : PARSER_STATEMENT_FOR_OF); -#else /* !ENABLED (JERRY_ESNEXT) */ - parser_stack_push_uint8 (context_p, PARSER_STATEMENT_FOR_IN); + if (!is_for_in) + { + for_type = is_for_await ? PARSER_STATEMENT_FOR_AWAIT_OF : PARSER_STATEMENT_FOR_OF; + } #endif /* ENABLED (JERRY_ESNEXT) */ + + parser_stack_push_uint8 (context_p, for_type); parser_stack_iterator_init (context_p, &context_p->last_statement); return; } @@ -1515,6 +1563,13 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ } } +#if ENABLED (JERRY_ESNEXT) + if (is_for_await) + { + parser_raise_error (context_p, PARSER_ERR_FOR_AWAIT_NO_OF); + } +#endif /* ENABLED (JERRY_ESNEXT) */ + JERRY_ASSERT (context_p->next_scanner_info_p->source_p != context_p->source_p || context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR); @@ -3286,15 +3341,14 @@ consume_last_statement: case PARSER_STATEMENT_FOR_IN: #if ENABLED (JERRY_ESNEXT) case PARSER_STATEMENT_FOR_OF: + case PARSER_STATEMENT_FOR_AWAIT_OF: #endif /* ENABLED (JERRY_ESNEXT) */ { parser_for_in_of_statement_t for_in_of_statement; parser_loop_statement_t loop; #if ENABLED (JERRY_ESNEXT) - bool is_for_in = (context_p->stack_top_uint8 == PARSER_STATEMENT_FOR_IN); -#else - bool is_for_in = true; + uint8_t for_type = context_p->stack_top_uint8; #endif /* ENABLED (JERRY_ESNEXT) */ parser_stack_pop_uint8 (context_p); @@ -3305,18 +3359,32 @@ consume_last_statement: parser_set_continues_to_current_position (context_p, loop.branch_list_p); parser_flush_cbc (context_p); - PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, is_for_in ? PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION - : PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); + + uint16_t stack_allocation = PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION; +#if ENABLED (JERRY_ESNEXT) + if (for_type != PARSER_STATEMENT_FOR_IN) + { + stack_allocation = (for_type == PARSER_STATEMENT_FOR_OF ? PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION + : PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION); + } +#endif /* ENABLED (JERRY_ESNEXT) */ + + PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, stack_allocation); #ifndef JERRY_NDEBUG - PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, - is_for_in ? PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION - : PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); + PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, stack_allocation); #endif /* !JERRY_NDEBUG */ - parser_emit_cbc_ext_backward_branch (context_p, - is_for_in ? CBC_EXT_BRANCH_IF_FOR_IN_HAS_NEXT - : CBC_EXT_BRANCH_IF_FOR_OF_HAS_NEXT, - for_in_of_statement.start_offset); + cbc_ext_opcode_t opcode = CBC_EXT_BRANCH_IF_FOR_IN_HAS_NEXT; + +#if ENABLED (JERRY_ESNEXT) + if (for_type != PARSER_STATEMENT_FOR_IN) + { + opcode = (for_type == PARSER_STATEMENT_FOR_OF ? CBC_EXT_BRANCH_IF_FOR_OF_HAS_NEXT + : CBC_EXT_BRANCH_IF_FOR_AWAIT_OF_HAS_NEXT); + } +#endif /* ENABLED (JERRY_ESNEXT) */ + + parser_emit_cbc_ext_backward_branch (context_p, opcode, for_in_of_statement.start_offset); parser_set_breaks_to_current_position (context_p, loop.branch_list_p); parser_set_branch_to_current_position (context_p, &for_in_of_statement.branch); diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index 22000ef4d..ba1be0a2d 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -956,6 +956,14 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "for in-of loop variable declaration may not have an initializer."; } + case PARSER_ERR_FOR_AWAIT_NO_ASYNC: + { + return "for-await-of is only allowed inside async functions and generators."; + } + case PARSER_ERR_FOR_AWAIT_NO_OF: + { + return "only 'of' form is allowed for for-await loops."; + } case PARSER_ERR_DUPLICATED_PROTO: { return "Duplicate __proto__ fields are not allowed in object literals."; diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index 9214d33d7..b950700c2 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -82,6 +82,8 @@ typedef enum PARSER_ERR_YIELD_NOT_ALLOWED, /**< yield expression is not allowed */ PARSER_ERR_AWAIT_NOT_ALLOWED, /**< await expression is not allowed */ PARSER_ERR_FOR_IN_OF_DECLARATION, /**< variable declaration in for-in or for-of loop */ + PARSER_ERR_FOR_AWAIT_NO_ASYNC, /**< for-await-of is only allowed inside async functions */ + PARSER_ERR_FOR_AWAIT_NO_OF, /**< only 'of' form is allowed for for-await loops */ PARSER_ERR_DUPLICATED_PROTO, /**< duplicated __proto__ fields are not allowed */ #endif /* ENABLED (JERRY_ESNEXT) */ PARSER_ERR_DELETE_IDENT_NOT_ALLOWED, /**< identifier delete is not allowed in strict mode */ diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 50c0d8ba6..aed97c0f5 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -1181,6 +1181,14 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ case LEXER_KEYW_FOR: { lexer_next_token (context_p); + +#if ENABLED (JERRY_ESNEXT) + if (context_p->token.type == LEXER_KEYW_AWAIT) + { + lexer_next_token (context_p); + } +#endif /* ENABLED (JERRY_ESNEXT) */ + if (context_p->token.type != LEXER_LEFT_PAREN) { scanner_raise_error (context_p); diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index f4ae4dad0..7297b9c91 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -730,6 +730,12 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /* ecma_value_t *stack_top_p = executable_object_p->frame_ctx.stack_top_p; + if (value != ECMA_VALUE_EMPTY) + { + *stack_top_p = value; + executable_object_p->frame_ctx.stack_top_p = stack_top_p + 1; + } + if (executable_object_p->frame_ctx.context_depth > 0) { while (register_p < register_end_p) @@ -752,9 +758,6 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /* ecma_ref_if_object (executable_object_p->frame_ctx.block_result); - *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)); executable_object_p->extended_object.u.class_prop.extra_info |= ECMA_EXECUTABLE_OBJECT_RUNNING; @@ -840,6 +843,54 @@ opfunc_async_generator_yield (ecma_extended_object_t *async_generator_object_p, } } /* opfunc_async_generator_yield */ +/** + * Creates a new executable object and awaits for the value + * + * Note: + * extra_flags can be used to set additional extra_info flags + * + * @return a new Promise object on success, error otherwise + */ +ecma_value_t +opfunc_async_create_and_await (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ + ecma_value_t value, /**< awaited value (takes reference) */ + uint16_t extra_flags) /**< extra flags */ +{ + JERRY_ASSERT (frame_ctx_p->block_result == ECMA_VALUE_UNDEFINED); + /* TODO: An CBC_FUNCTION_ASYNC_ARROW should be defined. */ + JERRY_ASSERT (CBC_FUNCTION_GET_TYPE (frame_ctx_p->bytecode_header_p->status_flags) == CBC_FUNCTION_ASYNC + || CBC_FUNCTION_GET_TYPE (frame_ctx_p->bytecode_header_p->status_flags) == CBC_FUNCTION_ARROW); + + ecma_object_t *promise_p = ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE); + ecma_value_t result = ecma_promise_reject_or_resolve (ecma_make_object_value (promise_p), value, true); + ecma_free_value (value); + + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + + vm_executable_object_t *executable_object_p; + executable_object_p = opfunc_create_executable_object (frame_ctx_p, VM_CREATE_EXECUTABLE_OBJECT_ASYNC); + + executable_object_p->extended_object.u.class_prop.extra_info |= extra_flags; + + ecma_promise_async_then (result, ecma_make_object_value ((ecma_object_t *) executable_object_p)); + ecma_deref_object ((ecma_object_t *) executable_object_p); + ecma_free_value (result); + + ecma_object_t *old_new_target_p = JERRY_CONTEXT (current_new_target); + JERRY_CONTEXT (current_new_target) = promise_p; + + result = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_PROMISE_EXECUTOR_EMPTY); + + JERRY_ASSERT (ecma_is_value_object (result)); + executable_object_p->frame_ctx.block_result = result; + + JERRY_CONTEXT (current_new_target) = old_new_target_p; + return result; +} /* opfunc_async_create_and_await */ + /** * Implicit class constructor handler when the classHeritage is not present. * diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 62ec4e71d..876824e60 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -138,6 +138,9 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, ec void opfunc_async_generator_yield (ecma_extended_object_t *async_generator_object_p, ecma_value_t value); +ecma_value_t +opfunc_async_create_and_await (vm_frame_ctx_t *frame_ctx_p, ecma_value_t value, uint16_t extra_flags); + ecma_value_t opfunc_create_implicit_class_constructor (uint8_t opcode); diff --git a/jerry-core/vm/vm-stack.c b/jerry-core/vm/vm-stack.c index 4f37d7cfd..d45dd20ad 100644 --- a/jerry-core/vm/vm-stack.c +++ b/jerry-core/vm/vm-stack.c @@ -28,10 +28,13 @@ */ JERRY_STATIC_ASSERT (PARSER_WITH_CONTEXT_STACK_ALLOCATION == PARSER_BLOCK_CONTEXT_STACK_ALLOCATION, - parser_with_context_stack_allocation_must_be_equal_to_parser_block_context_stack_allocation); + with_context_stack_allocation_must_be_equal_to_block_context_stack_allocation); JERRY_STATIC_ASSERT (PARSER_WITH_CONTEXT_STACK_ALLOCATION == PARSER_TRY_CONTEXT_STACK_ALLOCATION, - parser_with_context_stack_allocation_must_be_equal_to_parser_block_context_stack_allocation); + with_context_stack_allocation_must_be_equal_to_block_context_stack_allocation); + +JERRY_STATIC_ASSERT (PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION == PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION, + for_of_context_stack_allocation_must_be_equal_to_for_await_of_context_stack_allocation); /** * Abort (finalize) the current stack context, and remove it. @@ -79,6 +82,7 @@ vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ } #if ENABLED (JERRY_ESNEXT) 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]); @@ -313,7 +317,7 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ #if ENABLED (JERRY_ESNEXT) /** - * Get the offsets of ecma values from the specified item of a context. + * Get the offsets of ecma values corresponding to the passed context. * * @return array of offsets, last item represents the size of the context item */ @@ -325,7 +329,7 @@ vm_get_context_value_offsets (ecma_value_t *context_item_p) /**< any item of a c case VM_CONTEXT_FINALLY_THROW: case VM_CONTEXT_FINALLY_RETURN: { - return (2 << (VM_CONTEXT_OFFSET_SHIFT)) | PARSER_FINALLY_CONTEXT_STACK_ALLOCATION; + return (PARSER_FINALLY_CONTEXT_STACK_ALLOCATION << VM_CONTEXT_OFFSET_SHIFT) | 2; } case VM_CONTEXT_FINALLY_JUMP: { @@ -338,16 +342,19 @@ vm_get_context_value_offsets (ecma_value_t *context_item_p) /**< any item of a c { return PARSER_WITH_CONTEXT_STACK_ALLOCATION; } - case VM_CONTEXT_FOR_OF: + case VM_CONTEXT_FOR_IN: { - return ((4 << (VM_CONTEXT_OFFSET_SHIFT * 3)) - | (3 << (VM_CONTEXT_OFFSET_SHIFT * 2)) - | (2 << (VM_CONTEXT_OFFSET_SHIFT)) - | PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); + return (PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION << VM_CONTEXT_OFFSET_SHIFT) | 4; } default: { - return (4 << (VM_CONTEXT_OFFSET_SHIFT)) | PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION; + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (context_item_p[-1]) == VM_CONTEXT_FOR_OF + || VM_GET_CONTEXT_TYPE (context_item_p[-1]) == VM_CONTEXT_FOR_AWAIT_OF); + + return ((PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION << (VM_CONTEXT_OFFSET_SHIFT * 3)) + | (4 << (VM_CONTEXT_OFFSET_SHIFT * 2)) + | (3 << VM_CONTEXT_OFFSET_SHIFT) + | 2); } } } /* vm_get_context_value_offsets */ diff --git a/jerry-core/vm/vm-stack.h b/jerry-core/vm/vm-stack.h index a675966e8..33b493fd9 100644 --- a/jerry-core/vm/vm-stack.h +++ b/jerry-core/vm/vm-stack.h @@ -74,6 +74,7 @@ typedef enum VM_CONTEXT_FOR_IN, /**< for-in context */ #if ENABLED (JERRY_ESNEXT) VM_CONTEXT_FOR_OF, /**< for-of context */ + VM_CONTEXT_FOR_AWAIT_OF, /**< for-await-of context */ #endif /* ENABLED (JERRY_ESNEXT) */ } vm_stack_context_type_t; @@ -94,7 +95,7 @@ typedef enum #define VM_CONTEXT_HAS_NEXT_OFFSET(offsets) ((offsets) >= (1 << VM_CONTEXT_OFFSET_SHIFT)) /** - * Gets the next offset from the offset array. + * Get the next offset from the offset array. */ #define VM_CONTEXT_GET_NEXT_OFFSET(offsets) (-((int32_t) ((offsets) & ((1 << VM_CONTEXT_OFFSET_SHIFT) - 1)))) diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 7ddd70dd1..176780b9f 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -2210,7 +2210,8 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { ecma_extended_object_t *async_generator_object_p = VM_GET_EXECUTABLE_OBJECT (frame_ctx_p); - JERRY_ASSERT (!(async_generator_object_p->u.class_prop.extra_info & ECMA_GENERATOR_ITERATE_AND_YIELD)); + JERRY_ASSERT (!(async_generator_object_p->u.class_prop.extra_info + & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD)); /* Byte code is executed at the first time. */ left_value = stack_top_p[-1]; @@ -2237,7 +2238,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ goto error; } - async_generator_object_p->u.class_prop.extra_info |= ECMA_GENERATOR_ITERATE_AND_YIELD; + async_generator_object_p->u.class_prop.extra_info |= ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD; frame_ctx_p->block_result = left_value; frame_ctx_p->call_operation = VM_EXEC_RETURN; @@ -2247,50 +2248,21 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } case VM_OC_AWAIT: { - ecma_value_t promise = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE)); - ecma_value_t argument = *(--stack_top_p); - - result = ecma_promise_reject_or_resolve (promise, argument, true); - ecma_free_value (argument); - - if (ECMA_IS_VALUE_ERROR (result)) + if (JERRY_UNLIKELY (frame_ctx_p->block_result == ECMA_VALUE_UNDEFINED)) { - goto error; + frame_ctx_p->call_operation = VM_EXEC_RETURN; + frame_ctx_p->byte_code_p = byte_code_p; + frame_ctx_p->stack_top_p = --stack_top_p; + + result = opfunc_async_create_and_await (frame_ctx_p, *stack_top_p, 0); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + return result; } - - frame_ctx_p->call_operation = VM_EXEC_RETURN; - frame_ctx_p->byte_code_p = byte_code_p; - frame_ctx_p->stack_top_p = stack_top_p; - - if (frame_ctx_p->block_result == ECMA_VALUE_UNDEFINED) - { - vm_executable_object_t *executable_object_p; - executable_object_p = opfunc_create_executable_object (frame_ctx_p, VM_CREATE_EXECUTABLE_OBJECT_ASYNC); - - ecma_promise_async_then (result, ecma_make_object_value ((ecma_object_t *) executable_object_p)); - ecma_deref_object ((ecma_object_t *) executable_object_p); - ecma_free_value (result); - - ecma_object_t *old_new_target_p = JERRY_CONTEXT (current_new_target); - JERRY_CONTEXT (current_new_target) = ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE); - - result = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_PROMISE_EXECUTOR_EMPTY); - - JERRY_ASSERT (ecma_is_value_object (result)); - executable_object_p->frame_ctx.block_result = result; - - JERRY_CONTEXT (current_new_target) = old_new_target_p; - } - else - { - ecma_object_t *object_p = (ecma_object_t *) VM_GET_EXECUTABLE_OBJECT (frame_ctx_p); - ecma_promise_async_then (result, ecma_make_object_value (object_p)); - - ecma_free_value (result); - result = ECMA_VALUE_UNDEFINED; - } - - return result; + /* FALLTHRU */ } case VM_OC_GENERATOR_AWAIT: { @@ -3668,7 +3640,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ frame_ctx_p->lex_env_p = with_env_p; continue; } - case VM_OC_FOR_IN_CREATE_CONTEXT: + case VM_OC_FOR_IN_INIT: { ecma_value_t value = *(--stack_top_p); @@ -3780,7 +3752,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ continue; } #if ENABLED (JERRY_ESNEXT) - case VM_OC_FOR_OF_CREATE_CONTEXT: + case VM_OC_FOR_OF_INIT: { ecma_value_t value = *(--stack_top_p); @@ -3844,7 +3816,8 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ case VM_OC_FOR_OF_GET_NEXT: { 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); + 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); *stack_top_p++ = context_top_p[-2]; context_top_p[-2] = ECMA_VALUE_UNDEFINED; @@ -3853,6 +3826,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ case VM_OC_FOR_OF_HAS_NEXT: { 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); result = ecma_op_iterator_step (stack_top_p[-3], stack_top_p[-4]); @@ -3885,6 +3859,106 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ byte_code_p = byte_code_start_p + branch_offset; continue; } + case VM_OC_FOR_AWAIT_OF_INIT: + { + ecma_value_t value = *(--stack_top_p); + + JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); + + ecma_value_t next_method; + result = ecma_op_get_iterator (value, ECMA_VALUE_ASYNC_ITERATOR, &next_method); + + ecma_free_value (value); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + ecma_value_t iterator = result; + result = ecma_op_iterator_next (result, next_method, ECMA_VALUE_EMPTY); + + if (ECMA_IS_VALUE_ERROR (result)) + { + ecma_free_value (iterator); + ecma_free_value (next_method); + goto error; + } + + branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p); + + VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION); + stack_top_p += PARSER_FOR_AWAIT_OF_CONTEXT_STACK_ALLOCATION; + stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_FOR_AWAIT_OF, branch_offset); + stack_top_p[-2] = ECMA_VALUE_UNDEFINED; + stack_top_p[-3] = iterator; + stack_top_p[-4] = next_method; + + if (byte_code_p[0] == CBC_EXT_OPCODE && byte_code_p[1] == CBC_EXT_CLONE_CONTEXT) + { + /* No need to duplicate the first context. */ + byte_code_p += 2; + } + + frame_ctx_p->call_operation = VM_EXEC_RETURN; + frame_ctx_p->byte_code_p = byte_code_p; + frame_ctx_p->stack_top_p = stack_top_p; + + uint16_t extra_flags = (ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD + | (ECMA_AWAIT_FOR_NEXT << ECMA_AWAIT_STATE_SHIFT)); + + if (CBC_FUNCTION_GET_TYPE (frame_ctx_p->bytecode_header_p->status_flags) == CBC_FUNCTION_ASYNC_GENERATOR + || frame_ctx_p->block_result != ECMA_VALUE_UNDEFINED) + { + ecma_extended_object_t *executable_object_p = VM_GET_EXECUTABLE_OBJECT (frame_ctx_p); + result = ecma_promise_async_await (executable_object_p, result); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + executable_object_p->u.class_prop.extra_info |= extra_flags; + return ECMA_VALUE_UNDEFINED; + } + + result = opfunc_async_create_and_await (frame_ctx_p, result, extra_flags); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + return result; + } + case VM_OC_FOR_AWAIT_OF_HAS_NEXT: + { + 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); + + result = ecma_op_iterator_next (stack_top_p[-3], stack_top_p[-4], ECMA_VALUE_EMPTY); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + ecma_extended_object_t *executable_object_p = VM_GET_EXECUTABLE_OBJECT (frame_ctx_p); + result = ecma_promise_async_await (executable_object_p, result); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + uint16_t extra_flags = (ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD + | (ECMA_AWAIT_FOR_NEXT << ECMA_AWAIT_STATE_SHIFT)); + executable_object_p->u.class_prop.extra_info |= extra_flags; + + frame_ctx_p->call_operation = VM_EXEC_RETURN; + frame_ctx_p->byte_code_p = byte_code_start_p + branch_offset; + frame_ctx_p->stack_top_p = stack_top_p; + return ECMA_VALUE_UNDEFINED; + } #endif /* ENABLED (JERRY_ESNEXT) */ case VM_OC_TRY: { diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 663d3405c..df274a121 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -206,7 +206,7 @@ typedef enum VM_OC_BLOCK_CREATE_CONTEXT, /**< create lexical environment for blocks enclosed in braces */ VM_OC_WITH, /**< with */ - VM_OC_FOR_IN_CREATE_CONTEXT, /**< for in create context */ + VM_OC_FOR_IN_INIT, /**< for-in init context */ VM_OC_FOR_IN_GET_NEXT, /**< get next */ VM_OC_FOR_IN_HAS_NEXT, /**< has next */ @@ -243,9 +243,11 @@ typedef enum VM_OC_CLONE_CONTEXT, /**< clone lexical environment with let/const declarations */ VM_OC_SET_COMPUTED_PROPERTY, /**< set computed property */ - VM_OC_FOR_OF_CREATE_CONTEXT, /**< for of create context */ - VM_OC_FOR_OF_GET_NEXT, /**< get next */ - VM_OC_FOR_OF_HAS_NEXT, /**< has next */ + VM_OC_FOR_OF_INIT, /**< for-of init context */ + VM_OC_FOR_OF_GET_NEXT, /**< for-of get next */ + VM_OC_FOR_OF_HAS_NEXT, /**< for-of has next */ + VM_OC_FOR_AWAIT_OF_INIT, /**< for-await-of init context */ + VM_OC_FOR_AWAIT_OF_HAS_NEXT, /**< for-await-of has next */ VM_OC_LOCAL_EVAL, /**< eval in local context */ VM_OC_SUPER_CALL, /**< call the 'super' constructor */ @@ -312,9 +314,11 @@ typedef enum VM_OC_CLONE_CONTEXT = VM_OC_NONE, /**< clone lexical environment with let/const declarations */ VM_OC_SET_COMPUTED_PROPERTY = VM_OC_NONE, /**< set computed property is unused */ - VM_OC_FOR_OF_CREATE_CONTEXT = VM_OC_NONE, /**< for of create context */ - VM_OC_FOR_OF_GET_NEXT = VM_OC_NONE, /**< get next */ - VM_OC_FOR_OF_HAS_NEXT = VM_OC_NONE, /**< has next */ + VM_OC_FOR_OF_INIT = VM_OC_NONE, /**< for-of init context */ + VM_OC_FOR_OF_GET_NEXT = VM_OC_NONE, /**< for-of get next */ + VM_OC_FOR_OF_HAS_NEXT = VM_OC_NONE, /**< for-of has next */ + VM_OC_FOR_AWAIT_OF_INIT = VM_OC_NONE, /**< for-await-of init context */ + VM_OC_FOR_AWAIT_OF_HAS_NEXT = VM_OC_NONE, /**< for-await-of has next */ VM_OC_LOCAL_EVAL = VM_OC_NONE, /**< eval in local context */ VM_OC_SUPER_CALL = VM_OC_NONE, /**< call the 'super' constructor */ diff --git a/tests/jerry/es.next/for-await-of.js b/tests/jerry/es.next/for-await-of.js new file mode 100644 index 000000000..e042a9083 --- /dev/null +++ b/tests/jerry/es.next/for-await-of.js @@ -0,0 +1,198 @@ +// 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_syntax_error (code) +{ + try { + eval (code); + assert (false); + } catch (e) { + assert (e instanceof SyntaxError); + } +} + +check_syntax_error ("for await (a of b)"); +check_syntax_error ("async function f() { for await (a in b) ; }"); +check_syntax_error ("async function f() { for await ( ; ; ) ; }"); +check_syntax_error ("async function f() { for await (let a = 0; a < 4; a++) ; }"); + +var successCount = 0; + +// Test 1 + +var promise1 = Promise.resolve("Resolved"); + +var asyncIter1 = { + [Symbol.asyncIterator]() { + var idx = 0; + + function next() { + idx++; + if (idx == 1) { + return { value:"Val", done: false } + } else if (idx == 2) { + return { value:promise1, done: false } + } else if (idx == 3) { + return { value:4.5, done: false } + } + return { value:promise1, done: true } + } + + successCount++ + return { next } + } +} + +function checkAsyncIter1(v, idx) +{ + if (idx === 1) { + assert(v === "Val") + } else if (idx === 2) { + assert(v === promise1) + } else if (idx === 3) { + assert(v === 4.5) + } else { + assert(false) + } +} + +async function f1a() { + var idx = 0; + for await (var v of asyncIter1) { + checkAsyncIter1(v, ++idx); + successCount++; + } + successCount++; +} + +f1a() + +async function f1b() { + await promise1 + + var idx = 0; + for await (var v of asyncIter1) { + checkAsyncIter1(v, ++idx); + successCount++; + } + successCount++; +} + +f1b() + +async function *f1c() { + var idx = 0; + for await (var v of asyncIter1) { + checkAsyncIter1(v, ++idx); + successCount++; + } + successCount++; +} + +f1c().next() + +async function *f1d() { + await promise1 + + var idx = 0; + for await (var v of asyncIter1) { + checkAsyncIter1(v, ++idx); + successCount++; + } + successCount++; +} + +f1d().next() + +// Test 2 + +var state2 = 0 +var promise2 = Promise.reject("Rejected"); + +var asyncIter2 = { + [Symbol.asyncIterator]() { + var idx = 0; + assert(++state2 === 1) + + function next() { + idx++; + if (idx == 1) { + assert(++state2 === 2) + return { value:"Str", done: false } + } else if (idx == 2) { + assert(++state2 === 4) + return { value:promise2, done: false } + } else if (idx == 3) { + assert(++state2 === 6) + return { value:-3.5, done: false } + } + assert(++state2 === 8) + return { value:promise2, done: true } + } + + successCount++ + return { next } + } +} + +function checkAsyncIter2(v, idx) +{ + if (idx === 1) { + assert(v === "Str") + } else if (idx === 2) { + assert(v === promise2) + } else if (idx === 3) { + assert(v === -3.5) + } else { + assert(false) + } +} + +async function *f2a() { + var idx = 0; + for await (var v of asyncIter2) { + checkAsyncIter2(v, ++idx) + yield v + } + successCount++; +} + +async function f2b() { + var idx = 0; + var g = f2a(); + var v; + + while (true) { + v = await g.next() + + if (v.done) { + break; + } + + checkAsyncIter2(v.value, ++idx) + ++state2 + assert(state2 === 3 || state2 === 5 || state2 === 7) + } + + successCount++; +} + +f2b(); + +// END + +function __checkAsync() { + assert(state2 === 8) + assert(successCount === 23) +}