Implement async function execution. (#3897)
JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
@@ -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) */
|
||||
|
||||
@@ -66,14 +66,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
|
||||
*
|
||||
@@ -132,7 +124,7 @@ ecma_builtin_generator_prototype_object_do (vm_executable_object_t *executable_o
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -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'.");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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))
|
||||
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;
|
||||
|
||||
ecma_fulfill_promise (promise, (argc == 0) ? ECMA_VALUE_UNDEFINED : argv[0]);
|
||||
}
|
||||
|
||||
/* 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]))
|
||||
{
|
||||
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);
|
||||
|
||||
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]);
|
||||
}
|
||||
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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user