mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Optimize Promise data structures. (#3768)
This patch reworks several structures: - Fulfill and reject reactions are combined into one collection. The values in this collection are compressed: a capability followed by an optional fulfill and reject functions. - Fulfill and reject reactions are directly stored, no need to allocate an object for them. - The job queue directly stores its items, this saves a pointer to the value, and the callback is replaced by an uint8 type. - Promise status and already resolved is stored in extra_info. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
parent
1774cca47c
commit
1105b43c22
@ -76,9 +76,8 @@ JERRY_STATIC_ASSERT ((int) RE_FLAG_GLOBAL == (int) JERRY_REGEXP_FLAG_GLOBAL
|
||||
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
|
||||
/* The internal ECMA_PROMISE_STATE_* values are "one byte away" from the API values */
|
||||
JERRY_STATIC_ASSERT (((ECMA_PROMISE_STATE_PENDING + 1) == JERRY_PROMISE_STATE_PENDING)
|
||||
&& ((ECMA_PROMISE_STATE_FULFILLED + 1) == JERRY_PROMISE_STATE_FULFILLED)
|
||||
&& ((ECMA_PROMISE_STATE_REJECTED + 1) == JERRY_PROMISE_STATE_REJECTED),
|
||||
JERRY_STATIC_ASSERT ((int) ECMA_PROMISE_IS_PENDING == (int) JERRY_PROMISE_STATE_PENDING
|
||||
&& (int) ECMA_PROMISE_IS_FULFILLED == (int) JERRY_PROMISE_STATE_FULFILLED,
|
||||
promise_internal_state_matches_external);
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */
|
||||
|
||||
@ -1616,14 +1615,14 @@ jerry_create_promise (void)
|
||||
jerry_assert_api_available ();
|
||||
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
|
||||
ecma_object_t * old_new_target_p = JERRY_CONTEXT (current_new_target);
|
||||
ecma_object_t *old_new_target_p = JERRY_CONTEXT (current_new_target);
|
||||
|
||||
if (old_new_target_p == NULL)
|
||||
{
|
||||
JERRY_CONTEXT (current_new_target) = ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE);
|
||||
}
|
||||
|
||||
ecma_value_t promise_value = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_PROMISE_EXECUTOR_EMPTY);
|
||||
ecma_value_t promise_value = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_PROMISE_EXECUTOR_EMPTY);
|
||||
|
||||
JERRY_CONTEXT (current_new_target) = old_new_target_p;
|
||||
return promise_value;
|
||||
@ -3331,12 +3330,10 @@ jerry_get_promise_state (const jerry_value_t promise) /**< promise object to get
|
||||
return JERRY_PROMISE_STATE_NONE;
|
||||
}
|
||||
|
||||
uint8_t state = ecma_promise_get_state (ecma_get_object_from_value (promise));
|
||||
uint16_t flags = ecma_promise_get_flags (ecma_get_object_from_value (promise));
|
||||
flags &= (ECMA_PROMISE_IS_PENDING | ECMA_PROMISE_IS_FULFILLED);
|
||||
|
||||
JERRY_ASSERT (state < ECMA_PROMISE_STATE__COUNT);
|
||||
|
||||
/* Static assert above guarantees the mapping from internal type to external type. */
|
||||
return (jerry_promise_state_t) (state + 1);
|
||||
return (flags ? flags : JERRY_PROMISE_STATE_REJECTED);
|
||||
#else /* !ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */
|
||||
JERRY_UNUSED (promise);
|
||||
return JERRY_PROMISE_STATE_NONE;
|
||||
|
||||
@ -268,27 +268,28 @@ ecma_gc_mark_promise_object (ecma_extended_object_t *ext_object_p) /**< extended
|
||||
|
||||
/* Mark all reactions. */
|
||||
ecma_promise_object_t *promise_object_p = (ecma_promise_object_t *) ext_object_p;
|
||||
ecma_collection_t *collection_p = promise_object_p->fulfill_reactions;
|
||||
ecma_collection_t *collection_p = promise_object_p->reactions;
|
||||
|
||||
if (collection_p != NULL)
|
||||
{
|
||||
ecma_value_t *buffer_p = collection_p->buffer_p;
|
||||
ecma_value_t *buffer_end_p = buffer_p + collection_p->item_count;
|
||||
|
||||
for (uint32_t i = 0; i < collection_p->item_count; i++)
|
||||
while (buffer_p < buffer_end_p)
|
||||
{
|
||||
ecma_gc_set_object_visited (ecma_get_object_from_value (buffer_p[i]));
|
||||
}
|
||||
}
|
||||
ecma_value_t value = *buffer_p++;
|
||||
|
||||
collection_p = promise_object_p->reject_reactions;
|
||||
ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, value));
|
||||
|
||||
if (collection_p != NULL)
|
||||
{
|
||||
ecma_value_t *buffer_p = collection_p->buffer_p;
|
||||
if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (value))
|
||||
{
|
||||
ecma_gc_set_object_visited (ecma_get_object_from_value (*buffer_p++));
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < collection_p->item_count; i++)
|
||||
{
|
||||
ecma_gc_set_object_visited (ecma_get_object_from_value (buffer_p[i]));
|
||||
if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (value))
|
||||
{
|
||||
ecma_gc_set_object_visited (ecma_get_object_from_value (*buffer_p++));
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* ecma_gc_mark_promise_object */
|
||||
@ -1145,8 +1146,10 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
|
||||
case LIT_MAGIC_STRING_PROMISE_UL:
|
||||
{
|
||||
ecma_free_value_if_not_object (ext_object_p->u.class_prop.u.value);
|
||||
ecma_collection_free_if_not_object (((ecma_promise_object_t *) object_p)->fulfill_reactions);
|
||||
ecma_collection_free_if_not_object (((ecma_promise_object_t *) object_p)->reject_reactions);
|
||||
|
||||
/* Reactions only contains objects. */
|
||||
ecma_collection_destroy (((ecma_promise_object_t *) object_p)->reactions);
|
||||
|
||||
ext_object_size = sizeof (ecma_promise_object_t);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -23,6 +23,11 @@
|
||||
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
|
||||
|
||||
/**
|
||||
* Mask for job queue type.
|
||||
*/
|
||||
#define ECMA_JOB_QUEURE_TYPE_MASK ((uintptr_t) 0x07)
|
||||
|
||||
/** \addtogroup ecma ECMA
|
||||
* @{
|
||||
*
|
||||
@ -35,7 +40,9 @@
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ecma_value_t reaction; /**< the PromiseReaction */
|
||||
ecma_job_queue_item_t header; /**< job queue item header */
|
||||
ecma_value_t capability; /**< capability object */
|
||||
ecma_value_t handler; /**< handler function */
|
||||
ecma_value_t argument; /**< argument for the reaction */
|
||||
} ecma_job_promise_reaction_t;
|
||||
|
||||
@ -44,8 +51,9 @@ typedef struct
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ecma_job_queue_item_t header; /**< job queue item header */
|
||||
ecma_value_t promise; /**< promise to be resolved */
|
||||
ecma_value_t thenable; /**< thenbale object */
|
||||
ecma_value_t thenable; /**< thenable object */
|
||||
ecma_value_t then; /**< 'then' function */
|
||||
} ecma_job_promise_resolve_thenable_t;
|
||||
|
||||
@ -59,23 +67,26 @@ void ecma_job_queue_init (void)
|
||||
} /* ecma_job_queue_init */
|
||||
|
||||
/**
|
||||
* Create a PromiseReactionJob.
|
||||
* Get the type of the job.
|
||||
*
|
||||
* @return pointer to the PromiseReactionJob
|
||||
* @return type of the job
|
||||
*/
|
||||
static ecma_job_promise_reaction_t *
|
||||
ecma_create_promise_reaction_job (ecma_value_t reaction, /**< PromiseReaction */
|
||||
ecma_value_t argument) /**< argument for the reaction */
|
||||
static inline ecma_job_queue_item_type_t JERRY_ATTR_ALWAYS_INLINE
|
||||
ecma_job_queue_get_type (ecma_job_queue_item_t *job_p) /**< the job */
|
||||
{
|
||||
JERRY_ASSERT (ecma_is_value_object (reaction));
|
||||
return (ecma_job_queue_item_type_t) (job_p->next_and_type & ECMA_JOB_QUEURE_TYPE_MASK);
|
||||
} /* ecma_job_queue_get_type */
|
||||
|
||||
ecma_job_promise_reaction_t *job_p;
|
||||
job_p = (ecma_job_promise_reaction_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_reaction_t));
|
||||
job_p->reaction = ecma_copy_value (reaction);
|
||||
job_p->argument = ecma_copy_value (argument);
|
||||
|
||||
return job_p;
|
||||
} /* ecma_create_promise_reaction_job */
|
||||
/**
|
||||
* Get the next job of the job queue.
|
||||
*
|
||||
* @return next job
|
||||
*/
|
||||
static inline ecma_job_queue_item_t *JERRY_ATTR_ALWAYS_INLINE
|
||||
ecma_job_queue_get_next (ecma_job_queue_item_t *job_p) /**< the job */
|
||||
{
|
||||
return (ecma_job_queue_item_t *) (job_p->next_and_type & ~ECMA_JOB_QUEURE_TYPE_MASK);
|
||||
} /* ecma_job_queue_get_next */
|
||||
|
||||
/**
|
||||
* Free the heap and the member of the PromiseReactionJob.
|
||||
@ -85,36 +96,13 @@ ecma_free_promise_reaction_job (ecma_job_promise_reaction_t *job_p) /**< points
|
||||
{
|
||||
JERRY_ASSERT (job_p != NULL);
|
||||
|
||||
ecma_free_value (job_p->reaction);
|
||||
ecma_free_value (job_p->capability);
|
||||
ecma_free_value (job_p->handler);
|
||||
ecma_free_value (job_p->argument);
|
||||
|
||||
jmem_heap_free_block (job_p, sizeof (ecma_job_promise_reaction_t));
|
||||
} /* ecma_free_promise_reaction_job */
|
||||
|
||||
/**
|
||||
* Create a PromiseResolveThenableJob
|
||||
*
|
||||
* @return pointer to the PromiseResolveThenableJob
|
||||
*/
|
||||
static ecma_job_promise_resolve_thenable_t *
|
||||
ecma_create_promise_resolve_thenable_job (ecma_value_t promise, /**< promise to be resolved */
|
||||
ecma_value_t thenable, /**< thenable object */
|
||||
ecma_value_t then) /**< 'then' function */
|
||||
{
|
||||
JERRY_ASSERT (ecma_is_promise (ecma_get_object_from_value (promise)));
|
||||
JERRY_ASSERT (ecma_is_value_object (thenable));
|
||||
JERRY_ASSERT (ecma_op_is_callable (then));
|
||||
|
||||
ecma_job_promise_resolve_thenable_t *job_p;
|
||||
job_p = (ecma_job_promise_resolve_thenable_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_resolve_thenable_t));
|
||||
|
||||
job_p->promise = ecma_copy_value (promise);
|
||||
job_p->thenable = ecma_copy_value (thenable);
|
||||
job_p->then = ecma_copy_value (then);
|
||||
|
||||
return job_p;
|
||||
} /* ecma_create_promise_resolve_thenable_job */
|
||||
|
||||
/**
|
||||
* Free the heap and the member of the PromiseResolveThenableJob.
|
||||
*/
|
||||
@ -140,20 +128,15 @@ ecma_free_promise_resolve_thenable_job (ecma_job_promise_resolve_thenable_t *job
|
||||
* Returned value must be freed with ecma_free_value
|
||||
*/
|
||||
static ecma_value_t
|
||||
ecma_process_promise_reaction_job (void *obj_p) /**< the job to be operated */
|
||||
ecma_process_promise_reaction_job (ecma_job_promise_reaction_t *job_p) /**< the job to be operated */
|
||||
{
|
||||
ecma_job_promise_reaction_t *job_p = (ecma_job_promise_reaction_t *) obj_p;
|
||||
ecma_object_t *reaction_p = ecma_get_object_from_value (job_p->reaction);
|
||||
|
||||
ecma_string_t *capability_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_CAPABILITY);
|
||||
ecma_string_t *handler_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_HANDLER);
|
||||
ecma_string_t *resolve_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE);
|
||||
ecma_string_t *reject_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT);
|
||||
|
||||
/* 2. */
|
||||
ecma_value_t capability = ecma_op_object_get (reaction_p, capability_str_p);
|
||||
ecma_value_t capability = job_p->capability;
|
||||
/* 3. */
|
||||
ecma_value_t handler = ecma_op_object_get (reaction_p, handler_str_p);
|
||||
ecma_value_t handler = job_p->handler;
|
||||
|
||||
JERRY_ASSERT (ecma_is_value_boolean (handler) || ecma_op_is_callable (handler));
|
||||
|
||||
@ -208,8 +191,6 @@ ecma_process_promise_reaction_job (void *obj_p) /**< the job to be operated */
|
||||
}
|
||||
|
||||
ecma_free_value (handler_result);
|
||||
ecma_free_value (handler);
|
||||
ecma_free_value (capability);
|
||||
ecma_free_promise_reaction_job (job_p);
|
||||
|
||||
return status;
|
||||
@ -224,25 +205,25 @@ ecma_process_promise_reaction_job (void *obj_p) /**< the job to be operated */
|
||||
* Returned value must be freed with ecma_free_value
|
||||
*/
|
||||
static ecma_value_t
|
||||
ecma_process_promise_resolve_thenable_job (void *obj_p) /**< the job to be operated */
|
||||
ecma_process_promise_resolve_thenable_job (ecma_job_promise_resolve_thenable_t *job_p) /**< the job to be operated */
|
||||
{
|
||||
ecma_job_promise_resolve_thenable_t *job_p = (ecma_job_promise_resolve_thenable_t *) obj_p;
|
||||
ecma_object_t *promise_p = ecma_get_object_from_value (job_p->promise);
|
||||
ecma_promise_resolving_functions_t *funcs = ecma_promise_create_resolving_functions (promise_p);
|
||||
ecma_promise_resolving_functions_t funcs;
|
||||
ecma_promise_create_resolving_functions (promise_p, &funcs);
|
||||
|
||||
ecma_string_t *str_resolve_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_RESOLVE_FUNCTION);
|
||||
ecma_string_t *str_reject_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_REJECT_FUNCTION);
|
||||
|
||||
ecma_op_object_put (promise_p,
|
||||
str_resolve_p,
|
||||
funcs->resolve,
|
||||
funcs.resolve,
|
||||
false);
|
||||
ecma_op_object_put (promise_p,
|
||||
str_reject_p,
|
||||
funcs->reject,
|
||||
funcs.reject,
|
||||
false);
|
||||
|
||||
ecma_value_t argv[] = { funcs->resolve, funcs->reject };
|
||||
ecma_value_t argv[] = { funcs.resolve, funcs.reject };
|
||||
ecma_value_t ret;
|
||||
ecma_value_t then_call_result = ecma_op_function_call (ecma_get_object_from_value (job_p->then),
|
||||
job_p->thenable,
|
||||
@ -255,7 +236,7 @@ ecma_process_promise_resolve_thenable_job (void *obj_p) /**< the job to be opera
|
||||
{
|
||||
then_call_result = jcontext_take_exception ();
|
||||
|
||||
ret = ecma_op_function_call (ecma_get_object_from_value (funcs->reject),
|
||||
ret = ecma_op_function_call (ecma_get_object_from_value (funcs.reject),
|
||||
ECMA_VALUE_UNDEFINED,
|
||||
&then_call_result,
|
||||
1);
|
||||
@ -263,7 +244,7 @@ ecma_process_promise_resolve_thenable_job (void *obj_p) /**< the job to be opera
|
||||
ecma_free_value (then_call_result);
|
||||
}
|
||||
|
||||
ecma_promise_free_resolving_functions (funcs);
|
||||
ecma_promise_free_resolving_functions (&funcs);
|
||||
ecma_free_promise_resolve_thenable_job (job_p);
|
||||
|
||||
return ret;
|
||||
@ -273,23 +254,21 @@ ecma_process_promise_resolve_thenable_job (void *obj_p) /**< the job to be opera
|
||||
* Enqueue a Promise job into the jobqueue.
|
||||
*/
|
||||
static void
|
||||
ecma_enqueue_job (ecma_job_handler_t handler, /**< the handler for the job */
|
||||
void *job_p) /**< the job */
|
||||
ecma_enqueue_job (ecma_job_queue_item_t *job_p) /**< the job */
|
||||
{
|
||||
ecma_job_queueitem_t *item_p = jmem_heap_alloc_block (sizeof (ecma_job_queueitem_t));
|
||||
item_p->job_p = job_p;
|
||||
item_p->handler = handler;
|
||||
item_p->next_p = NULL;
|
||||
JERRY_ASSERT (job_p->next_and_type <= ECMA_JOB_QUEURE_TYPE_MASK);
|
||||
|
||||
if (JERRY_CONTEXT (job_queue_head_p) == NULL)
|
||||
{
|
||||
JERRY_CONTEXT (job_queue_head_p) = item_p;
|
||||
JERRY_CONTEXT (job_queue_tail_p) = item_p;
|
||||
JERRY_CONTEXT (job_queue_head_p) = job_p;
|
||||
JERRY_CONTEXT (job_queue_tail_p) = job_p;
|
||||
}
|
||||
else
|
||||
{
|
||||
JERRY_CONTEXT (job_queue_tail_p)->next_p = item_p;
|
||||
JERRY_CONTEXT (job_queue_tail_p) = item_p;
|
||||
JERRY_ASSERT ((JERRY_CONTEXT (job_queue_tail_p)->next_and_type & ~ECMA_JOB_QUEURE_TYPE_MASK) == 0);
|
||||
|
||||
JERRY_CONTEXT (job_queue_tail_p)->next_and_type |= (uintptr_t) job_p;
|
||||
JERRY_CONTEXT (job_queue_tail_p) = job_p;
|
||||
}
|
||||
} /* ecma_enqueue_job */
|
||||
|
||||
@ -297,11 +276,18 @@ ecma_enqueue_job (ecma_job_handler_t handler, /**< the handler for the job */
|
||||
* Enqueue a PromiseReactionJob into the jobqueue.
|
||||
*/
|
||||
void
|
||||
ecma_enqueue_promise_reaction_job (ecma_value_t reaction, /**< PromiseReaction */
|
||||
ecma_enqueue_promise_reaction_job (ecma_value_t capability, /**< capability object */
|
||||
ecma_value_t handler, /**< handler function */
|
||||
ecma_value_t argument) /**< argument for the reaction */
|
||||
{
|
||||
ecma_job_promise_reaction_t *job_p = ecma_create_promise_reaction_job (reaction, argument);
|
||||
ecma_enqueue_job (ecma_process_promise_reaction_job, job_p);
|
||||
ecma_job_promise_reaction_t *job_p;
|
||||
job_p = (ecma_job_promise_reaction_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_reaction_t));
|
||||
job_p->header.next_and_type = ECMA_JOB_PROMISE_REACTION;
|
||||
job_p->capability = ecma_copy_value (capability);
|
||||
job_p->handler = ecma_copy_value (handler);
|
||||
job_p->argument = ecma_copy_value (argument);
|
||||
|
||||
ecma_enqueue_job (&job_p->header);
|
||||
} /* ecma_enqueue_promise_reaction_job */
|
||||
|
||||
/**
|
||||
@ -312,10 +298,18 @@ ecma_enqueue_promise_resolve_thenable_job (ecma_value_t promise, /**< promise to
|
||||
ecma_value_t thenable, /**< thenable object */
|
||||
ecma_value_t then) /**< 'then' function */
|
||||
{
|
||||
ecma_job_promise_resolve_thenable_t *job_p = ecma_create_promise_resolve_thenable_job (promise,
|
||||
thenable,
|
||||
then);
|
||||
ecma_enqueue_job (ecma_process_promise_resolve_thenable_job, job_p);
|
||||
JERRY_ASSERT (ecma_is_promise (ecma_get_object_from_value (promise)));
|
||||
JERRY_ASSERT (ecma_is_value_object (thenable));
|
||||
JERRY_ASSERT (ecma_op_is_callable (then));
|
||||
|
||||
ecma_job_promise_resolve_thenable_t *job_p;
|
||||
job_p = (ecma_job_promise_resolve_thenable_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_resolve_thenable_t));
|
||||
job_p->header.next_and_type = ECMA_JOB_PROMISE_THENABLE;
|
||||
job_p->promise = ecma_copy_value (promise);
|
||||
job_p->thenable = ecma_copy_value (thenable);
|
||||
job_p->then = ecma_copy_value (then);
|
||||
|
||||
ecma_enqueue_job (&job_p->header);
|
||||
} /* ecma_enqueue_promise_resolve_thenable_job */
|
||||
|
||||
/**
|
||||
@ -332,15 +326,26 @@ ecma_process_all_enqueued_jobs (void)
|
||||
|
||||
while (JERRY_CONTEXT (job_queue_head_p) != NULL && !ECMA_IS_VALUE_ERROR (ret))
|
||||
{
|
||||
ecma_job_queueitem_t *item_p = JERRY_CONTEXT (job_queue_head_p);
|
||||
JERRY_CONTEXT (job_queue_head_p) = JERRY_CONTEXT (job_queue_head_p)->next_p;
|
||||
ecma_job_queue_item_t *job_p = JERRY_CONTEXT (job_queue_head_p);
|
||||
JERRY_CONTEXT (job_queue_head_p) = ecma_job_queue_get_next (job_p);
|
||||
|
||||
void *job_p = item_p->job_p;
|
||||
ecma_job_handler_t handler = item_p->handler;
|
||||
jmem_heap_free_block (item_p, sizeof (ecma_job_queueitem_t));
|
||||
ecma_fast_free_value (ret);
|
||||
|
||||
ecma_free_value (ret);
|
||||
ret = handler (job_p);
|
||||
switch (ecma_job_queue_get_type (job_p))
|
||||
{
|
||||
case ECMA_JOB_PROMISE_REACTION:
|
||||
{
|
||||
ret = ecma_process_promise_reaction_job ((ecma_job_promise_reaction_t *) job_p);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
JERRY_ASSERT (ecma_job_queue_get_type (job_p) == ECMA_JOB_PROMISE_THENABLE);
|
||||
|
||||
ret = ecma_process_promise_resolve_thenable_job ((ecma_job_promise_resolve_thenable_t *) job_p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -354,12 +359,24 @@ ecma_free_all_enqueued_jobs (void)
|
||||
{
|
||||
while (JERRY_CONTEXT (job_queue_head_p) != NULL)
|
||||
{
|
||||
ecma_job_queueitem_t *item_p = JERRY_CONTEXT (job_queue_head_p);
|
||||
JERRY_CONTEXT (job_queue_head_p) = item_p->next_p;
|
||||
void *job_p = item_p->job_p;
|
||||
jmem_heap_free_block (item_p, sizeof (ecma_job_queueitem_t));
|
||||
ecma_job_queue_item_t *job_p = JERRY_CONTEXT (job_queue_head_p);
|
||||
JERRY_CONTEXT (job_queue_head_p) = ecma_job_queue_get_next (job_p);
|
||||
|
||||
ecma_free_promise_reaction_job (job_p);
|
||||
switch (ecma_job_queue_get_type (job_p))
|
||||
{
|
||||
case ECMA_JOB_PROMISE_REACTION:
|
||||
{
|
||||
ecma_free_promise_reaction_job ((ecma_job_promise_reaction_t *) job_p);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
JERRY_ASSERT (ecma_job_queue_get_type (job_p) == ECMA_JOB_PROMISE_THENABLE);
|
||||
|
||||
ecma_free_promise_resolve_thenable_job ((ecma_job_promise_resolve_thenable_t *) job_p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* ecma_free_all_enqueued_jobs */
|
||||
|
||||
|
||||
@ -26,23 +26,25 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jerry job handler function type
|
||||
* Job queue item types.
|
||||
*/
|
||||
typedef ecma_value_t (*ecma_job_handler_t) (void *job_p);
|
||||
typedef enum
|
||||
{
|
||||
ECMA_JOB_PROMISE_REACTION, /**< promise reaction job */
|
||||
ECMA_JOB_PROMISE_THENABLE, /**< promise thenable job */
|
||||
} ecma_job_queue_item_type_t;
|
||||
|
||||
/**
|
||||
* Description of the job queue item.
|
||||
*/
|
||||
typedef struct ecma_job_queueitem_t
|
||||
typedef struct
|
||||
{
|
||||
struct ecma_job_queueitem_t *next_p; /**< points to next item */
|
||||
ecma_job_handler_t handler; /**< the handler for the job*/
|
||||
void *job_p; /**< points to the job */
|
||||
} ecma_job_queueitem_t;
|
||||
uintptr_t next_and_type; /**< next and type members of a queue item */
|
||||
} ecma_job_queue_item_t;
|
||||
|
||||
void ecma_job_queue_init (void);
|
||||
|
||||
void ecma_enqueue_promise_reaction_job (ecma_value_t reaction, ecma_value_t argument);
|
||||
void ecma_enqueue_promise_reaction_job (ecma_value_t capability, ecma_value_t handler, ecma_value_t argument);
|
||||
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);
|
||||
|
||||
|
||||
@ -84,61 +84,30 @@ ecma_promise_set_result (ecma_object_t *obj_p, /**< points to promise object */
|
||||
*
|
||||
* @return the state's enum value
|
||||
*/
|
||||
uint8_t
|
||||
ecma_promise_get_state (ecma_object_t *obj_p) /**< points to promise object */
|
||||
uint16_t
|
||||
ecma_promise_get_flags (ecma_object_t *obj_p) /**< points to promise object */
|
||||
{
|
||||
JERRY_ASSERT (ecma_is_promise (obj_p));
|
||||
|
||||
return ((ecma_promise_object_t *) obj_p)->state;
|
||||
} /* ecma_promise_get_state */
|
||||
return ((ecma_extended_object_t *) obj_p)->u.class_prop.extra_info;
|
||||
} /* ecma_promise_get_flags */
|
||||
|
||||
/**
|
||||
* Set the PromiseState of promise.
|
||||
*/
|
||||
static inline void JERRY_ATTR_ALWAYS_INLINE
|
||||
ecma_promise_set_state (ecma_object_t *obj_p, /**< points to promise object */
|
||||
uint8_t state) /**< the state */
|
||||
bool is_fulfilled) /**< new flags */
|
||||
{
|
||||
JERRY_ASSERT (ecma_is_promise (obj_p));
|
||||
JERRY_ASSERT (ecma_promise_get_flags (obj_p) & ECMA_PROMISE_IS_PENDING);
|
||||
|
||||
((ecma_promise_object_t *) obj_p)->state = state;
|
||||
uint16_t flags_to_invert = (is_fulfilled ? (ECMA_PROMISE_IS_PENDING | ECMA_PROMISE_IS_FULFILLED)
|
||||
: ECMA_PROMISE_IS_PENDING);
|
||||
|
||||
((ecma_extended_object_t *) obj_p)->u.class_prop.extra_info ^= flags_to_invert;
|
||||
} /* ecma_promise_set_state */
|
||||
|
||||
/**
|
||||
* Get the bool value of alreadyResolved.
|
||||
*
|
||||
* @return bool value of alreadyResolved.
|
||||
*/
|
||||
static bool
|
||||
ecma_get_already_resolved_bool_value (ecma_value_t already_resolved) /**< the alreadyResolved */
|
||||
{
|
||||
JERRY_ASSERT (ecma_is_value_object (already_resolved));
|
||||
|
||||
ecma_object_t *already_resolved_p = ecma_get_object_from_value (already_resolved);
|
||||
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) already_resolved_p;
|
||||
|
||||
JERRY_ASSERT (ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_BOOLEAN_UL);
|
||||
|
||||
return ext_object_p->u.class_prop.u.value == ECMA_VALUE_TRUE;
|
||||
} /* ecma_get_already_resolved_bool_value */
|
||||
|
||||
/**
|
||||
* Set the value of alreadyResolved.
|
||||
*/
|
||||
static void
|
||||
ecma_set_already_resolved_value (ecma_value_t already_resolved, /**< the alreadyResolved */
|
||||
bool value) /**< the value to set */
|
||||
{
|
||||
JERRY_ASSERT (ecma_is_value_object (already_resolved));
|
||||
|
||||
ecma_object_t *already_resolved_p = ecma_get_object_from_value (already_resolved);
|
||||
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) already_resolved_p;
|
||||
|
||||
JERRY_ASSERT (ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_BOOLEAN_UL);
|
||||
|
||||
ext_object_p->u.class_prop.u.value = ecma_make_boolean_value (value);
|
||||
} /* ecma_set_already_resolved_value */
|
||||
|
||||
/**
|
||||
* Take a collection of Reactions and enqueue a new PromiseReactionJob for each Reaction.
|
||||
*
|
||||
@ -146,13 +115,49 @@ ecma_set_already_resolved_value (ecma_value_t already_resolved, /**< the already
|
||||
*/
|
||||
static void
|
||||
ecma_promise_trigger_reactions (ecma_collection_t *reactions, /**< lists of reactions */
|
||||
ecma_value_t value) /**< value for resolve or reject */
|
||||
ecma_value_t value, /**< value for resolve or reject */
|
||||
bool is_reject) /**< true if promise is rejected, false otherwise */
|
||||
{
|
||||
ecma_value_t *buffer_p = reactions->buffer_p;
|
||||
ecma_value_t *buffer_end_p = buffer_p + reactions->item_count;
|
||||
|
||||
for (uint32_t i = 0; i < reactions->item_count; i++)
|
||||
while (buffer_p < buffer_end_p)
|
||||
{
|
||||
ecma_enqueue_promise_reaction_job (buffer_p[i], value);
|
||||
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);
|
||||
|
||||
if (!is_reject)
|
||||
{
|
||||
ecma_value_t handler = ECMA_VALUE_TRUE;
|
||||
|
||||
if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (capability_with_tag))
|
||||
{
|
||||
handler = *buffer_p++;
|
||||
}
|
||||
|
||||
ecma_enqueue_promise_reaction_job (capability, handler, value);
|
||||
}
|
||||
else if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (capability_with_tag))
|
||||
{
|
||||
buffer_p++;
|
||||
}
|
||||
|
||||
if (is_reject)
|
||||
{
|
||||
ecma_value_t handler = ECMA_VALUE_FALSE;
|
||||
|
||||
if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (capability_with_tag))
|
||||
{
|
||||
handler = *buffer_p++;
|
||||
}
|
||||
|
||||
ecma_enqueue_promise_reaction_job (capability, handler, value);
|
||||
}
|
||||
else if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (capability_with_tag))
|
||||
{
|
||||
buffer_p++;
|
||||
}
|
||||
}
|
||||
} /* ecma_promise_trigger_reactions */
|
||||
|
||||
@ -167,25 +172,22 @@ ecma_reject_promise (ecma_value_t promise, /**< promise */
|
||||
{
|
||||
ecma_object_t *obj_p = ecma_get_object_from_value (promise);
|
||||
|
||||
JERRY_ASSERT (ecma_promise_get_state (obj_p) == ECMA_PROMISE_STATE_PENDING);
|
||||
JERRY_ASSERT (ecma_promise_get_flags (obj_p) & ECMA_PROMISE_IS_PENDING);
|
||||
|
||||
ecma_promise_set_state (obj_p, ECMA_PROMISE_STATE_REJECTED);
|
||||
ecma_promise_set_state (obj_p, false);
|
||||
ecma_promise_set_result (obj_p, ecma_copy_value_if_not_object (reason));
|
||||
ecma_promise_object_t *promise_p = (ecma_promise_object_t *) obj_p;
|
||||
|
||||
/* GC can be triggered by ecma_new_collection so freeing the collection
|
||||
first and creating a new one might cause a heap after use event. */
|
||||
ecma_collection_t *reject_reactions = promise_p->reject_reactions;
|
||||
ecma_collection_t *fulfill_reactions = promise_p->fulfill_reactions;
|
||||
ecma_collection_t *reactions = promise_p->reactions;
|
||||
|
||||
/* Fulfill reactions will never be triggered. */
|
||||
ecma_promise_trigger_reactions (reject_reactions, reason);
|
||||
ecma_promise_trigger_reactions (reactions, reason, true);
|
||||
|
||||
promise_p->reject_reactions = ecma_new_collection ();
|
||||
promise_p->fulfill_reactions = ecma_new_collection ();
|
||||
promise_p->reactions = ecma_new_collection ();
|
||||
|
||||
ecma_collection_free_if_not_object (reject_reactions);
|
||||
ecma_collection_free_if_not_object (fulfill_reactions);
|
||||
ecma_collection_destroy (reactions);
|
||||
} /* ecma_reject_promise */
|
||||
|
||||
/**
|
||||
@ -199,25 +201,22 @@ ecma_fulfill_promise (ecma_value_t promise, /**< promise */
|
||||
{
|
||||
ecma_object_t *obj_p = ecma_get_object_from_value (promise);
|
||||
|
||||
JERRY_ASSERT (ecma_promise_get_state (obj_p) == ECMA_PROMISE_STATE_PENDING);
|
||||
JERRY_ASSERT (ecma_promise_get_flags (obj_p) & ECMA_PROMISE_IS_PENDING);
|
||||
|
||||
ecma_promise_set_state (obj_p, ECMA_PROMISE_STATE_FULFILLED);
|
||||
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;
|
||||
|
||||
/* GC can be triggered by ecma_new_collection so freeing the collection
|
||||
first and creating a new one might cause a heap after use event. */
|
||||
ecma_collection_t *reject_reactions = promise_p->reject_reactions;
|
||||
ecma_collection_t *fulfill_reactions = promise_p->fulfill_reactions;
|
||||
ecma_collection_t *reactions = promise_p->reactions;
|
||||
|
||||
/* Reject reactions will never be triggered. */
|
||||
ecma_promise_trigger_reactions (fulfill_reactions, value);
|
||||
ecma_promise_trigger_reactions (reactions, value, false);
|
||||
|
||||
promise_p->reject_reactions = ecma_new_collection ();
|
||||
promise_p->fulfill_reactions = ecma_new_collection ();
|
||||
promise_p->reactions = ecma_new_collection ();
|
||||
|
||||
ecma_collection_free_if_not_object (reject_reactions);
|
||||
ecma_collection_free_if_not_object (fulfill_reactions);
|
||||
ecma_collection_destroy (reactions);
|
||||
} /* ecma_fulfill_promise */
|
||||
|
||||
/**
|
||||
@ -235,33 +234,27 @@ ecma_promise_reject_handler (const ecma_value_t function, /**< the function itse
|
||||
{
|
||||
JERRY_UNUSED (this);
|
||||
|
||||
ecma_string_t *str_promise_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE);
|
||||
ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED);
|
||||
|
||||
ecma_object_t *function_p = ecma_get_object_from_value (function);
|
||||
/* 2. */
|
||||
ecma_value_t promise = ecma_op_object_get (function_p, str_promise_p);
|
||||
ecma_value_t promise = ecma_op_object_get_by_magic_id (function_p, LIT_INTERNAL_MAGIC_STRING_PROMISE);
|
||||
/* 1. */
|
||||
JERRY_ASSERT (ecma_is_promise (ecma_get_object_from_value (promise)));
|
||||
/* 3. */
|
||||
ecma_value_t already_resolved = ecma_op_object_get (function_p, str_already_resolved_p);
|
||||
ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise);
|
||||
JERRY_ASSERT (ecma_is_promise (promise_obj_p));
|
||||
|
||||
/* 4. */
|
||||
if (ecma_get_already_resolved_bool_value (already_resolved))
|
||||
/* 3., 4. */
|
||||
if (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED)
|
||||
{
|
||||
ecma_free_value (already_resolved);
|
||||
ecma_free_value (promise);
|
||||
return ECMA_VALUE_UNDEFINED;
|
||||
}
|
||||
|
||||
/* 5. */
|
||||
ecma_set_already_resolved_value (already_resolved, true);
|
||||
((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED;
|
||||
|
||||
/* 6. */
|
||||
ecma_value_t reject_value = (argc == 0) ? ECMA_VALUE_UNDEFINED : argv[0];
|
||||
ecma_reject_promise (promise, reject_value);
|
||||
ecma_free_value (promise);
|
||||
ecma_free_value (already_resolved);
|
||||
return ECMA_VALUE_UNDEFINED;
|
||||
} /* ecma_promise_reject_handler */
|
||||
|
||||
@ -280,25 +273,21 @@ ecma_promise_resolve_handler (const ecma_value_t function, /**< the function its
|
||||
{
|
||||
JERRY_UNUSED (this);
|
||||
|
||||
ecma_string_t *str_promise_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE);
|
||||
ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED);
|
||||
|
||||
ecma_object_t *function_p = ecma_get_object_from_value (function);
|
||||
/* 2. */
|
||||
ecma_value_t promise = ecma_op_object_get (function_p, str_promise_p);
|
||||
ecma_value_t promise = ecma_op_object_get_by_magic_id (function_p, LIT_INTERNAL_MAGIC_STRING_PROMISE);
|
||||
/* 1. */
|
||||
JERRY_ASSERT (ecma_is_promise (ecma_get_object_from_value (promise)));
|
||||
/* 3. */
|
||||
ecma_value_t already_resolved = ecma_op_object_get (function_p, str_already_resolved_p);
|
||||
ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise);
|
||||
JERRY_ASSERT (ecma_is_promise (promise_obj_p));
|
||||
|
||||
/* 4. */
|
||||
if (ecma_get_already_resolved_bool_value (already_resolved))
|
||||
/* 3., 4. */
|
||||
if (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED)
|
||||
{
|
||||
goto end_of_resolve_function;
|
||||
}
|
||||
|
||||
/* 5. */
|
||||
ecma_set_already_resolved_value (already_resolved, true);
|
||||
((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)
|
||||
@ -348,12 +337,11 @@ ecma_promise_resolve_handler (const ecma_value_t function, /**< the function its
|
||||
|
||||
end_of_resolve_function:
|
||||
ecma_free_value (promise);
|
||||
ecma_free_value (already_resolved);
|
||||
return ECMA_VALUE_UNDEFINED;
|
||||
} /* ecma_promise_resolve_handler */
|
||||
|
||||
/**
|
||||
* CapabilityiesExecutor Function.
|
||||
* CapabilitiesExecutor Function.
|
||||
*
|
||||
* See also: ES2015 25.4.1.5.1
|
||||
*
|
||||
@ -416,13 +404,11 @@ ecma_call_builtin_executor (ecma_object_t *executor_p, /**< the executor object
|
||||
*
|
||||
* @return pointer to the resolving function
|
||||
*/
|
||||
static ecma_object_t *
|
||||
static ecma_value_t
|
||||
ecma_promise_create_resolving_functions_helper (ecma_object_t *obj_p, /**< Promise Object */
|
||||
ecma_value_t already_resolved, /**< AlreadyResolved property */
|
||||
ecma_external_handler_t handler_cb) /**< Callback handler */
|
||||
{
|
||||
ecma_string_t *str_promise_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE);
|
||||
ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED);
|
||||
ecma_object_t *func_obj_p = ecma_op_create_external_function_object (handler_cb);
|
||||
|
||||
ecma_op_object_put (func_obj_p,
|
||||
@ -430,12 +416,7 @@ ecma_promise_create_resolving_functions_helper (ecma_object_t *obj_p, /**< Promi
|
||||
ecma_make_object_value (obj_p),
|
||||
false);
|
||||
|
||||
ecma_op_object_put (func_obj_p,
|
||||
str_already_resolved_p,
|
||||
already_resolved,
|
||||
false);
|
||||
|
||||
return func_obj_p;
|
||||
return ecma_make_object_value (func_obj_p);
|
||||
} /* ecma_promise_create_resolving_functions_helper */
|
||||
|
||||
/**
|
||||
@ -445,29 +426,17 @@ ecma_promise_create_resolving_functions_helper (ecma_object_t *obj_p, /**< Promi
|
||||
*
|
||||
* @return pointer to the resolving functions
|
||||
*/
|
||||
ecma_promise_resolving_functions_t *
|
||||
ecma_promise_create_resolving_functions (ecma_object_t *object_p) /**< the promise object */
|
||||
void
|
||||
ecma_promise_create_resolving_functions (ecma_object_t *object_p, /**< the promise object */
|
||||
ecma_promise_resolving_functions_t *funcs) /**< [out] resolving functions */
|
||||
{
|
||||
/* 1. */
|
||||
ecma_value_t already_resolved = ecma_op_create_boolean_object (ECMA_VALUE_FALSE);
|
||||
|
||||
/* 2. - 4. */
|
||||
ecma_object_t *resolve_p = ecma_promise_create_resolving_functions_helper (object_p,
|
||||
already_resolved,
|
||||
ecma_promise_resolve_handler);
|
||||
funcs->resolve = ecma_promise_create_resolving_functions_helper (object_p,
|
||||
ecma_promise_resolve_handler);
|
||||
|
||||
/* 5. - 7. */
|
||||
ecma_object_t *reject_p = ecma_promise_create_resolving_functions_helper (object_p,
|
||||
already_resolved,
|
||||
ecma_promise_reject_handler);
|
||||
|
||||
/* 8. */
|
||||
ecma_promise_resolving_functions_t *funcs = jmem_heap_alloc_block (sizeof (ecma_promise_resolving_functions_t));
|
||||
funcs->resolve = ecma_make_object_value (resolve_p);
|
||||
funcs->reject = ecma_make_object_value (reject_p);
|
||||
|
||||
ecma_free_value (already_resolved);
|
||||
return funcs;
|
||||
funcs->reject = ecma_promise_create_resolving_functions_helper (object_p,
|
||||
ecma_promise_reject_handler);
|
||||
} /* ecma_promise_create_resolving_functions */
|
||||
|
||||
/**
|
||||
@ -478,7 +447,6 @@ ecma_promise_free_resolving_functions (ecma_promise_resolving_functions_t *funcs
|
||||
{
|
||||
ecma_free_value (funcs->resolve);
|
||||
ecma_free_value (funcs->reject);
|
||||
jmem_heap_free_block (funcs, sizeof (ecma_promise_resolving_functions_t));
|
||||
} /* ecma_promise_free_resolving_functions */
|
||||
|
||||
/**
|
||||
@ -503,35 +471,37 @@ ecma_op_create_promise_object (ecma_value_t executor, /**< the executor function
|
||||
return ECMA_VALUE_ERROR;
|
||||
}
|
||||
|
||||
/* Calling ecma_new_collection might trigger a GC call, so this
|
||||
* allocation is performed before the object is constructed. */
|
||||
ecma_collection_t *reactions = ecma_new_collection ();
|
||||
|
||||
ecma_object_t *object_p = ecma_create_object (proto_p,
|
||||
sizeof (ecma_promise_object_t),
|
||||
ECMA_OBJECT_TYPE_CLASS);
|
||||
ecma_deref_object (proto_p);
|
||||
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
|
||||
ext_object_p->u.class_prop.class_id = LIT_MAGIC_STRING_PROMISE_UL;
|
||||
/* 5 */
|
||||
ext_object_p->u.class_prop.extra_info = ECMA_PROMISE_IS_PENDING;
|
||||
ext_object_p->u.class_prop.u.value = ECMA_VALUE_UNDEFINED;
|
||||
ecma_promise_object_t *promise_object_p = (ecma_promise_object_t *) object_p;
|
||||
promise_object_p->fulfill_reactions = NULL;
|
||||
promise_object_p->reject_reactions = NULL;
|
||||
|
||||
/* 5 */
|
||||
ecma_promise_set_state (object_p, ECMA_PROMISE_STATE_PENDING);
|
||||
/* 6-7. */
|
||||
promise_object_p->fulfill_reactions = ecma_new_collection ();
|
||||
promise_object_p->reject_reactions = ecma_new_collection ();
|
||||
promise_object_p->reactions = reactions;
|
||||
/* 8. */
|
||||
ecma_promise_resolving_functions_t *funcs = ecma_promise_create_resolving_functions (object_p);
|
||||
ecma_promise_resolving_functions_t funcs;
|
||||
ecma_promise_create_resolving_functions (object_p, &funcs);
|
||||
|
||||
ecma_string_t *str_resolve_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_RESOLVE_FUNCTION);
|
||||
ecma_string_t *str_reject_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_REJECT_FUNCTION);
|
||||
|
||||
ecma_op_object_put (object_p,
|
||||
str_resolve_p,
|
||||
funcs->resolve,
|
||||
funcs.resolve,
|
||||
false);
|
||||
ecma_op_object_put (object_p,
|
||||
str_reject_p,
|
||||
funcs->reject,
|
||||
funcs.reject,
|
||||
false);
|
||||
|
||||
/* 9. */
|
||||
@ -541,7 +511,7 @@ ecma_op_create_promise_object (ecma_value_t executor, /**< the executor function
|
||||
{
|
||||
JERRY_ASSERT (ecma_op_is_callable (executor));
|
||||
|
||||
ecma_value_t argv[] = { funcs->resolve, funcs->reject };
|
||||
ecma_value_t argv[] = { funcs.resolve, funcs.reject };
|
||||
completion = ecma_op_function_call (ecma_get_object_from_value (executor),
|
||||
ECMA_VALUE_UNDEFINED,
|
||||
argv,
|
||||
@ -552,8 +522,8 @@ ecma_op_create_promise_object (ecma_value_t executor, /**< the executor function
|
||||
JERRY_ASSERT (ecma_is_value_object (executor));
|
||||
|
||||
completion = ecma_call_builtin_executor (ecma_get_object_from_value (executor),
|
||||
funcs->resolve,
|
||||
funcs->reject);
|
||||
funcs.resolve,
|
||||
funcs.reject);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -567,13 +537,13 @@ ecma_op_create_promise_object (ecma_value_t executor, /**< the executor function
|
||||
{
|
||||
/* 10.a. */
|
||||
completion = jcontext_take_exception ();
|
||||
status = ecma_op_function_call (ecma_get_object_from_value (funcs->reject),
|
||||
status = ecma_op_function_call (ecma_get_object_from_value (funcs.reject),
|
||||
ECMA_VALUE_UNDEFINED,
|
||||
&completion,
|
||||
1);
|
||||
}
|
||||
|
||||
ecma_promise_free_resolving_functions (funcs);
|
||||
ecma_promise_free_resolving_functions (&funcs);
|
||||
ecma_free_value (completion);
|
||||
|
||||
/* 10.b. */
|
||||
@ -830,8 +800,6 @@ ecma_promise_do_then (ecma_value_t promise, /**< the promise which call 'then' *
|
||||
ecma_value_t on_rejected, /**< on_rejected function */
|
||||
ecma_value_t result_capability) /**< promise capability */
|
||||
{
|
||||
ecma_string_t *capability_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_CAPABILITY);
|
||||
ecma_string_t *handler_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_HANDLER);
|
||||
ecma_string_t *promise_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE);
|
||||
|
||||
/* 3. boolean true indicates "indentity" */
|
||||
@ -846,57 +814,56 @@ ecma_promise_do_then (ecma_value_t promise, /**< the promise which call 'then' *
|
||||
on_rejected = ECMA_VALUE_FALSE;
|
||||
}
|
||||
|
||||
/* 5-6. */
|
||||
ecma_object_t *fulfill_reaction_p = ecma_op_create_object_object_noarg ();
|
||||
ecma_object_t *reject_reaction_p = ecma_op_create_object_object_noarg ();
|
||||
ecma_op_object_put (fulfill_reaction_p,
|
||||
capability_str_p,
|
||||
result_capability,
|
||||
false);
|
||||
ecma_op_object_put (fulfill_reaction_p,
|
||||
handler_str_p,
|
||||
on_fulfilled,
|
||||
false);
|
||||
ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise);
|
||||
ecma_promise_object_t *promise_p = (ecma_promise_object_t *) promise_obj_p;
|
||||
|
||||
ecma_op_object_put (reject_reaction_p,
|
||||
capability_str_p,
|
||||
result_capability,
|
||||
false);
|
||||
ecma_op_object_put (reject_reaction_p,
|
||||
handler_str_p,
|
||||
on_rejected,
|
||||
false);
|
||||
uint16_t flags = ecma_promise_get_flags (promise_obj_p);
|
||||
|
||||
ecma_object_t *obj_p = ecma_get_object_from_value (promise);
|
||||
ecma_promise_object_t *promise_p = (ecma_promise_object_t *) obj_p;
|
||||
|
||||
if (ecma_promise_get_state (obj_p) == ECMA_PROMISE_STATE_PENDING)
|
||||
if (flags & ECMA_PROMISE_IS_PENDING)
|
||||
{
|
||||
/* 7. */
|
||||
ecma_collection_push_back (promise_p->fulfill_reactions, ecma_make_object_value (fulfill_reaction_p));
|
||||
ecma_collection_push_back (promise_p->reject_reactions, ecma_make_object_value (reject_reaction_p));
|
||||
ecma_value_t capability_with_tag;
|
||||
ECMA_SET_NON_NULL_POINTER_TAG (capability_with_tag, ecma_get_object_from_value (result_capability), 0);
|
||||
|
||||
if (on_fulfilled != ECMA_VALUE_TRUE)
|
||||
{
|
||||
ECMA_SET_FIRST_BIT_TO_POINTER_TAG (capability_with_tag);
|
||||
}
|
||||
|
||||
if (on_rejected != ECMA_VALUE_FALSE)
|
||||
{
|
||||
ECMA_SET_SECOND_BIT_TO_POINTER_TAG (capability_with_tag);
|
||||
}
|
||||
|
||||
ecma_collection_push_back (promise_p->reactions, capability_with_tag);
|
||||
|
||||
if (on_fulfilled != ECMA_VALUE_TRUE)
|
||||
{
|
||||
ecma_collection_push_back (promise_p->reactions, on_fulfilled);
|
||||
}
|
||||
|
||||
if (on_rejected != ECMA_VALUE_FALSE)
|
||||
{
|
||||
ecma_collection_push_back (promise_p->reactions, on_rejected);
|
||||
}
|
||||
}
|
||||
else if (ecma_promise_get_state (obj_p) == ECMA_PROMISE_STATE_FULFILLED)
|
||||
else if (flags & ECMA_PROMISE_IS_FULFILLED)
|
||||
{
|
||||
/* 8. */
|
||||
ecma_value_t value = ecma_promise_get_result (obj_p);
|
||||
ecma_enqueue_promise_reaction_job (ecma_make_object_value (fulfill_reaction_p), value);
|
||||
ecma_value_t value = ecma_promise_get_result (promise_obj_p);
|
||||
ecma_enqueue_promise_reaction_job (result_capability, on_fulfilled, value);
|
||||
ecma_free_value (value);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 9. */
|
||||
ecma_value_t reason = ecma_promise_get_result (obj_p);
|
||||
ecma_enqueue_promise_reaction_job (ecma_make_object_value (reject_reaction_p), reason);
|
||||
ecma_value_t reason = ecma_promise_get_result (promise_obj_p);
|
||||
ecma_enqueue_promise_reaction_job (result_capability, on_rejected, reason);
|
||||
ecma_free_value (reason);
|
||||
}
|
||||
|
||||
/* 10. */
|
||||
ecma_value_t ret = ecma_op_object_get (ecma_get_object_from_value (result_capability), promise_str_p);
|
||||
|
||||
ecma_deref_object (fulfill_reaction_p);
|
||||
ecma_deref_object (reject_reaction_p);
|
||||
return ret;
|
||||
return ecma_op_object_get (ecma_get_object_from_value (result_capability), promise_str_p);
|
||||
} /* ecma_promise_do_then */
|
||||
|
||||
/**
|
||||
|
||||
@ -31,11 +31,10 @@
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
ECMA_PROMISE_STATE_PENDING, /**< pending state */
|
||||
ECMA_PROMISE_STATE_FULFILLED, /**< fulfilled state */
|
||||
ECMA_PROMISE_STATE_REJECTED, /** rejected state */
|
||||
ECMA_PROMISE_STATE__COUNT /**< number of states */
|
||||
} ecma_promise_state_t;
|
||||
ECMA_PROMISE_IS_PENDING = (1 << 0), /**< pending state */
|
||||
ECMA_PROMISE_IS_FULFILLED = (1 << 1), /**< fulfilled state */
|
||||
ECMA_PROMISE_ALREADY_RESOLVED = (1 << 2), /**< already resolved */
|
||||
} ecma_promise_flags_t;
|
||||
|
||||
/**
|
||||
* Indicates the type of the executor in promise construct.
|
||||
@ -62,20 +61,35 @@ typedef struct
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ecma_extended_object_t ecma_extended_object_t; /**< extended object part */
|
||||
uint8_t state; /**< promise state, see ecma_promise_state_t */
|
||||
ecma_collection_t *fulfill_reactions; /**< list of PromiseFullfillReactions */
|
||||
ecma_collection_t *reject_reactions; /**< list of PromiseRejectReactions */
|
||||
ecma_extended_object_t header; /**< extended object part */
|
||||
ecma_collection_t *reactions; /**< list of promise reactions */
|
||||
} ecma_promise_object_t;
|
||||
|
||||
/* The Promise reaction is a compressed structure, where each item can
|
||||
* be a sequence of up to three ecma object values as seen below:
|
||||
*
|
||||
* [ Capability ][ Optional fullfilled callback ][ Optional rejected callback ]
|
||||
* [ Async function callback ]
|
||||
*
|
||||
* The first member is an object, which lower bits specify the type of the reaction:
|
||||
* bit 2 is not set: callback reactions
|
||||
* The first two objects specify the resolve/reject functions of the promise
|
||||
* returned by the `then` operation which can be used to chain event handlers.
|
||||
*
|
||||
* bit 0: has a fullfilled callback
|
||||
* bit 1: has a rejected callback
|
||||
*
|
||||
* bit 2 is set: async function callback
|
||||
*/
|
||||
|
||||
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);
|
||||
uint8_t ecma_promise_get_state (ecma_object_t *promise_p);
|
||||
uint16_t ecma_promise_get_flags (ecma_object_t *promise_p);
|
||||
ecma_value_t ecma_promise_get_result (ecma_object_t *promise_p);
|
||||
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);
|
||||
ecma_promise_resolving_functions_t *ecma_promise_create_resolving_functions (ecma_object_t *object_p);
|
||||
void ecma_promise_create_resolving_functions (ecma_object_t *object_p, ecma_promise_resolving_functions_t *funcs);
|
||||
void ecma_promise_free_resolving_functions (ecma_promise_resolving_functions_t *funcs);
|
||||
|
||||
/**
|
||||
|
||||
@ -177,8 +177,8 @@ struct jerry_context_t
|
||||
#endif /* ENABLED (JERRY_BUILTIN_REGEXP) */
|
||||
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
|
||||
ecma_job_queueitem_t *job_queue_head_p; /**< points to the head item of the jobqueue */
|
||||
ecma_job_queueitem_t *job_queue_tail_p; /**< points to the tail item of the jobqueue*/
|
||||
ecma_job_queue_item_t *job_queue_head_p; /**< points to the head item of the job queue */
|
||||
ecma_job_queue_item_t *job_queue_tail_p; /**< points to the tail item of the job queue */
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */
|
||||
|
||||
#if ENABLED (JERRY_VM_EXEC_STOP)
|
||||
|
||||
@ -34,14 +34,12 @@ typedef enum
|
||||
LIT_NON_INTERNAL_MAGIC_STRING__COUNT, /**< number of non-internal magic strings */
|
||||
LIT_INTERNAL_MAGIC_STRING_PROMISE = LIT_NON_INTERNAL_MAGIC_STRING__COUNT, /**< [[Promise]] of promise
|
||||
* reject or resolve functions */
|
||||
LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED, /**< [[AlreadyResolved]] of promise reject or resolve functions */
|
||||
LIT_INTERNAL_MAGIC_STRING_RESOLVE_FUNCTION, /**< the resolve funtion of the promise object */
|
||||
LIT_INTERNAL_MAGIC_STRING_REJECT_FUNCTION, /**< the reject function of the promise object */
|
||||
LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE, /**< [[Promise]] property */
|
||||
LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE, /**< [[Resolve]] property */
|
||||
LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT, /**< [[Reject]] property */
|
||||
LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_CAPABILITY, /**< [[Capability]] property */
|
||||
LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_HANDLER, /**< [[Handler]] property */
|
||||
LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_ALREADY_CALLED, /**< [[AlreadyCalled]] property */
|
||||
LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_INDEX, /**< [[Index]] property */
|
||||
LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_VALUE, /**< [[Values]] property */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user