From 1105b43c22c06363cf876383bd6a3762b20bc521 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Mon, 25 May 2020 18:00:43 +0200 Subject: [PATCH] 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 --- jerry-core/api/jerry.c | 17 +- jerry-core/ecma/base/ecma-gc.c | 31 +- jerry-core/ecma/operations/ecma-jobqueue.c | 191 ++++++----- jerry-core/ecma/operations/ecma-jobqueue.h | 18 +- .../ecma/operations/ecma-promise-object.c | 299 ++++++++---------- .../ecma/operations/ecma-promise-object.h | 36 ++- jerry-core/jcontext/jcontext.h | 4 +- jerry-core/lit/lit-magic-strings.h | 2 - 8 files changed, 298 insertions(+), 300 deletions(-) diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 786b2a09d..ae904bd3b 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -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; diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 014e35b55..d828598de 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -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; } diff --git a/jerry-core/ecma/operations/ecma-jobqueue.c b/jerry-core/ecma/operations/ecma-jobqueue.c index 30dcb5b70..1b3b49156 100644 --- a/jerry-core/ecma/operations/ecma-jobqueue.c +++ b/jerry-core/ecma/operations/ecma-jobqueue.c @@ -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 */ diff --git a/jerry-core/ecma/operations/ecma-jobqueue.h b/jerry-core/ecma/operations/ecma-jobqueue.h index 260776823..5436fa4d9 100644 --- a/jerry-core/ecma/operations/ecma-jobqueue.h +++ b/jerry-core/ecma/operations/ecma-jobqueue.h @@ -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); diff --git a/jerry-core/ecma/operations/ecma-promise-object.c b/jerry-core/ecma/operations/ecma-promise-object.c index 182f0c20a..a50f24f8f 100644 --- a/jerry-core/ecma/operations/ecma-promise-object.c +++ b/jerry-core/ecma/operations/ecma-promise-object.c @@ -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 */ /** diff --git a/jerry-core/ecma/operations/ecma-promise-object.h b/jerry-core/ecma/operations/ecma-promise-object.h index 09b9bd3a7..0c4f9336d 100644 --- a/jerry-core/ecma/operations/ecma-promise-object.h +++ b/jerry-core/ecma/operations/ecma-promise-object.h @@ -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); /** diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index ba3bf376a..698450f48 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -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) diff --git a/jerry-core/lit/lit-magic-strings.h b/jerry-core/lit/lit-magic-strings.h index 6ca3f91ed..14e817035 100644 --- a/jerry-core/lit/lit-magic-strings.h +++ b/jerry-core/lit/lit-magic-strings.h @@ -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 */