diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 68eddd9825..ecc49ed7f6 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -463,6 +463,11 @@ ecma_gc_mark_executable_object (ecma_object_t *object_p) /**< object */ register_p++; } + + if (ecma_is_value_object (executable_object_p->frame_ctx.block_result)) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (executable_object_p->frame_ctx.block_result)); + } } /* ecma_gc_mark_executable_object */ #endif /* ENABLED (JERRY_ESNEXT) */ 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 07876a99b1..973c9713fc 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c @@ -50,14 +50,6 @@ static const uint8_t ecma_builtin_generator_prototype_return[2] = CBC_EXT_OPCODE, CBC_EXT_RETURN }; -/** - * Byte code sequence which throws an exception. - */ -static const uint8_t ecma_builtin_generator_prototype_throw[1] = -{ - CBC_THROW -}; - /** * Helper function for next / return / throw * @@ -148,7 +140,7 @@ ecma_builtin_generator_prototype_object_do (ecma_value_t this_arg, /**< this arg } else if (resume_mode == ECMA_ITERATOR_THROW) { - executable_object_p->frame_ctx.byte_code_p = ecma_builtin_generator_prototype_throw; + executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; } ecma_value_t value = opfunc_resume_executable_object (executable_object_p, arg); diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index f0dc3663b9..b9bf27f629 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -1289,6 +1289,11 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ message_p = ECMA_ERR_MSG ("Generator functions cannot be invoked with 'new'."); break; } + case CBC_FUNCTION_ASYNC: + { + message_p = ECMA_ERR_MSG ("Async functions cannot be invoked with 'new'."); + break; + } case CBC_FUNCTION_ARROW: { message_p = ECMA_ERR_MSG ("Arrow functions cannot be invoked with 'new'."); diff --git a/jerry-core/ecma/operations/ecma-jobqueue.c b/jerry-core/ecma/operations/ecma-jobqueue.c index 134c6dc35c..c7fb2fe4db 100644 --- a/jerry-core/ecma/operations/ecma-jobqueue.c +++ b/jerry-core/ecma/operations/ecma-jobqueue.c @@ -20,6 +20,7 @@ #include "ecma-objects.h" #include "ecma-promise-object.h" #include "jcontext.h" +#include "opcodes.h" #if ENABLED (JERRY_BUILTIN_PROMISE) @@ -46,6 +47,16 @@ typedef struct ecma_value_t argument; /**< argument for the reaction */ } ecma_job_promise_reaction_t; +/** + * Description of the PromiseAsyncReactionJob + */ +typedef struct +{ + ecma_job_queue_item_t header; /**< job queue item header */ + ecma_value_t executable_object; /**< executable object */ + ecma_value_t argument; /**< argument for the reaction */ +} ecma_job_promise_async_reaction_t; + /** * Description of the PromiseResolveThenableJob */ @@ -103,6 +114,21 @@ ecma_free_promise_reaction_job (ecma_job_promise_reaction_t *job_p) /**< points jmem_heap_free_block (job_p, sizeof (ecma_job_promise_reaction_t)); } /* ecma_free_promise_reaction_job */ +/** + * Free the heap and the member of the PromiseAsyncReactionJob. + */ +static void +ecma_free_promise_async_reaction_job (ecma_job_promise_async_reaction_t *job_p) /**< points to the + * PromiseAsyncReactionJob */ +{ + JERRY_ASSERT (job_p != NULL); + + ecma_free_value (job_p->executable_object); + ecma_free_value (job_p->argument); + + jmem_heap_free_block (job_p, sizeof (ecma_job_promise_async_reaction_t)); +} /* ecma_free_promise_async_reaction_job */ + /** * Free the heap and the member of the PromiseResolveThenableJob. */ @@ -196,6 +222,31 @@ ecma_process_promise_reaction_job (ecma_job_promise_reaction_t *job_p) /**< the return status; } /* ecma_process_promise_reaction_job */ +/** + * The processor for PromiseAsyncReactionJob. + * + * @return ecma value + * Returned value must be freed with ecma_free_value + */ +static ecma_value_t +ecma_process_promise_async_reaction_job (ecma_job_promise_async_reaction_t *job_p) /**< the job to be operated */ +{ + ecma_object_t *object_p = ecma_get_object_from_value (job_p->executable_object); + vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p; + + if (ecma_job_queue_get_type (&job_p->header) == ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED) + { + executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; + } + + ecma_value_t result = opfunc_resume_executable_object (executable_object_p, job_p->argument); + /* Argument reference is taken by opfunc_resume_executable_object. */ + job_p->argument = ECMA_VALUE_UNDEFINED; + ecma_free_promise_async_reaction_job (job_p); + + return result; +} /* ecma_process_promise_async_reaction_job */ + /** * Process the PromiseResolveThenableJob. * @@ -273,7 +324,7 @@ ecma_enqueue_job (ecma_job_queue_item_t *job_p) /**< the job */ } /* ecma_enqueue_job */ /** - * Enqueue a PromiseReactionJob into the jobqueue. + * Enqueue a PromiseReactionJob into the job queue. */ void ecma_enqueue_promise_reaction_job (ecma_value_t capability, /**< capability object */ @@ -291,7 +342,25 @@ ecma_enqueue_promise_reaction_job (ecma_value_t capability, /**< capability obje } /* ecma_enqueue_promise_reaction_job */ /** - * Enqueue a PromiseResolveThenableJob into the jobqueue. + * Enqueue a PromiseAsyncReactionJob into the job queue. + */ +void +ecma_enqueue_promise_async_reaction_job (ecma_value_t executable_object, /**< executable object */ + ecma_value_t argument, /**< argument */ + bool is_rejected) /**< is_fulfilled */ +{ + ecma_job_promise_async_reaction_t *job_p; + job_p = (ecma_job_promise_async_reaction_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_async_reaction_t)); + job_p->header.next_and_type = (is_rejected ? ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED + : ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED); + job_p->executable_object = ecma_copy_value (executable_object); + job_p->argument = ecma_copy_value (argument); + + ecma_enqueue_job (&job_p->header); +} /* ecma_enqueue_promise_async_reaction_job */ + +/** + * Enqueue a PromiseResolveThenableJob into the job queue. */ void ecma_enqueue_promise_resolve_thenable_job (ecma_value_t promise, /**< promise to be resolved */ @@ -338,6 +407,12 @@ ecma_process_all_enqueued_jobs (void) ret = ecma_process_promise_reaction_job ((ecma_job_promise_reaction_t *) job_p); break; } + case ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED: + case ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED: + { + ret = ecma_process_promise_async_reaction_job ((ecma_job_promise_async_reaction_t *) job_p); + break; + } default: { JERRY_ASSERT (ecma_job_queue_get_type (job_p) == ECMA_JOB_PROMISE_THENABLE); @@ -369,6 +444,12 @@ ecma_free_all_enqueued_jobs (void) ecma_free_promise_reaction_job ((ecma_job_promise_reaction_t *) job_p); break; } + case ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED: + case ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED: + { + ecma_free_promise_async_reaction_job ((ecma_job_promise_async_reaction_t *) job_p); + break; + } default: { JERRY_ASSERT (ecma_job_queue_get_type (job_p) == ECMA_JOB_PROMISE_THENABLE); diff --git a/jerry-core/ecma/operations/ecma-jobqueue.h b/jerry-core/ecma/operations/ecma-jobqueue.h index dccdfa464d..5f9f21cfc3 100644 --- a/jerry-core/ecma/operations/ecma-jobqueue.h +++ b/jerry-core/ecma/operations/ecma-jobqueue.h @@ -31,6 +31,8 @@ typedef enum { ECMA_JOB_PROMISE_REACTION, /**< promise reaction job */ + ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED, /**< fulfilled promise async reaction job */ + ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED, /**< rejected promise async reaction job */ ECMA_JOB_PROMISE_THENABLE, /**< promise thenable job */ } ecma_job_queue_item_type_t; @@ -45,6 +47,8 @@ typedef struct void ecma_job_queue_init (void); void ecma_enqueue_promise_reaction_job (ecma_value_t capability, ecma_value_t handler, ecma_value_t argument); +void ecma_enqueue_promise_async_reaction_job (ecma_value_t executable_object, + ecma_value_t argument, bool is_rejected); void ecma_enqueue_promise_resolve_thenable_job (ecma_value_t promise, ecma_value_t thenable, ecma_value_t then); void ecma_free_all_enqueued_jobs (void); diff --git a/jerry-core/ecma/operations/ecma-promise-object.c b/jerry-core/ecma/operations/ecma-promise-object.c index 282cfd1b9f..361ca3d4c2 100644 --- a/jerry-core/ecma/operations/ecma-promise-object.c +++ b/jerry-core/ecma/operations/ecma-promise-object.c @@ -123,22 +123,28 @@ ecma_promise_trigger_reactions (ecma_collection_t *reactions, /**< lists of reac while (buffer_p < buffer_end_p) { - ecma_value_t capability_with_tag = *buffer_p++; - ecma_object_t *capability_obj_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, capability_with_tag); - ecma_value_t capability = ecma_make_object_value (capability_obj_p); + ecma_value_t object_with_tag = *buffer_p++; + ecma_object_t *object_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, object_with_tag); + ecma_value_t object = ecma_make_object_value (object_p); + + if (JMEM_CP_GET_THIRD_BIT_FROM_POINTER_TAG (object_with_tag)) + { + ecma_enqueue_promise_async_reaction_job (object, value, is_reject); + continue; + } if (!is_reject) { ecma_value_t handler = ECMA_VALUE_TRUE; - if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (capability_with_tag)) + if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (object_with_tag)) { handler = *buffer_p++; } - ecma_enqueue_promise_reaction_job (capability, handler, value); + ecma_enqueue_promise_reaction_job (object, handler, value); } - else if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (capability_with_tag)) + else if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (object_with_tag)) { buffer_p++; } @@ -147,14 +153,14 @@ ecma_promise_trigger_reactions (ecma_collection_t *reactions, /**< lists of reac { ecma_value_t handler = ECMA_VALUE_FALSE; - if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (capability_with_tag)) + if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (object_with_tag)) { handler = *buffer_p++; } - ecma_enqueue_promise_reaction_job (capability, handler, value); + ecma_enqueue_promise_reaction_job (object, handler, value); } - else if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (capability_with_tag)) + else if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (object_with_tag)) { buffer_p++; } @@ -198,7 +204,7 @@ ecma_is_resolver_already_called (ecma_object_t *resolver_p, /**< resolver */ * * See also: ES2015 25.4.1.7 */ -static void +void ecma_reject_promise (ecma_value_t promise, /**< promise */ ecma_value_t reason) /**< reason for reject */ { @@ -227,7 +233,7 @@ ecma_reject_promise (ecma_value_t promise, /**< promise */ * * See also: ES2015 25.4.1.4 */ -static void +void ecma_fulfill_promise (ecma_value_t promise, /**< promise */ ecma_value_t value) /**< fulfilled value */ { @@ -235,6 +241,37 @@ ecma_fulfill_promise (ecma_value_t promise, /**< promise */ JERRY_ASSERT (ecma_promise_get_flags (obj_p) & ECMA_PROMISE_IS_PENDING); + if (promise == value) + { + ecma_raise_type_error (ECMA_ERR_MSG ("A promise cannot be resolved with itself.")); + ecma_value_t exception = jcontext_take_exception (); + ecma_reject_promise (promise, exception); + ecma_free_value (exception); + return; + } + + if (ecma_is_value_object (value)) + { + ecma_value_t then = ecma_op_object_get_by_magic_id (ecma_get_object_from_value (value), LIT_MAGIC_STRING_THEN); + + if (ECMA_IS_VALUE_ERROR (then)) + { + then = jcontext_take_exception (); + ecma_reject_promise (promise, then); + ecma_free_value (then); + return; + } + + if (ecma_op_is_callable (then)) + { + ecma_enqueue_promise_resolve_thenable_job (promise, value, then); + ecma_free_value (then); + return; + } + + ecma_free_value (then); + } + ecma_promise_set_state (obj_p, true); ecma_promise_set_result (obj_p, ecma_copy_value_if_not_object (value)); ecma_promise_object_t *promise_p = (ecma_promise_object_t *) obj_p; @@ -311,62 +348,16 @@ ecma_promise_resolve_handler (const ecma_value_t function, /**< the function its JERRY_ASSERT (ecma_is_promise (promise_obj_p)); /* 3., 4. */ - if (ecma_is_resolver_already_called (function_p, promise_obj_p)) - { - goto end_of_resolve_function; - } - - /* 5. */ - ((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED; - - /* If the argc is 0, then fulfill the `undefined`. */ - if (argc == 0) - { - ecma_fulfill_promise (promise, ECMA_VALUE_UNDEFINED); - goto end_of_resolve_function; - } - - /* 6. */ - if (argv[0] == promise) - { - ecma_object_t *error_p = ecma_new_standard_error (ECMA_ERROR_TYPE); - ecma_reject_promise (promise, ecma_make_object_value (error_p)); - ecma_deref_object (error_p); - goto end_of_resolve_function; - } - - /* 7. */ - if (!ecma_is_value_object (argv[0])) + if (!ecma_is_resolver_already_called (function_p, promise_obj_p)) { - ecma_fulfill_promise (promise, argv[0]); - goto end_of_resolve_function; - } - - /* 8. */ - ecma_value_t then = ecma_op_object_get_by_magic_id (ecma_get_object_from_value (argv[0]), - LIT_MAGIC_STRING_THEN); + /* 5. */ + ((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED; - if (ECMA_IS_VALUE_ERROR (then)) - { - /* 9. */ - then = jcontext_take_exception (); - ecma_reject_promise (promise, then); - } - else if (!ecma_op_is_callable (then)) - { - /* 11 .*/ - ecma_fulfill_promise (promise, argv[0]); + ecma_fulfill_promise (promise, (argc == 0) ? ECMA_VALUE_UNDEFINED : argv[0]); } - else - { - /* 12 */ - ecma_enqueue_promise_resolve_thenable_job (promise, argv[0], then); - } - - ecma_free_value (then); -end_of_resolve_function: ecma_free_value (promise); + return ECMA_VALUE_UNDEFINED; } /* ecma_promise_resolve_handler */ @@ -963,6 +954,31 @@ ecma_promise_then (ecma_value_t promise, /**< the promise which call 'then' */ return ret; } /* ecma_promise_then */ +/** + * Resume the execution of an async function after the promise is resolved + */ +void +ecma_promise_async_then (ecma_value_t promise, /**< promise object */ + ecma_value_t executable_object) /**< executable object of the async function */ +{ + ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise); + uint16_t flags = ecma_promise_get_flags (promise_obj_p); + + if (flags & ECMA_PROMISE_IS_PENDING) + { + ecma_value_t executable_object_with_tag; + ECMA_SET_NON_NULL_POINTER_TAG (executable_object_with_tag, ecma_get_object_from_value (executable_object), 0); + ECMA_SET_THIRD_BIT_TO_POINTER_TAG (executable_object_with_tag); + + ecma_collection_push_back (((ecma_promise_object_t *) promise_obj_p)->reactions, executable_object_with_tag); + return; + } + + ecma_value_t value = ecma_promise_get_result (promise_obj_p); + ecma_enqueue_promise_async_reaction_job (executable_object, value, !(flags & ECMA_PROMISE_IS_FULFILLED)); + ecma_free_value (value); +} /* ecma_promise_async_then */ + /** * @} * @} diff --git a/jerry-core/ecma/operations/ecma-promise-object.h b/jerry-core/ecma/operations/ecma-promise-object.h index 0e59660cc8..311ecf8c41 100644 --- a/jerry-core/ecma/operations/ecma-promise-object.h +++ b/jerry-core/ecma/operations/ecma-promise-object.h @@ -86,9 +86,12 @@ bool ecma_is_promise (ecma_object_t *obj_p); ecma_value_t ecma_op_create_promise_object (ecma_value_t executor, ecma_promise_executor_type_t type); uint16_t ecma_promise_get_flags (ecma_object_t *promise_p); ecma_value_t ecma_promise_get_result (ecma_object_t *promise_p); +void ecma_reject_promise (ecma_value_t promise, ecma_value_t reason); +void ecma_fulfill_promise (ecma_value_t promise, ecma_value_t value); ecma_value_t ecma_promise_new_capability (ecma_value_t constructor); ecma_value_t ecma_promise_reject_or_resolve (ecma_value_t this_arg, ecma_value_t value, bool is_resolve); ecma_value_t ecma_promise_then (ecma_value_t promise, ecma_value_t on_fulfilled, ecma_value_t on_rejected); +void ecma_promise_async_then (ecma_value_t promise, ecma_value_t executable_object); void ecma_promise_create_resolving_functions (ecma_object_t *object_p, ecma_promise_resolving_functions_t *funcs, bool create_already_resolved); void ecma_promise_free_resolving_functions (ecma_promise_resolving_functions_t *funcs); diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index ab236b3db4..1dfbf12191 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 (48u) +#define JERRY_SNAPSHOT_VERSION (49u) /** * Flags for jerry_generate_snapshot and jerry_generate_function_snapshot. diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index c854bd9528..0d0b29d6a8 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -728,12 +728,12 @@ VM_OC_YIELD) \ CBC_OPCODE (CBC_EXT_AWAIT, CBC_NO_FLAG, 0, \ VM_OC_AWAIT) \ + CBC_OPCODE (CBC_EXT_ASYNC_EXIT, CBC_NO_FLAG, 0, \ + VM_OC_ASYNC_EXIT) \ CBC_OPCODE (CBC_EXT_RETURN, CBC_NO_FLAG, -1, \ VM_OC_EXT_RETURN | VM_OC_GET_STACK) \ - CBC_OPCODE (CBC_EXT_RETURN_PROMISE, CBC_NO_FLAG, -1, \ - VM_OC_RETURN_PROMISE | VM_OC_GET_STACK) \ - CBC_OPCODE (CBC_EXT_RETURN_PROMISE_UNDEFINED, CBC_NO_FLAG, 0, \ - VM_OC_RETURN_PROMISE) \ + CBC_OPCODE (CBC_EXT_RETURN_UNDEFINED, CBC_NO_FLAG, 0, \ + VM_OC_EXT_RETURN) \ CBC_OPCODE (CBC_EXT_PUSH_NEW_TARGET, CBC_NO_FLAG, 1, \ VM_OC_PUSH_NEW_TARGET | VM_OC_PUT_STACK) \ \ @@ -843,6 +843,7 @@ typedef enum /* The following functions cannot be constructed (see CBC_FUNCTION_IS_CONSTRUCTABLE) */ CBC_FUNCTION_GENERATOR, /**< generator function */ + CBC_FUNCTION_ASYNC, /**< async function */ /* The following functions has no prototype (see CBC_FUNCTION_HAS_PROTOTYPE) */ CBC_FUNCTION_ARROW, /**< arrow function */ diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 9f609ae688..48b69dbf5e 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -3017,7 +3017,7 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ #if ENABLED (JERRY_ESNEXT) if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) { - parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_PROMISE_UNDEFINED); + parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_UNDEFINED); break; } #endif /* ENABLED (JERRY_ESNEXT) */ @@ -3028,14 +3028,6 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ parser_parse_expression (context_p, PARSE_EXPR); -#if ENABLED (JERRY_ESNEXT) - if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) - { - parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_PROMISE); - break; - } -#endif /* ENABLED (JERRY_ESNEXT) */ - if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL; diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 67e07695c3..8babadceab 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -687,6 +687,11 @@ parse_print_final_cbc (ecma_compiled_code_t *compiled_code_p, /**< compiled code JERRY_DEBUG_MSG (",generator"); break; } + case CBC_FUNCTION_ASYNC: + { + JERRY_DEBUG_MSG (",async"); + break; + } case CBC_FUNCTION_ARROW: { JERRY_DEBUG_MSG (",arrow"); @@ -948,6 +953,23 @@ parser_post_processing (parser_context_t *context_p) /**< context */ parser_branch_t branch; parser_stack_pop (context_p, &branch, sizeof (parser_branch_t)); parser_set_branch_to_current_position (context_p, &branch); + + JERRY_ASSERT (!(context_p->status_flags & PARSER_NO_END_LABEL)); + } + + if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) + { + PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); +#ifndef JERRY_NDEBUG + PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); +#endif /* !JERRY_NDEBUG */ + + parser_branch_t branch; + + parser_stack_pop (context_p, &branch, sizeof (parser_branch_t)); + parser_set_branch_to_current_position (context_p, &branch); + + JERRY_ASSERT (!(context_p->status_flags & PARSER_NO_END_LABEL)); } #endif /* ENABLED (JERRY_ESNEXT) */ @@ -1031,14 +1053,6 @@ parser_post_processing (parser_context_t *context_p) /**< context */ PARSER_NEXT_BYTE (page_p, offset); length++; -#if ENABLED (JERRY_ESNEXT) - if (ext_opcode == CBC_EXT_RETURN_PROMISE - || ext_opcode == CBC_EXT_RETURN_PROMISE_UNDEFINED) - { - last_opcode = CBC_RETURN; - } -#endif /* ENABLED (JERRY_ESNEXT) */ - #if ENABLED (JERRY_LINE_INFO) if (ext_opcode == CBC_EXT_LINE) { @@ -1339,14 +1353,18 @@ parser_post_processing (parser_context_t *context_p) /**< context */ { compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ARROW); } - else if (context_p->status_flags & PARSER_CLASS_CONSTRUCTOR) - { - compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_CONSTRUCTOR); - } else if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) { compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_GENERATOR); } + else if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) + { + compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ASYNC); + } + else if (context_p->status_flags & PARSER_CLASS_CONSTRUCTOR) + { + compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_CONSTRUCTOR); + } else { compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_NORMAL); @@ -1562,7 +1580,7 @@ parser_post_processing (parser_context_t *context_p) /**< context */ if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) { dst_p[-1] = CBC_EXT_OPCODE; - dst_p[0] = CBC_EXT_RETURN_PROMISE_UNDEFINED; + dst_p[0] = CBC_EXT_ASYNC_EXIT; dst_p++; } #endif /* ENABLED (JERRY_ESNEXT) */ @@ -1728,6 +1746,17 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ { context_p->status_flags &= (uint32_t) ~PARSER_IS_ASYNC_FUNCTION; } + + if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) + { + parser_branch_t branch; + parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_TRY_CREATE_CONTEXT, &branch); + parser_stack_push (context_p, &branch, sizeof (parser_branch_t)); + +#ifndef JERRY_NDEBUG + context_p->context_stack_depth = PARSER_TRY_CONTEXT_STACK_ALLOCATION; +#endif /* !JERRY_NDEBUG */ + } #endif /* ENABLED (JERRY_ESNEXT) */ if (context_p->token.type == end_type) @@ -1968,7 +1997,7 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ parser_stack_push (context_p, &branch, sizeof (parser_branch_t)); #ifndef JERRY_NDEBUG - context_p->context_stack_depth = PARSER_BLOCK_CONTEXT_STACK_ALLOCATION; + PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ } else @@ -2570,20 +2599,13 @@ parser_parse_arrow_function (parser_context_t *context_p, /**< context */ parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); - if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { - parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_PROMISE); + context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL; } else { - if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) - { - context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL; - } - else - { - parser_emit_cbc (context_p, CBC_RETURN); - } + parser_emit_cbc (context_p, CBC_RETURN); } parser_flush_cbc (context_p); diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index 088ef01621..4406052002 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -575,43 +575,54 @@ opfunc_append_array (ecma_value_t *stack_top_p, /**< current stack top */ * * @return executable object */ -ecma_value_t -opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ +vm_executable_object_t * +opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ + vm_create_executable_object_type_t type) /**< executable object type */ { const ecma_compiled_code_t *bytecode_header_p = frame_ctx_p->bytecode_header_p; - size_t size; + size_t size, register_end; ecma_bytecode_ref ((ecma_compiled_code_t *) bytecode_header_p); if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; - size = ((size_t) args_p->register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); + register_end = (size_t) args_p->register_end; + size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; - size = ((size_t) args_p->register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); + register_end = (size_t) args_p->register_end; + size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); } size_t total_size = JERRY_ALIGNUP (sizeof (vm_executable_object_t) + size, sizeof (uintptr_t)); - ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_function_obj_p), - ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE); + ecma_object_t *proto_p = NULL; + + if (type == VM_CREATE_EXECUTABLE_OBJECT_GENERATOR) + { + proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_function_obj_p), + ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE); + } ecma_object_t *object_p = ecma_create_object (proto_p, total_size, ECMA_OBJECT_TYPE_CLASS); - ecma_deref_object (proto_p); - vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p; + if (type == VM_CREATE_EXECUTABLE_OBJECT_GENERATOR) + { + ecma_deref_object (proto_p); + } + + /* Async function objects are not accessible, so their class_id is not relevant. */ executable_object_p->extended_object.u.class_prop.class_id = LIT_MAGIC_STRING_GENERATOR_UL; executable_object_p->extended_object.u.class_prop.extra_info = 0; JERRY_ASSERT (!frame_ctx_p->is_eval_code); - JERRY_ASSERT (frame_ctx_p->context_depth == 0); vm_frame_ctx_t *new_frame_ctx_p = &(executable_object_p->frame_ctx); *new_frame_ctx_p = *frame_ctx_p; @@ -627,18 +638,51 @@ opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p) /**< frame context /* Initial state is "not running", so all object references are released. */ + if (frame_ctx_p->context_depth > 0) + { + JERRY_ASSERT (type != VM_CREATE_EXECUTABLE_OBJECT_GENERATOR); + + ecma_value_t *register_end_p = new_registers_p + register_end; + + JERRY_ASSERT (register_end_p <= new_stack_top_p); + + while (new_registers_p < register_end_p) + { + ecma_deref_if_object (*new_registers_p++); + } + + vm_ref_lex_env_chain (frame_ctx_p->lex_env_p, + frame_ctx_p->context_depth, + new_registers_p, + false); + + new_registers_p += frame_ctx_p->context_depth; + + JERRY_ASSERT (new_registers_p <= new_stack_top_p); + } + while (new_registers_p < new_stack_top_p) { ecma_deref_if_object (*new_registers_p++); } + JERRY_ASSERT (new_frame_ctx_p->block_result == ECMA_VALUE_UNDEFINED); + new_frame_ctx_p->this_binding = ecma_copy_value_if_not_object (new_frame_ctx_p->this_binding); JERRY_CONTEXT (vm_top_context_p) = new_frame_ctx_p->prev_context_p; - return ecma_make_object_value (object_p); + return executable_object_p; } /* opfunc_create_executable_object */ +/** + * Byte code which resumes an executable object with throw + */ +const uint8_t opfunc_resume_executable_object_with_throw[1] = +{ + CBC_THROW +}; + /** * Resume the execution of an inactive executable object * @@ -663,13 +707,15 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /* register_end_p = register_p + args_p->register_end; } - while (register_p < register_end_p) - { - ecma_ref_if_object (*register_p++); - } + ecma_value_t *stack_top_p = executable_object_p->frame_ctx.stack_top_p; if (executable_object_p->frame_ctx.context_depth > 0) { + while (register_p < register_end_p) + { + ecma_ref_if_object (*register_p++); + } + vm_ref_lex_env_chain (executable_object_p->frame_ctx.lex_env_p, executable_object_p->frame_ctx.context_depth, register_p, @@ -678,13 +724,13 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /* register_p += executable_object_p->frame_ctx.context_depth; } - ecma_value_t *stack_top_p = executable_object_p->frame_ctx.stack_top_p; - while (register_p < stack_top_p) { ecma_ref_if_object (*register_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; @@ -716,14 +762,15 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /* JERRY_CONTEXT (vm_top_context_p) = executable_object_p->frame_ctx.prev_context_p; register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx); - - while (register_p < register_end_p) - { - ecma_deref_if_object (*register_p++); - } + stack_top_p = executable_object_p->frame_ctx.stack_top_p; if (executable_object_p->frame_ctx.context_depth > 0) { + while (register_p < register_end_p) + { + ecma_deref_if_object (*register_p++); + } + vm_ref_lex_env_chain (executable_object_p->frame_ctx.lex_env_p, executable_object_p->frame_ctx.context_depth, register_p, @@ -732,30 +779,15 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /* register_p += executable_object_p->frame_ctx.context_depth; } - stack_top_p = executable_object_p->frame_ctx.stack_top_p; - while (register_p < stack_top_p) { ecma_deref_if_object (*register_p++); } - return result; -} /* opfunc_resume_executable_object */ + ecma_deref_if_object (executable_object_p->frame_ctx.block_result); -/** - * Create a Promise object if needed and resolve it with a value - * - * @return Promise object - */ -ecma_value_t -opfunc_return_promise (ecma_value_t value) /**< value */ -{ - ecma_value_t promise = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE)); - ecma_value_t result = ecma_promise_reject_or_resolve (promise, value, true); - - ecma_free_value (value); return result; -} /* opfunc_return_promise */ +} /* opfunc_resume_executable_object */ /** * 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 d9937584a3..fb8e55af51 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -54,6 +54,19 @@ typedef enum NUMBER_BITWISE_NOT, /**< bitwise NOT calculation */ } number_bitwise_logic_op; +#if ENABLED (JERRY_ESNEXT) + +/** + * Types for opfunc_create_executable_object. + */ +typedef enum +{ + VM_CREATE_EXECUTABLE_OBJECT_GENERATOR, /**< create a generator function */ + VM_CREATE_EXECUTABLE_OBJECT_ASYNC, /**< create an async function */ +} vm_create_executable_object_type_t; + +#endif /* ENABLED (JERRY_ESNEXT) */ + /** * The stack contains spread object during the upcoming APPEND_ARRAY operation */ @@ -113,14 +126,13 @@ ecma_value_t opfunc_append_array (ecma_value_t *stack_top_p, uint16_t values_length); #if ENABLED (JERRY_ESNEXT) -ecma_value_t -opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p); +vm_executable_object_t * +opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p, vm_create_executable_object_type_t type); -ecma_value_t -opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, ecma_value_t value); +extern const uint8_t opfunc_resume_executable_object_with_throw[]; ecma_value_t -opfunc_return_promise (ecma_value_t value); +opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, ecma_value_t value); 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 8d66441941..d8dd6bbe4b 100644 --- a/jerry-core/vm/vm-stack.c +++ b/jerry-core/vm/vm-stack.c @@ -262,6 +262,20 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ } JERRY_ASSERT (byte_code_p[0] == CBC_EXT_OPCODE); + +#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); + + frame_ctx_p->byte_code_p = byte_code_p; + + *vm_stack_top_ref_p = vm_stack_top_p; + return true; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + JERRY_ASSERT (byte_code_p[1] >= CBC_EXT_FINALLY && byte_code_p[1] <= CBC_EXT_FINALLY_3); @@ -310,21 +324,17 @@ vm_get_context_value_offsets (ecma_value_t *context_item_p) /**< any item of a c { return PARSER_TRY_CONTEXT_STACK_ALLOCATION; } -#if ENABLED (JERRY_ESNEXT) case VM_CONTEXT_BLOCK: -#endif /* ENABLED (JERRY_ESNEXT) */ case VM_CONTEXT_WITH: { return PARSER_WITH_CONTEXT_STACK_ALLOCATION; } -#if ENABLED (JERRY_ESNEXT) case VM_CONTEXT_FOR_OF: { return ((3 << (VM_CONTEXT_OFFSET_SHIFT * 2)) | (2 << (VM_CONTEXT_OFFSET_SHIFT)) | PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); } -#endif /* ENABLED (JERRY_ESNEXT) */ default: { return (4 << (VM_CONTEXT_OFFSET_SHIFT)) | PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION; diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 75e313a4f7..ce9c8ff619 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -30,6 +30,7 @@ #include "ecma-lex-env.h" #include "ecma-objects.h" #include "ecma-objects-general.h" +#include "ecma-promise-object.h" #include "ecma-regexp-object.h" #include "ecma-try-catch-macro.h" #include "jcontext.h" @@ -2176,14 +2177,11 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ 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_create_executable_object (frame_ctx_p); - if (ECMA_IS_VALUE_ERROR (result)) - { - goto error; - } + vm_executable_object_t *executable_object_p; + executable_object_p = opfunc_create_executable_object (frame_ctx_p, VM_CREATE_EXECUTABLE_OBJECT_GENERATOR); - return result; + return ecma_make_object_value ((ecma_object_t *) executable_object_p); } case VM_OC_YIELD: { @@ -2194,7 +2192,52 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } case VM_OC_AWAIT: { - continue; + 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)) + { + 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; + + 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 + { + const uintptr_t object_offset = (uintptr_t) offsetof (vm_executable_object_t, frame_ctx); + + ecma_object_t *object_p = (ecma_object_t *) (((uintptr_t) frame_ctx_p) - object_offset); + ecma_promise_async_then (result, ecma_make_object_value (object_p)); + + ecma_free_value (result); + result = ECMA_VALUE_UNDEFINED; + } + + return result; } case VM_OC_EXT_RETURN: { @@ -2210,11 +2253,48 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ goto error; } - case VM_OC_RETURN_PROMISE: + case VM_OC_ASYNC_EXIT: { - result = opfunc_return_promise (left_value); - left_value = ECMA_VALUE_UNDEFINED; - goto error; + JERRY_ASSERT (frame_ctx_p->context_depth == PARSER_TRY_CONTEXT_STACK_ALLOCATION); + JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p); + + result = frame_ctx_p->block_result; + frame_ctx_p->block_result = ECMA_VALUE_UNDEFINED; + + if (result == ECMA_VALUE_UNDEFINED) + { + 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_CONTEXT (current_new_target) = old_new_target_p; + } + + left_value = stack_top_p[-2]; + + if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW) + { + ecma_reject_promise (result, left_value); + } + else + { + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_TRY + || VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_RETURN); + + if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_TRY) + { + left_value = ECMA_VALUE_UNDEFINED; + } + + ecma_fulfill_promise (result, left_value); + } + + ecma_free_value (left_value); + + frame_ctx_p->context_depth = 0; + frame_ctx_p->call_operation = VM_NO_EXEC_OP; + return result; } case VM_OC_STRING_CONCAT: { diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index f1099dd1ee..46f0595353 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -273,7 +273,7 @@ typedef enum VM_OC_YIELD, /**< yield operation */ VM_OC_AWAIT, /**< await operation */ VM_OC_EXT_RETURN, /**< return which also clears the stack */ - VM_OC_RETURN_PROMISE, /**< return from an async function */ + VM_OC_ASYNC_EXIT, /**< return from async function */ VM_OC_STRING_CONCAT, /**< string concatenation */ VM_OC_GET_TEMPLATE_OBJECT, /**< GetTemplateObject operation */ VM_OC_PUSH_NEW_TARGET, /**< push new.target onto the stack */ @@ -342,7 +342,7 @@ typedef enum VM_OC_YIELD = VM_OC_NONE, /**< yield operation */ VM_OC_AWAIT = VM_OC_NONE, /**< await operation */ VM_OC_EXT_RETURN = VM_OC_NONE, /**< return which also clears the stack */ - VM_OC_RETURN_PROMISE = VM_OC_NONE, /**< return from an async function */ + VM_OC_ASYNC_EXIT = VM_OC_NONE, /**< return from async function */ VM_OC_STRING_CONCAT = VM_OC_NONE, /**< string concatenation */ VM_OC_GET_TEMPLATE_OBJECT = VM_OC_NONE, /**< GetTemplateObject operation */ VM_OC_PUSH_NEW_TARGET = VM_OC_NONE, /**< push new.target onto the stack */ diff --git a/tests/jerry/es.next/function-await2.js b/tests/jerry/es.next/function-await2.js new file mode 100644 index 0000000000..c4849f2c92 --- /dev/null +++ b/tests/jerry/es.next/function-await2.js @@ -0,0 +1,166 @@ +// 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 +var p, r + +// Test 1 + +async function f1(p) +{ + assert(await p === 1) + return 2 +} + +p = new Promise(function(resolve, reject) { r = resolve }) + +f1(p).then(function (v) { + assert(v === 2) + successCount++ +}) + +r(1) + +// Test 2 + +var f2 = async(p) => +{ + assert(await p === 3) +} + +p = new Promise(function(resolve, reject) { r = resolve }) + +f2(p).then(function (v) { + assert(v === undefined) + successCount++ +}) + +r(3) + +// Test 3 + +var thenableCounter = 0 + +async function f3() +{ + return new Promise(function(resolve) { resolve(f3) }) +} + +f3.then = function(resolve) { + // Repeating resolve with 'then' + if (++thenableCounter < 5) { + resolve(f3) + } else { + successCount++ + } +} + +f3() + +// Test 4 + +async function f4(p) +{ + try { + throw 4 + } catch (e) { + throw 5 + } +} + +p = new Promise(function(resolve, reject) { r = resolve }) + +f4(p).then(undefined, function (v) { + assert(v === 5) + successCount++ +}) + +r(1) + +// Test 5 + +async function f5(p) +{ + try { + return 6 + } finally { + throw 7 + } +} + +p = new Promise(function(resolve, reject) { r = resolve }) + +f5(p).then(undefined, function (v) { + assert(v === 7) + successCount++ +}) + +r(1) + +// Test 6 + +p = new Promise(function(resolve, reject) { r = resolve }) + +async function f6(p) +{ + await p + return self +} + +var self = f6() + +self.then(undefined, function (v) { + assert(v instanceof TypeError) + successCount++ +}) + +r(1) + +// Test 7 + +async function f7(p) +{ + var x = {} + assert((await x) === x) + + x = 3.14 + assert((await x) === x) + + x = "Test string" + assert((await x) === x) + + successCount++ +} +f7(); + +// Test 8 + +async function f8() { + var p = new Promise(function() {}); + Object.defineProperty(p, 'constructor', { get() { throw "Error!" } }); + + await p +} + +f8().then(undefined, function (v) { + assert(v === "Error!") + successCount++ +}) + +// END + +function __checkAsync() { + assert(successCount === 8) + assert(thenableCounter === 5) +} diff --git a/tests/jerry/es.next/function-await3.js b/tests/jerry/es.next/function-await3.js new file mode 100644 index 0000000000..6789b84f05 --- /dev/null +++ b/tests/jerry/es.next/function-await3.js @@ -0,0 +1,69 @@ +// 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 + +async function f1() +{ + throw 1 +} + +async function f2() +{ + try { + assert(await f1() && false) + } catch (e) { + assert(e === 1) + return 2 + } finally { + return 3 + } +} + +async function f3() +{ + return await f2() + 1 +} + +async function f4() +{ + return await f1() +} + +async function f5() +{ + var o = { a:f2, b:f2, c:f2, d:f2 } + + for (i in o) { + var p1 = f3() + var p2 = f4() + + assert(await o[i]() === 3) + assert(await p1 === 4) + + try { + assert(await p2 && false) + } catch (e) { + assert(e === 1) + } + + successCount++ + } +} + +f5() + +function __checkAsync() { + assert(successCount === 4) +}