diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 06b2a2e58..a1aee15ec 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -757,6 +757,42 @@ jerry_eval (const jerry_char_t *source_p, - [jerry_create_external_function](#jerry_create_external_function) - [jerry_external_handler_t](#jerry_external_handler_t) +## jerry_run_all_enqueued_jobs + +**Summary** + +Run enqueued Promise jobs until the first thrown error or until all get executed. + +**Prototype** + +```c +jerry_value_t +jerry_run_all_enqueued_jobs (void) +``` + +- return value - result of last executed job, may be error value. + +**Example** + +```c +{ + jerry_init (JERRY_INIT_EMPTY); + + const jerry_char_t script[] = "new Promise(function(f,r) { f('Hello, World!'); }).then(function(x) { print(x); });"; + size_t script_size = strlen ((const char *) script); + + jerry_value_t parsed_code = jerry_parse (script, script_size, false); + jerry_value_t script_value = jerry_run (parsed_code); + jerry_value_t job_value = jerry_run_all_enqueued_jobs (); + + jerry_release_value (job_value); + jerry_release_value (script_value); + jerry_release_value (parsed_code); + + jerry_cleanup (); +} +``` + # Get the global context diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 1e42452a4..45ccdaa64 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -432,6 +432,26 @@ jerry_eval (const jerry_char_t *source_p, /**< source code */ is_strict); } /* jerry_eval */ +/** + * Run enqueued Promise jobs until the first thrown error or until all get executed. + * + * Note: + * returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return result of last executed job, may be error value. + */ +jerry_value_t +jerry_run_all_enqueued_jobs (void) +{ + jerry_assert_api_available (); + +#ifndef CONFIG_DISABLE_ES2015_PROMISE_BUILTIN + return ecma_process_all_enqueued_jobs (); +#else /* CONFIG_DISABLE_ES2015_PROMISE_BUILTIN */ + return ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); +#endif /* CONFIG_DISABLE_ES2015_PROMISE_BUILTIN */ +} /* jerry_run_all_enqueued_jobs */ + /** * Get global object * diff --git a/jerry-core/ecma/base/ecma-init-finalize.c b/jerry-core/ecma/base/ecma-init-finalize.c index fc2ba709b..95b08d49b 100644 --- a/jerry-core/ecma/base/ecma-init-finalize.c +++ b/jerry-core/ecma/base/ecma-init-finalize.c @@ -46,6 +46,9 @@ ecma_init (void) JERRY_CONTEXT (ecma_prop_hashmap_alloc_last_is_hs_gc) = false; #endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ +#ifndef CONFIG_DISABLE_ES2015_PROMISE_BUILTIN + ecma_job_queue_init (); +#endif /* CONFIG_DISABLE_ES2015_PROMISE_BUILTIN */ } /* ecma_init */ /** diff --git a/jerry-core/ecma/operations/ecma-jobqueue.c b/jerry-core/ecma/operations/ecma-jobqueue.c index 1fc69fa3b..f7bbcbd4e 100644 --- a/jerry-core/ecma/operations/ecma-jobqueue.c +++ b/jerry-core/ecma/operations/ecma-jobqueue.c @@ -19,7 +19,7 @@ #include "ecma-jobqueue.h" #include "ecma-objects.h" #include "ecma-promise-object.h" -#include "jerryscript-port.h" +#include "jcontext.h" #ifndef CONFIG_DISABLE_ES2015_PROMISE_BUILTIN @@ -49,6 +49,15 @@ typedef struct ecma_value_t then; /** 'then' function */ } ecma_job_promise_resolve_thenable_t; +/** + * Initialize the jobqueue. + */ +void ecma_job_queue_init (void) +{ + JERRY_CONTEXT (job_queue_head_p) = NULL; + JERRY_CONTEXT (job_queue_tail_p) = NULL; +} /* ecma_job_queue_init */ + /** * Create a PromiseReactionJob. * @@ -257,18 +266,42 @@ ecma_process_promise_resolve_thenable_job (void *obj_p) /**< the job to be opera } /* ecma_process_promise_resolve_thenable_job */ /** - * Enqueue a PromiseReactionJob into a jobqueue. + * 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_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; + + if (JERRY_CONTEXT (job_queue_head_p) == NULL) + { + JERRY_CONTEXT (job_queue_head_p) = item_p; + JERRY_CONTEXT (job_queue_tail_p) = item_p; + } + else + { + JERRY_CONTEXT (job_queue_tail_p)->next_p = item_p; + JERRY_CONTEXT (job_queue_tail_p) = item_p; + } +} /* ecma_enqueue_job */ + +/** + * Enqueue a PromiseReactionJob into the jobqueue. */ void ecma_enqueue_promise_reaction_job (ecma_value_t reaction, /**< PromiseReaction */ ecma_value_t argument) /**< argument for the reaction */ { ecma_job_promise_reaction_t *job_p = ecma_create_promise_reaction_job (reaction, argument); - jerry_port_jobqueue_enqueue (ecma_process_promise_reaction_job, job_p); + ecma_enqueue_job (ecma_process_promise_reaction_job, job_p); } /* ecma_enqueue_promise_reaction_job */ /** - * Enqueue a PromiseResolveThenableJob into a jobqueue. + * Enqueue a PromiseResolveThenableJob into the jobqueue. */ void ecma_enqueue_promise_resolve_thenable_job (ecma_value_t promise, /**< promise to be resolved */ @@ -278,9 +311,37 @@ ecma_enqueue_promise_resolve_thenable_job (ecma_value_t promise, /**< promise to ecma_job_promise_resolve_thenable_t *job_p = ecma_create_promise_resolve_thenable_job (promise, thenable, then); - jerry_port_jobqueue_enqueue (ecma_process_promise_resolve_thenable_job, job_p); + ecma_enqueue_job (ecma_process_promise_resolve_thenable_job, job_p); } /* ecma_enqueue_promise_resolve_thenable_job */ +/** + * Process enqueued Promise jobs until the first thrown error or until the + * jobqueue becomes empty. + * + * @return result of the last processed job - if the jobqueue was non-empty, + * undefined - otherwise. + */ +ecma_value_t +ecma_process_all_enqueued_jobs (void) +{ + ecma_value_t ret = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); + + 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; + + 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_free_value (ret); + ret = handler (job_p); + } + + return ret; +} /* ecma_process_all_enqueued_jobs */ + /** * @} * @} diff --git a/jerry-core/ecma/operations/ecma-jobqueue.h b/jerry-core/ecma/operations/ecma-jobqueue.h index 41f61585a..76dcb4b2e 100644 --- a/jerry-core/ecma/operations/ecma-jobqueue.h +++ b/jerry-core/ecma/operations/ecma-jobqueue.h @@ -25,9 +25,28 @@ * @{ */ +/** + * Jerry job handler function type + */ +typedef ecma_value_t (*ecma_job_handler_t) (void *job_p); + +/** + * Description of the job queue item. + */ +typedef struct ecma_job_queueitem_t +{ + 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; + +void ecma_job_queue_init (void); + void ecma_enqueue_promise_reaction_job (ecma_value_t reaction, ecma_value_t argument); void ecma_enqueue_promise_resolve_thenable_job (ecma_value_t promise, ecma_value_t thenable, ecma_value_t then); +ecma_value_t ecma_process_all_enqueued_jobs (void); + /** * @} * @} diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index f50f042d7..462254e7c 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -237,6 +237,8 @@ jerry_value_t jerry_parse_named_resource (const jerry_char_t *name_p, size_t nam jerry_value_t jerry_run (const jerry_value_t func_val); jerry_value_t jerry_eval (const jerry_char_t *source_p, size_t source_size, bool is_strict); +jerry_value_t jerry_run_all_enqueued_jobs (void); + /** * Get the global context. */ diff --git a/jerry-core/include/jerryscript-port.h b/jerry-core/include/jerryscript-port.h index 69a91ff98..d4558d236 100644 --- a/jerry-core/include/jerryscript-port.h +++ b/jerry-core/include/jerryscript-port.h @@ -126,31 +126,6 @@ bool jerry_port_get_time_zone (jerry_time_zone_t *tz_p); */ double jerry_port_get_current_time (void); -/* - * JobQueue Port API - */ - -/** - * Jerry job handler function type - */ -typedef uint32_t (*jerry_job_handler_t) (void *); - -/** - * Enqueue a job described by a pair of function and data pointers. The port is - * expected to call the handler function with the given data at some (later) - * point of time. - * - * @param handler the pointer of the handler function associated with the job. - * @param job_p the data pointer to be passed to handler when called. - * - * Note: - * This port function is only called by the implementation of the Promise - * builtin (mandated by the ES2015 standard). If the engine is built with - * Promise disabled (e.g., with ES5.1 profile), then the port does not have - * to implement this function. - */ -void jerry_port_jobqueue_enqueue (jerry_job_handler_t handler, void *job_p); - /** * @} */ diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index df24c9bd3..a69d99484 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -20,6 +20,7 @@ #define JCONTEXT_H #include "ecma-builtins.h" +#include "ecma-jobqueue.h" #include "jerry-debugger.h" #include "jmem.h" #include "re-bytecode.h" @@ -84,6 +85,11 @@ typedef struct uint8_t re_cache_idx; /**< evicted item index when regex cache is full (round-robin) */ #endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */ +#ifndef CONFIG_DISABLE_ES2015_PROMISE_BUILTIN + 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*/ +#endif /* CONFIG_DISABLE_ES2015_PROMISE_BUILTIN */ + #ifdef JERRY_VM_EXEC_STOP uint32_t vm_exec_stop_frequency; /**< reset value for vm_exec_stop_counter */ uint32_t vm_exec_stop_counter; /**< down counter for reducing the calls of vm_exec_stop_cb */ diff --git a/jerry-main/main-unix.c b/jerry-main/main-unix.c index de69173c9..6cb4af8a2 100644 --- a/jerry-main/main-unix.c +++ b/jerry-main/main-unix.c @@ -560,9 +560,6 @@ main (int argc, is_repl_mode = true; } -#ifndef CONFIG_DISABLE_ES2015_PROMISE_BUILTIN - jerry_port_default_jobqueue_init (); -#endif /* !CONFIG_DISABLE_ES2015_PROMISE_BUILTIN */ jerry_init (flags); register_js_function ("assert", jerryx_handler_assert); @@ -728,15 +725,13 @@ main (int argc, args, 1); jerry_release_value (ret_val_print); -#ifndef CONFIG_DISABLE_ES2015_PROMISE_BUILTIN jerry_release_value (ret_val_eval); - ret_val_eval = jerry_port_default_jobqueue_run (); + ret_val_eval = jerry_run_all_enqueued_jobs (); if (jerry_value_has_error_flag (ret_val_eval)) { print_unhandled_exception (ret_val_eval); } -#endif /* !CONFIG_DISABLE_ES2015_PROMISE_BUILTIN */ } else { @@ -756,11 +751,10 @@ main (int argc, ret_code = JERRY_STANDALONE_EXIT_CODE_FAIL; } -#ifndef CONFIG_DISABLE_ES2015_PROMISE_BUILTIN else { jerry_release_value (ret_value); - ret_value = jerry_port_default_jobqueue_run (); + ret_value = jerry_run_all_enqueued_jobs (); if (jerry_value_has_error_flag (ret_value)) { @@ -768,7 +762,6 @@ main (int argc, ret_code = JERRY_STANDALONE_EXIT_CODE_FAIL; } } -#endif /* !CONFIG_DISABLE_ES2015_PROMISE_BUILTIN */ jerry_release_value (ret_value); jerry_cleanup (); diff --git a/jerry-port/default/default-jobqueue.c b/jerry-port/default/default-jobqueue.c deleted file mode 100644 index 7659a1438..000000000 --- a/jerry-port/default/default-jobqueue.c +++ /dev/null @@ -1,131 +0,0 @@ -/* Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "jerryscript.h" -#include "jerryscript-port.h" -#include "jerryscript-port-default.h" -#include "jmem.h" -#include "jrt.h" - -typedef struct jerry_port_queueitem_t jerry_port_queueitem_t; - -/** - * Description of the queue item. - */ -struct jerry_port_queueitem_t -{ - jerry_port_queueitem_t *next_p; /**< points to next item */ - jerry_job_handler_t handler; /**< the handler for the job*/ - void *job_p; /**< points to the job */ -}; - -/** - * Description of a job queue (FIFO). - */ -typedef struct -{ - jerry_port_queueitem_t *head_p; /**< points to the head item of the queue */ - jerry_port_queueitem_t *tail_p; /**< points to the tail item of the queue*/ -} jerry_port_jobqueue_t; - -static jerry_port_jobqueue_t queue; - -/** - * Initialize the job queue. - */ -void jerry_port_default_jobqueue_init (void) -{ - queue.head_p = NULL; - queue.tail_p = NULL; -} /* jerry_port_default_jobqueue_init */ - -/** - * Enqueue a job. - */ -void jerry_port_jobqueue_enqueue (jerry_job_handler_t handler, /**< the handler for the job */ - void *job_p) /**< the job */ -{ - jerry_port_queueitem_t *item_p = jmem_heap_alloc_block (sizeof (jerry_port_queueitem_t)); - item_p->job_p = job_p; - item_p->handler = handler; - item_p->next_p = NULL; - - if (queue.head_p == NULL) - { - queue.head_p = item_p; - queue.tail_p = item_p; - - return; - } - - queue.tail_p->next_p = item_p; - queue.tail_p = item_p; -} /* jerry_port_jobqueue_enqueue */ - -/** - * Dequeue and get the job. - * - * @return pointer to jerry_port_queueitem_t. - * It should be freed with jmem_heap_free_block. - */ -static jerry_port_queueitem_t * -jerry_port_default_jobqueue_dequeue (void) -{ - if (queue.head_p == NULL) - { - return NULL; - } - - jerry_port_queueitem_t *item_p = queue.head_p; - queue.head_p = queue.head_p->next_p; - - return item_p; -} /* jerry_port_default_jobqueue_dequeue */ - -/** - * Start the jobqueue. - * - * @return jerry value. - * If exception happens in the handler, stop the queue - * and return the exception. - * Otherwise, return undefined. - */ -jerry_value_t -jerry_port_default_jobqueue_run (void) -{ - jerry_value_t ret; - - while (true) - { - jerry_port_queueitem_t *item_p = jerry_port_default_jobqueue_dequeue (); - - if (item_p == NULL) - { - return jerry_create_undefined (); - } - - void *job_p = item_p->job_p; - jerry_job_handler_t handler = item_p->handler; - jmem_heap_free_block (item_p, sizeof (jerry_port_queueitem_t)); - ret = handler (job_p); - - if (jerry_value_has_error_flag (ret)) - { - return ret; - } - - jerry_release_value (ret); - } -} /* jerry_port_default_jobqueue_run */ diff --git a/jerry-port/default/include/jerryscript-port-default.h b/jerry-port/default/include/jerryscript-port-default.h index a0ee59cd4..29ae6d032 100644 --- a/jerry-port/default/include/jerryscript-port-default.h +++ b/jerry-port/default/include/jerryscript-port-default.h @@ -37,9 +37,6 @@ bool jerry_port_default_is_abort_on_fail (void); jerry_log_level_t jerry_port_default_get_log_level (void); void jerry_port_default_set_log_level (jerry_log_level_t level); -void jerry_port_default_jobqueue_init (void); -jerry_value_t jerry_port_default_jobqueue_run (void); - /** * @} */ diff --git a/tests/unit-core/test-promise.c b/tests/unit-core/test-promise.c index 0f855be24..09b9e8040 100644 --- a/tests/unit-core/test-promise.c +++ b/tests/unit-core/test-promise.c @@ -117,7 +117,6 @@ register_js_function (const char *name_p, /**< name of the function */ int main (void) { - jerry_port_default_jobqueue_init (); jerry_init (JERRY_INIT_EMPTY); register_js_function ("create_promise1", create_promise1_handler); @@ -151,7 +150,7 @@ main (void) jerry_resolve_or_reject_promise (my_promise1, str_reject, false); /* Run the jobqueue. */ - res = jerry_port_default_jobqueue_run (); + res = jerry_run_all_enqueued_jobs (); TEST_ASSERT (!jerry_value_has_error_flag (res)); TEST_ASSERT (count_in_assert == 2);