From 1f2a8d4ac1051f7b64e52a9b2576f6095fdb34e9 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Fri, 16 Apr 2021 17:50:57 +0200 Subject: [PATCH] Add notification callback for module state changes (#4656) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- docs/02.API-REFERENCE.md | 105 +++++++++++++++++++++++++ jerry-core/api/jerry.c | 18 +++++ jerry-core/ecma/base/ecma-module.c | 49 ++++++++++-- jerry-core/include/jerryscript-core.h | 1 + jerry-core/include/jerryscript-types.h | 6 ++ jerry-core/jcontext/jcontext.h | 5 +- tests/unit-core/test-module.c | 74 +++++++++++++++++ 7 files changed, 252 insertions(+), 6 deletions(-) diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index e9b2fcf93..6fff04430 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -820,6 +820,32 @@ typedef jerry_value_t (*jerry_module_resolve_callback_t) (const jerry_value_t sp - [jerry_module_link](#jerry_module_link) - [jerry_get_global_object](#jerry_get_global_object) +## jerry_module_state_changed_callback_t + +**Summary** + +Callback which is called after the module enters into linked, evaluated or error state. + +**Prototype** + +```c +typedef void (*jerry_module_state_changed_callback_t) (jerry_module_state_t new_state, + const jerry_value_t module, + const jerry_value_t value, + void *user_p); +``` + +- `new_state` - new state of the module. +- `module` - a module whose state is changed +- `value` - depends on the state: undefined for linked, module script result for evaluated, + and error value for error state. +- `user_p` - pointer passed to [jerry_module_set_state_changed_callback](#jerry_module_set_state_changed_callback). + +*New in version [[NEXT_RELEASE]]*. + +**See also** +- [jerry_module_set_state_changed_callback](#jerry_module_set_state_changed_callback) + ## jerry_native_module_evaluate_callback_t **Summary** @@ -4558,6 +4584,85 @@ main (void) - [jerry_module_state_t](#jerry_module_state_t) +## jerry_module_set_state_changed_callback + +**Summary** + +Sets a callback which is called after a module state is changed to linked, evaluated, or error. + +*Notes*: +- This API depends on a build option (`JERRY_MODULE_SYSTEM`) and can be checked + in runtime with the `JERRY_FEATURE_MODULE` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). + +**Prototype** + +```c +void +jerry_module_set_state_changed_callback (jerry_module_state_changed_callback_t callback, + void *user_p) +``` + +- `callback` - callback, which is called after the state change. +- `module_val` - pointer passed to the callback function. + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # (test="compile") + +```c +#include +#include + +static void +module_state_changed (jerry_module_state_t new_state, /**< new state of the module */ + const jerry_value_t module_val, /**< a module whose state is changed */ + const jerry_value_t value, /**< value argument */ + void *user_p) /**< user pointer */ +{ + (void) module_val; + (void) value; + (void) user_p; + + if (new_state == JERRY_MODULE_STATE_LINKED) + { + printf ("A module is entered into linked state\n"); + } +} /* module_state_changed */ + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + const jerry_char_t script[] = "12"; + const jerry_char_t file[] = "a.mjs"; + + jerry_module_set_state_changed_callback (module_state_changed, NULL); + + jerry_parse_options_t parse_options; + parse_options.options = JERRY_PARSE_MODULE | JERRY_PARSE_HAS_RESOURCE; + parse_options.resource_name_p = file; + parse_options.resource_name_length = sizeof (file) - 1; + + jerry_value_t module_value = jerry_parse (script, sizeof (script) - 1, &parse_options); + + jerry_release_value (jerry_module_link (module_value, NULL, NULL)); + + jerry_release_value (module_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_module_state_t](#jerry_module_state_t) +- [jerry_module_state_changed_callback_t](#jerry_module_state_changed_callback_t) + ## jerry_module_get_number_of_requests **Summary** diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index e4c889533..d1eeaabcb 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -755,6 +755,24 @@ jerry_module_get_state (const jerry_value_t module_val) /**< module object */ #endif /* JERRY_MODULE_SYSTEM */ } /* jerry_module_get_state */ +/** + * Sets a callback which is called after a module state is changed to linked, evaluated, or error. + */ +void +jerry_module_set_state_changed_callback (jerry_module_state_changed_callback_t callback, /**< callback */ + void *user_p) /**< pointer passed to the callback */ +{ + jerry_assert_api_available (); + +#if JERRY_MODULE_SYSTEM + JERRY_CONTEXT (module_state_changed_callback_p) = callback; + JERRY_CONTEXT (module_state_changed_callback_user_p) = user_p; +#else /* !JERRY_MODULE_SYSTEM */ + JERRY_UNUSED (callback); + JERRY_UNUSED (user_p); +#endif /* JERRY_MODULE_SYSTEM */ +} /* jerry_module_set_state_changed_callback */ + /** * Returns the number of import/export requests of a module * diff --git a/jerry-core/ecma/base/ecma-module.c b/jerry-core/ecma/base/ecma-module.c index fb77ffd13..3a7a1c879 100644 --- a/jerry-core/ecma/base/ecma-module.c +++ b/jerry-core/ecma/base/ecma-module.c @@ -59,7 +59,7 @@ ecma_module_create (void) } /* ecma_module_create */ /** - * cleanup context variables for the root module. + * Cleanup context variables for the root module. */ void ecma_module_cleanup_context (void) @@ -70,6 +70,27 @@ ecma_module_cleanup_context (void) #endif /* JERRY_NDEBUG */ } /* ecma_module_cleanup_context */ +/** + * Sets module state to error. + */ +static void +ecma_module_set_error_state (ecma_module_t *module_p) /**< module */ +{ + module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_ERROR; + + if (JERRY_CONTEXT (module_state_changed_callback_p) != NULL + && !jcontext_has_pending_abort ()) + { + jerry_value_t exception = jcontext_take_exception (); + + JERRY_CONTEXT (module_state_changed_callback_p) (JERRY_MODULE_STATE_ERROR, + ecma_make_object_value (&module_p->header.object), + exception, + JERRY_CONTEXT (module_state_changed_callback_user_p)); + jcontext_raise_exception (exception); + } +} /* ecma_module_set_error_state */ + /** * Gets the internal module pointer of a module * @@ -398,11 +419,21 @@ ecma_module_evaluate (ecma_module_t *module_p) /**< module */ ret_value = vm_run_module (module_p); } - module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_ERROR; - - if (!ECMA_IS_VALUE_ERROR (ret_value)) + if (JERRY_LIKELY (!ECMA_IS_VALUE_ERROR (ret_value))) { module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_EVALUATED; + + if (JERRY_CONTEXT (module_state_changed_callback_p) != NULL) + { + JERRY_CONTEXT (module_state_changed_callback_p) (JERRY_MODULE_STATE_EVALUATED, + ecma_make_object_value (&module_p->header.object), + ret_value, + JERRY_CONTEXT (module_state_changed_callback_user_p)); + } + } + else + { + ecma_module_set_error_state (module_p); } if (!(module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE)) @@ -943,7 +974,7 @@ restart: if (ECMA_IS_VALUE_ERROR (result)) { - current_module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_ERROR; + ecma_module_set_error_state (current_module_p); goto error; } @@ -974,6 +1005,14 @@ restart: JERRY_ASSERT (last_p->module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKING); last_p->module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_LINKED; + if (JERRY_CONTEXT (module_state_changed_callback_p) != NULL) + { + JERRY_CONTEXT (module_state_changed_callback_p) (JERRY_MODULE_STATE_LINKED, + ecma_make_object_value (&last_p->module_p->header.object), + ECMA_VALUE_UNDEFINED, + JERRY_CONTEXT (module_state_changed_callback_user_p)); + } + jmem_heap_free_block (last_p, sizeof (ecma_module_stack_item_t)); last_p = prev_p; } diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 970602413..fe76d5225 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -269,6 +269,7 @@ jerry_value_t jerry_module_link (const jerry_value_t module_val, jerry_module_resolve_callback_t callback_p, void *user_p); jerry_value_t jerry_module_evaluate (const jerry_value_t module_val); jerry_module_state_t jerry_module_get_state (const jerry_value_t module_val); +void jerry_module_set_state_changed_callback (jerry_module_state_changed_callback_t callback, void *user_p); size_t jerry_module_get_number_of_requests (const jerry_value_t module_val); jerry_value_t jerry_module_get_request (const jerry_value_t module_val, size_t request_index); jerry_value_t jerry_module_get_namespace (const jerry_value_t module_val); diff --git a/jerry-core/include/jerryscript-types.h b/jerry-core/include/jerryscript-types.h index 8af1d72df..6fd033a57 100644 --- a/jerry-core/include/jerryscript-types.h +++ b/jerry-core/include/jerryscript-types.h @@ -550,6 +550,12 @@ typedef jerry_value_t (*jerry_module_resolve_callback_t) (const jerry_value_t sp const jerry_value_t referrer, void *user_p); +/** + * Callback which is called after the module enters into linked, evaluated or error state. + */ +typedef void (*jerry_module_state_changed_callback_t) (jerry_module_state_t new_state, const jerry_value_t module, + const jerry_value_t value, void *user_p); + /** * Callback which is called by jerry_module_evaluate to evaluate the native module. */ diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index 0e44b7d23..a25879745 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -148,7 +148,10 @@ struct jerry_context_t #endif /* JERRY_ESNEXT */ #if JERRY_MODULE_SYSTEM - ecma_module_t *module_current_p; /**< current module context */ + ecma_module_t *module_current_p; /**< current module context */ + jerry_module_state_changed_callback_t module_state_changed_callback_p; /**< callback which is called after the + * state of a module is changed */ + void *module_state_changed_callback_user_p; /**< user pointer for module_state_changed_callback_p */ #endif /* JERRY_MODULE_SYSTEM */ vm_frame_ctx_t *vm_top_context_p; /**< top (current) interpreter context */ diff --git a/tests/unit-core/test-module.c b/tests/unit-core/test-module.c index 2e5ccfd36..27857c82c 100644 --- a/tests/unit-core/test-module.c +++ b/tests/unit-core/test-module.c @@ -224,6 +224,43 @@ resolve_callback4 (const jerry_value_t specifier, /**< module specifier */ return native_module; } /* resolve_callback4 */ +static void +module_state_changed (jerry_module_state_t new_state, /**< new state of the module */ + const jerry_value_t module_val, /**< a module whose state is changed */ + const jerry_value_t value, /**< value argument */ + void *user_p) /**< user pointer */ +{ + TEST_ASSERT (jerry_module_get_state (module_val) == new_state); + TEST_ASSERT (module_val == module); + TEST_ASSERT (user_p == (void *) &counter); + + ++counter; + + switch (counter) + { + case 1: + case 3: + { + TEST_ASSERT (new_state == JERRY_MODULE_STATE_LINKED); + TEST_ASSERT (jerry_value_is_undefined (value)); + break; + } + case 2: + { + TEST_ASSERT (new_state == JERRY_MODULE_STATE_EVALUATED); + TEST_ASSERT (jerry_value_is_number (value) && jerry_get_number_value (value) == 33.5); + break; + } + default: + { + TEST_ASSERT (counter == 4); + TEST_ASSERT (new_state == JERRY_MODULE_STATE_ERROR); + TEST_ASSERT (jerry_value_is_number (value) && jerry_get_number_value (value) == -5.5); + break; + } + } +} /* module_state_changed */ + int main (void) { @@ -471,6 +508,43 @@ main (void) jerry_release_value (object); jerry_release_value (number); + counter = 0; + jerry_module_set_state_changed_callback (module_state_changed, (void *) &counter); + + jerry_char_t source4[] = TEST_STRING_LITERAL ( + "33.5\n" + ); + module = jerry_parse (source4, sizeof (source4) - 1, &module_parse_options); + + result = jerry_module_link (module, NULL, NULL); + TEST_ASSERT (!jerry_value_is_error (result)); + jerry_release_value (result); + + result = jerry_module_evaluate (module); + TEST_ASSERT (!jerry_value_is_error (result)); + jerry_release_value (result); + + jerry_release_value (module); + + jerry_char_t source5[] = TEST_STRING_LITERAL ( + "throw -5.5\n" + ); + module = jerry_parse (source5, sizeof (source5) - 1, &module_parse_options); + + result = jerry_module_link (module, NULL, NULL); + TEST_ASSERT (!jerry_value_is_error (result)); + jerry_release_value (result); + + result = jerry_module_evaluate (module); + TEST_ASSERT (jerry_value_is_error (result)); + jerry_release_value (result); + + jerry_release_value (module); + + jerry_module_set_state_changed_callback (NULL, NULL); + + TEST_ASSERT (counter == 4); + jerry_cleanup (); return 0;