diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index dff99caeb..ad0f9470a 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -747,6 +747,21 @@ typedef void (*jerry_error_object_created_callback_t) (const jerry_value_t error - [jerry_set_error_object_created_callback](#jerry_set_error_object_created_callback) +## jerry_module_state_t + +An enum representing the current status of a module + + - JERRY_MODULE_STATE_INVALID - Return value for jerry_module_get_state when its argument is not a module + - JERRY_MODULE_STATE_UNLINKED - Module is currently unlinked + - JERRY_MODULE_STATE_LINKING - Module is currently being linked + - JERRY_MODULE_STATE_LINKED - Module has been linked (its depencencies has been resolved) + - JERRY_MODULE_STATE_EVALUATING - Module is currently being evaluated + - JERRY_MODULE_STATE_EVALUATED - Module has been evaluated (its source code has been executed) + - JERRY_MODULE_STATE_ERROR - An error has been encountered before the evaluated state is reached + - JERRY_MODULE_STATE_NATIVE - Module is native module + +*New in version [[NEXT_RELEASE]]*. + ## jerry_module_resolve_callback_t **Summary** @@ -4365,6 +4380,341 @@ main (void) **See also** - [jerry_module_resolve_callback_t](#jerry_module_resolve_callback_t) +## jerry_module_evaluate + +Evaluate a module and its dependencies. The module must be in linked state. + +*Notes*: +- Returned value must be freed with [jerry_release_value](#jerry_release_value) + when it is no longer needed. +- 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 +jerry_value_t jerry_module_evaluate (const jerry_value_t module_val); +``` + +- `module_val` - module object +- return + - result of module bytecode execution - if evaluation was successful + - error, otherwise + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # (test="compile") + +```c +#include +#include + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + const jerry_char_t script[] = "export var a = 6"; + const jerry_char_t file[] = "a.mjs"; + + 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 (jerry_module_evaluate (module_value)); + + jerry_release_value (module_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_module_link](#jerry_module_link) + +## jerry_module_get_state + +**Summary** + +Returns the current status of a module. The available values +are listed in [jerry_module_state_t](#jerry_module_state_t) + +*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 +jerry_module_state_t jerry_module_get_state (const jerry_value_t module_val); +``` + +- `module_val` - module object +- return + - current status - if module_val is a module + - JERRY_MODULE_STATE_INVALID - otherwise + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # (test="compile") + +```c +#include +#include + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + const jerry_char_t script[] = "import a from 'b.mjs'"; + const jerry_char_t file[] = "a.mjs"; + + 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); + + if (jerry_module_get_state (module_value) == JERRY_MODULE_STATE_UNLINKED) + { + printf ("Module parsing has been successful\n"); + } + + jerry_release_value (module_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_module_state_t](#jerry_module_state_t) + +## jerry_module_get_number_of_requests + +**Summary** + +Returns the number of import/export requests of a module. +The requests can be queried by [jerry_module_get_request](#jerry_module_get_request). + + +*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 +size_t jerry_module_get_number_of_requests (const jerry_value_t module_val); +``` + +- `module_val` - module object +- return + - number of import/export requests of a module, if `module_val` is module, + - 0, otherwise + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # (test="compile") + +```c +#include +#include + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + const jerry_char_t script[] = "export * from 'b.mjs'" + "import a from 'c.mjs'"; + const jerry_char_t file[] = "a.mjs"; + + 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); + + /* Prints 2. */ + printf ("Number of requests: %d\n", (int) jerry_module_get_number_of_requests (module_value)); + + jerry_release_value (module_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_module_get_request](#jerry_module_get_request) +- [jerry_parse](#jerry_parse) +- [jerry_module_link](#jerry_module_link) + +## jerry_module_get_request + +**Summary** + +Returns the module request specified by the `request_index` argument. The requests +are ordered in source code occurence. When parsing is completed, all returned values +are strings. If [jerry_module_link](#jerry_module_link) is completed successfully +all returned values are module objects instead. If linking is in progress or fails, +the successfully resolved dependencies are module objects, the rest are strings. +The number of requests can be queried by +[jerry_module_get_number_of_requests](#jerry_module_get_number_of_requests). + +*Notes*: +- Returned value must be freed with [jerry_release_value](#jerry_release_value) + when it is no longer needed. +- 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 +jerry_value_t jerry_module_get_request (const jerry_value_t module_val, size_t request_index); +``` + +- `module_val` - module object +- return + - string, if the request has not been resolved yet + - module object, if the request has been resolved successfully + - error, otherwise + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # (test="compile") + +```c +#include + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + const jerry_char_t script[] = "export * from 'b.mjs'" + "import a from 'c.mjs'"; + const jerry_char_t file[] = "a.mjs"; + + 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_value_t request_value = jerry_module_get_request (module_value, 0); + /* Returns with b.mjs */ + jerry_release_value (request_value); + + request_value = jerry_module_get_request (module_value, 1); + /* Returns with c.mjs */ + jerry_release_value (request_value); + + jerry_release_value (module_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_module_get_number_of_requests](#jerry_module_get_number_of_requests) +- [jerry_parse](#jerry_parse) +- [jerry_module_link](#jerry_module_link) + +## jerry_module_get_namespace + +Returns the namespace object of a module + +*Notes*: +- Returned value must be freed with [jerry_release_value](#jerry_release_value) + when it is no longer needed. +- 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 +jerry_value_t jerry_module_get_namespace (const jerry_value_t module_val); +``` + +- `module_val` - module object +- return + - object, if namespace object is available + - error, otherwise + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # (test="compile") + +```c +#include +#include + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + const jerry_char_t script[] = "export var a = 6"; + const jerry_char_t file[] = "a.mjs"; + + 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 (jerry_module_evaluate (module_value)); + + jerry_value_t namespace_value = jerry_module_get_namespace (module_value); + + /* Exports can be checked. */ + + jerry_release_value (namespace_value); + jerry_release_value (module_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_module_link](#jerry_module_link) +- [jerry_module_evaluate](#jerry_module_evaluate) + # Functions for promise objects These APIs all depend on the es.next profile (or on some build options). diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index df91c7a24..6fe5a0ff2 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -689,20 +689,6 @@ jerry_run (const jerry_value_t func_val) /**< function to run */ ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; -#if JERRY_MODULE_SYSTEM - if (JERRY_UNLIKELY (ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_MODULE_UL)) - { - ecma_module_t *root_module_p = (ecma_module_t *) ext_object_p; - - if (root_module_p->header.u.class_prop.extra_info != ECMA_MODULE_STATE_LINKED) - { - return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Module must be in linked state"))); - } - - return jerry_return (ecma_module_evaluate (root_module_p)); - } -#endif /* JERRY_MODULE_SYSTEM */ - if (ext_object_p->u.class_prop.class_id != LIT_MAGIC_STRING_SCRIPT_UL) { return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p))); @@ -757,6 +743,8 @@ jerry_module_link (const jerry_value_t module_val, /**< root module */ * jerry_port_module_resolve when NULL is passed */ void *user_p) /**< pointer passed to the resolve callback */ { + jerry_assert_api_available (); + #if JERRY_MODULE_SYSTEM if (callback == NULL) { @@ -780,6 +768,191 @@ jerry_module_link (const jerry_value_t module_val, /**< root module */ #endif /* JERRY_MODULE_SYSTEM */ } /* jerry_module_link */ +/** + * Evaluate a module and its dependencies. The module must be in linked state. + * + * Note: + * returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return result of module bytecode execution - if evaluation was successful + * error - otherwise + */ +jerry_value_t +jerry_module_evaluate (const jerry_value_t module_val) /**< root module */ +{ + jerry_assert_api_available (); + +#if JERRY_MODULE_SYSTEM + ecma_module_t *module_p = ecma_module_get_resolved_module (module_val); + + if (module_p == NULL) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_not_module_p))); + } + + if (module_p->header.u.class_prop.extra_info != JERRY_MODULE_STATE_LINKED) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Module must be in linked state"))); + } + + return jerry_return (ecma_module_evaluate (module_p)); +#else /* !JERRY_MODULE_SYSTEM */ + JERRY_UNUSED (module_val); + + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_module_not_supported_p))); +#endif /* JERRY_MODULE_SYSTEM */ +} /* jerry_module_evaluate */ + +/** + * Returns the current status of a module + * + * @return current status - if module_val is a module, + * JERRY_MODULE_STATE_INVALID - otherwise + */ +jerry_module_state_t +jerry_module_get_state (const jerry_value_t module_val) /**< module object */ +{ + jerry_assert_api_available (); + +#if JERRY_MODULE_SYSTEM + ecma_module_t *module_p = ecma_module_get_resolved_module (module_val); + + if (module_p == NULL) + { + return JERRY_MODULE_STATE_INVALID; + } + + return (jerry_module_state_t) module_p->header.u.class_prop.extra_info; +#else /* !JERRY_MODULE_SYSTEM */ + JERRY_UNUSED (module_val); + + return JERRY_MODULE_STATE_INVALID; +#endif /* JERRY_MODULE_SYSTEM */ +} /* jerry_module_get_state */ + +/** + * Returns the number of import/export requests of a module + * + * @return number of import/export requests of a module + */ +size_t +jerry_module_get_number_of_requests (const jerry_value_t module_val) /**< module */ +{ + jerry_assert_api_available (); + +#if JERRY_MODULE_SYSTEM + ecma_module_t *module_p = ecma_module_get_resolved_module (module_val); + + if (module_p == NULL) + { + return 0; + } + + size_t number_of_requests = 0; + + ecma_module_node_t *node_p = module_p->imports_p; + + while (node_p != NULL) + { + number_of_requests++; + node_p = node_p->next_p; + } + + return number_of_requests; +#else /* !JERRY_MODULE_SYSTEM */ + JERRY_UNUSED (module_val); + + return 0; +#endif /* JERRY_MODULE_SYSTEM */ +} /* jerry_module_get_number_of_requests */ + +/** + * Returns the module request specified by the request_index argument + * + * Note: + * returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return string - if the request has not been resolved yet, + * module object - if the request has been resolved successfully, + * error - otherwise + */ +jerry_value_t +jerry_module_get_request (const jerry_value_t module_val, /**< module */ + size_t request_index) /**< request index */ +{ + jerry_assert_api_available (); + +#if JERRY_MODULE_SYSTEM + ecma_module_t *module_p = ecma_module_get_resolved_module (module_val); + + if (module_p == NULL) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_not_module_p))); + } + + ecma_module_node_t *node_p = module_p->imports_p; + + while (node_p != NULL) + { + if (request_index == 0) + { + return ecma_copy_value (node_p->u.path_or_module); + } + + --request_index; + node_p = node_p->next_p; + } + + return jerry_throw (ecma_raise_range_error (ECMA_ERR_MSG ("Request is not available"))); +#else /* !JERRY_MODULE_SYSTEM */ + JERRY_UNUSED (module_val); + JERRY_UNUSED (request_index); + + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_module_not_supported_p))); +#endif /* JERRY_MODULE_SYSTEM */ +} /* jerry_module_get_request */ + +/** + * Returns the namespace object of a module + * + * Note: + * returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return object - if namespace object is available, + * error - otherwise + */ +jerry_value_t +jerry_module_get_namespace (const jerry_value_t module_val) /**< module */ +{ + jerry_assert_api_available (); + +#if JERRY_MODULE_SYSTEM + ecma_module_t *module_p = ecma_module_get_resolved_module (module_val); + + if (module_p == NULL) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_not_module_p))); + } + + if (module_p->namespace_object_p == NULL) + { + if (module_p->header.u.class_prop.extra_info != JERRY_MODULE_STATE_EVALUATED) + { + return jerry_throw (ecma_raise_range_error (ECMA_ERR_MSG ("Namespace object has not been created yet"))); + } + + ecma_module_create_namespace_object (module_p); + } + + ecma_ref_object (module_p->namespace_object_p); + return ecma_make_object_value (module_p->namespace_object_p); +#else /* !JERRY_MODULE_SYSTEM */ + JERRY_UNUSED (module_val); + + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_module_not_supported_p))); +#endif /* JERRY_MODULE_SYSTEM */ +} /* jerry_module_get_namespace */ + /** * Run enqueued Promise jobs until the first thrown error or until all get executed. * diff --git a/jerry-core/ecma/base/ecma-module.c b/jerry-core/ecma/base/ecma-module.c index a21356397..4454dfdad 100644 --- a/jerry-core/ecma/base/ecma-module.c +++ b/jerry-core/ecma/base/ecma-module.c @@ -40,7 +40,7 @@ ecma_module_initialize_context (void) ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; ext_object_p->u.class_prop.class_id = LIT_MAGIC_STRING_MODULE_UL; - ext_object_p->u.class_prop.extra_info = ECMA_MODULE_STATE_UNLINKED; + ext_object_p->u.class_prop.extra_info = JERRY_MODULE_STATE_UNLINKED; ecma_module_t *module_p = (ecma_module_t *) obj_p; @@ -219,7 +219,7 @@ ecma_module_resolve_export (ecma_module_t *const module_p, /**< base module */ continue; } - if (current_module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_NATIVE) + if (current_module_p->header.u.class_prop.extra_info == JERRY_MODULE_STATE_NATIVE) { ecma_object_t *object_p = current_module_p->namespace_object_p; ecma_value_t prop_value = ecma_op_object_find_own (ecma_make_object_value (object_p), @@ -382,17 +382,17 @@ ecma_module_resolve_export (ecma_module_t *const module_p, /**< base module */ ecma_value_t ecma_module_evaluate (ecma_module_t *module_p) /**< module */ { - if (module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_ERROR) + if (module_p->header.u.class_prop.extra_info == JERRY_MODULE_STATE_ERROR) { return ecma_raise_range_error (ECMA_ERR_MSG ("Module is in error state")); } - if (module_p->header.u.class_prop.extra_info >= ECMA_MODULE_STATE_EVALUATING) + if (module_p->header.u.class_prop.extra_info >= JERRY_MODULE_STATE_EVALUATING) { return ECMA_VALUE_EMPTY; } - JERRY_ASSERT (module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_LINKED); + JERRY_ASSERT (module_p->header.u.class_prop.extra_info == JERRY_MODULE_STATE_LINKED); #if JERRY_BUILTIN_REALMS ecma_object_t *global_object_p = (ecma_object_t *) ecma_op_function_get_realm (module_p->compiled_code_p); @@ -400,18 +400,18 @@ ecma_module_evaluate (ecma_module_t *module_p) /**< module */ ecma_object_t *global_object_p = ecma_builtin_get_global (); #endif /* JERRY_BUILTIN_REALMS */ - module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_EVALUATING; + module_p->header.u.class_prop.extra_info = JERRY_MODULE_STATE_EVALUATING; module_p->scope_p = ecma_create_decl_lex_env (ecma_get_global_environment (global_object_p)); ecma_value_t ret_value; ret_value = vm_run_module (module_p); - module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_ERROR; + module_p->header.u.class_prop.extra_info = JERRY_MODULE_STATE_ERROR; if (!ECMA_IS_VALUE_ERROR (ret_value)) { ecma_free_value (ret_value); - module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_EVALUATED; + module_p->header.u.class_prop.extra_info = JERRY_MODULE_STATE_EVALUATED; ret_value = ECMA_VALUE_EMPTY; } @@ -482,7 +482,7 @@ ecma_module_namespace_object_add_export_if_needed (ecma_module_t *module_p, /**< * @return ECMA_VALUE_ERROR - if an error occured * ECMA_VALUE_EMPTY - otherwise */ -static ecma_value_t +ecma_value_t ecma_module_create_namespace_object (ecma_module_t *module_p) /**< module */ { ecma_value_t result = ECMA_VALUE_EMPTY; @@ -491,7 +491,7 @@ ecma_module_create_namespace_object (ecma_module_t *module_p) /**< module */ return result; } - JERRY_ASSERT (module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_EVALUATED); + JERRY_ASSERT (module_p->header.u.class_prop.extra_info == JERRY_MODULE_STATE_EVALUATED); ecma_module_resolve_set_t *resolve_set_p = NULL; ecma_module_resolve_stack_t *stack_p = NULL; @@ -688,7 +688,7 @@ ecma_module_connect_imports (ecma_module_t *module_p) return ecma_raise_syntax_error (ECMA_ERR_MSG ("Ambiguous import request")); } - if (record.module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_NATIVE) + if (record.module_p->header.u.class_prop.extra_info == JERRY_MODULE_STATE_NATIVE) { ecma_object_t *object_p = record.module_p->namespace_object_p; prop_value = ecma_op_object_find_own (ecma_make_object_value (object_p), object_p, record.name_p); @@ -858,12 +858,12 @@ ecma_module_link (ecma_module_t *module_p, /**< root module */ jerry_module_resolve_callback_t callback, /**< resolve module callback */ void *user_p) /**< pointer passed to the resolve callback */ { - if (module_p->header.u.class_prop.extra_info != ECMA_MODULE_STATE_UNLINKED) + if (module_p->header.u.class_prop.extra_info != JERRY_MODULE_STATE_UNLINKED) { return ecma_raise_type_error (ECMA_ERR_MSG ("Module must be in unlinked state")); } - module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_LINKING; + module_p->header.u.class_prop.extra_info = JERRY_MODULE_STATE_LINKING; uint32_t dfs_index = 0; ecma_module_stack_item_t *last_p; @@ -919,7 +919,7 @@ restart: node_p->u.path_or_module = resolve_result; ecma_deref_object (ecma_get_object_from_value (resolve_result)); - if (resolved_module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_ERROR) + if (resolved_module_p->header.u.class_prop.extra_info == JERRY_MODULE_STATE_ERROR) { ecma_raise_type_error (ECMA_ERR_MSG ("Cannot link to a module which is in error state")); goto error; @@ -938,10 +938,10 @@ restart: { module_p = ecma_module_get_from_object (node_p->u.path_or_module); - if (module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_UNLINKED) + if (module_p->header.u.class_prop.extra_info == JERRY_MODULE_STATE_UNLINKED) { current_p->node_p = node_p->next_p; - module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_LINKING; + module_p->header.u.class_prop.extra_info = JERRY_MODULE_STATE_LINKING; ecma_module_stack_item_t *item_p; item_p = (ecma_module_stack_item_t *) jmem_heap_alloc_block (sizeof (ecma_module_stack_item_t)); @@ -962,7 +962,7 @@ restart: goto restart; } - if (module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_LINKING) + if (module_p->header.u.class_prop.extra_info == JERRY_MODULE_STATE_LINKING) { uint32_t dfs_ancestor_index = module_p->header.u.class_prop.u.dfs_ancestor_index; @@ -996,8 +996,8 @@ restart: { ecma_module_stack_item_t *prev_p = last_p->prev_p; - JERRY_ASSERT (last_p->module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_LINKING); - last_p->module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_LINKED; + JERRY_ASSERT (last_p->module_p->header.u.class_prop.extra_info == JERRY_MODULE_STATE_LINKING); + last_p->module_p->header.u.class_prop.extra_info = JERRY_MODULE_STATE_LINKED; jmem_heap_free_block (last_p, sizeof (ecma_module_stack_item_t)); last_p = prev_p; @@ -1017,8 +1017,8 @@ error: { ecma_module_stack_item_t *prev_p = last_p->prev_p; - JERRY_ASSERT (last_p->module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_LINKING); - last_p->module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_UNLINKED; + JERRY_ASSERT (last_p->module_p->header.u.class_prop.extra_info == JERRY_MODULE_STATE_LINKING); + last_p->module_p->header.u.class_prop.extra_info = JERRY_MODULE_STATE_UNLINKED; jmem_heap_free_block (last_p, sizeof (ecma_module_stack_item_t)); last_p = prev_p; @@ -1075,7 +1075,9 @@ ecma_module_release_module_nodes (ecma_module_node_t *module_node_p, /**< first void ecma_module_release_module (ecma_module_t *module_p) /**< module */ { - ecma_module_state_t state = (ecma_module_state_t) module_p->header.u.class_prop.extra_info; + jerry_module_state_t state = (jerry_module_state_t) module_p->header.u.class_prop.extra_info; + + JERRY_ASSERT (state != JERRY_MODULE_STATE_INVALID); if (module_p->namespace_object_p != NULL) { @@ -1087,7 +1089,7 @@ ecma_module_release_module (ecma_module_t *module_p) /**< module */ #endif /* JERRY_NDEBUG */ } - if (state == ECMA_MODULE_STATE_NATIVE) + if (state == JERRY_MODULE_STATE_NATIVE) { return; } @@ -1097,7 +1099,7 @@ ecma_module_release_module (ecma_module_t *module_p) /**< module */ ecma_module_release_module_nodes (module_p->indirect_exports_p, false); ecma_module_release_module_nodes (module_p->star_exports_p, false); - if (state >= ECMA_MODULE_STATE_EVALUATING) + if (state >= JERRY_MODULE_STATE_EVALUATING) { /* The module structure keeps a strong reference to the module scope, which will require an extra GC call. */ JERRY_CONTEXT (ecma_gc_new_objects)++; diff --git a/jerry-core/ecma/base/ecma-module.h b/jerry-core/ecma/base/ecma-module.h index d6c2e5220..074695002 100644 --- a/jerry-core/ecma/base/ecma-module.h +++ b/jerry-core/ecma/base/ecma-module.h @@ -35,20 +35,6 @@ typedef struct ecma_module_names ecma_string_t *local_name_p; /**< Local name of the item */ } ecma_module_names_t; -/** - * An enum identifing the current state of the module - */ -typedef enum -{ - ECMA_MODULE_STATE_UNLINKED = 0, /**< module is currently unlinked */ - ECMA_MODULE_STATE_LINKING = 1, /**< module is currently being linked */ - ECMA_MODULE_STATE_LINKED = 2, /**< module has been linked */ - ECMA_MODULE_STATE_EVALUATING = 3, /**< module is currently being evaluated */ - ECMA_MODULE_STATE_EVALUATED = 4, /**< module has been evaluated */ - ECMA_MODULE_STATE_ERROR = 5, /**< error is encountered during module init */ - ECMA_MODULE_STATE_NATIVE = 6, /**< module is native */ -} ecma_module_state_t; - /** * Module structure storing an instance of a module * @@ -130,6 +116,7 @@ ecma_value_t ecma_module_evaluate (ecma_module_t *module_p); void ecma_module_initialize_context (void); void ecma_module_cleanup_context (void); +ecma_value_t ecma_module_create_namespace_object (ecma_module_t *module_p); void ecma_module_release_module_names (ecma_module_names_t *module_name_p); void ecma_module_release_module (ecma_module_t *module_p); diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 1171ce26b..494e0c212 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -267,6 +267,11 @@ jerry_value_t jerry_to_property_descriptor (jerry_value_t obj_value, jerry_prope 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); +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); /** * Promise functions. diff --git a/jerry-core/include/jerryscript-types.h b/jerry-core/include/jerryscript-types.h index cf9d3d797..2fbb4e1b8 100644 --- a/jerry-core/include/jerryscript-types.h +++ b/jerry-core/include/jerryscript-types.h @@ -288,13 +288,6 @@ typedef void (*jerry_object_native_free_callback_t) (void *native_p); */ typedef void (*jerry_error_object_created_callback_t) (const jerry_value_t error_object, void *user_p); -/** - * Callback which is called by jerry_module_link to get the referenced module. - */ -typedef jerry_value_t (*jerry_module_resolve_callback_t) (const jerry_value_t specifier, - const jerry_value_t referrer, - void *user_p); - /** * Callback which tells whether the ECMAScript execution should be stopped. * @@ -522,6 +515,32 @@ typedef enum JERRY_ITERATOR_TYPE_SET, /**< Set iterator */ } jerry_iterator_type_t; +/** + * Module related types. + */ + +/** + * An enum representing the current status of a module + */ +typedef enum +{ + JERRY_MODULE_STATE_INVALID = 0, /**< return value for jerry_module_get_state when its argument is not a module */ + JERRY_MODULE_STATE_UNLINKED = 1, /**< module is currently unlinked */ + JERRY_MODULE_STATE_LINKING = 2, /**< module is currently being linked */ + JERRY_MODULE_STATE_LINKED = 3, /**< module has been linked (its depencencies has been resolved) */ + JERRY_MODULE_STATE_EVALUATING = 4, /**< module is currently being evaluated */ + JERRY_MODULE_STATE_EVALUATED = 5, /**< module has been evaluated (its source code has been executed) */ + JERRY_MODULE_STATE_ERROR = 6, /**< an error has been encountered before the evaluated state is reached */ + JERRY_MODULE_STATE_NATIVE = 7, /**< module is native module */ +} jerry_module_state_t; + +/** + * Callback which is called by jerry_module_link to get the referenced module. + */ +typedef jerry_value_t (*jerry_module_resolve_callback_t) (const jerry_value_t specifier, + const jerry_value_t referrer, + void *user_p); + /** * Proxy related types. */ diff --git a/jerry-main/main-jerry.c b/jerry-main/main-jerry.c index c61913cb0..e3942ae4a 100644 --- a/jerry-main/main-jerry.c +++ b/jerry-main/main-jerry.c @@ -88,6 +88,13 @@ restart: if (!jerry_value_is_error (ret_value)) { + if (jerry_module_get_state (ret_value) != JERRY_MODULE_STATE_UNLINKED) + { + /* A module can be evaluated only once. */ + jerry_release_value (ret_value); + continue; + } + jerry_value_t link_val = jerry_module_link (ret_value, NULL, NULL); if (jerry_value_is_error (link_val)) @@ -99,9 +106,9 @@ restart: { jerry_release_value (link_val); - jerry_value_t func_val = ret_value; - ret_value = jerry_run (func_val); - jerry_release_value (func_val); + jerry_value_t module_val = ret_value; + ret_value = jerry_module_evaluate (module_val); + jerry_release_value (module_val); } } diff --git a/tests/unit-core/test-module.c b/tests/unit-core/test-module.c index fae114218..3b474f02d 100644 --- a/tests/unit-core/test-module.c +++ b/tests/unit-core/test-module.c @@ -39,6 +39,21 @@ compare_specifier (jerry_value_t specifier, /* string value */ TEST_ASSERT (memcmp (buffer, string, length) == 0); } /* compare_specifier */ +static void +compare_property (jerry_value_t namespace_object, /**< namespace object */ + const char *name_p, /**< property name */ + double expected_value) /**< property value (number for simplicity) */ +{ + jerry_value_t name = jerry_create_string ((const jerry_char_t *) name_p); + jerry_value_t result = jerry_get_property (namespace_object, name); + + TEST_ASSERT (jerry_value_is_number (result)); + TEST_ASSERT (jerry_get_number_value (result) == expected_value); + + jerry_release_value (result); + jerry_release_value (name); +} /* compare_property */ + static jerry_value_t create_module (int id) /**< module id */ { @@ -110,6 +125,18 @@ resolve_callback2 (const jerry_value_t specifier, /**< module specifier */ return prev_module; } /* resolve_callback2 */ +static jerry_value_t +resolve_callback3 (const jerry_value_t specifier, /**< module specifier */ + const jerry_value_t referrer, /**< parent module */ + void *user_p) /**< user data */ +{ + (void) specifier; + (void) referrer; + (void) user_p; + + TEST_ASSERT (false); +} /* resolve_callback3 */ + int main (void) { @@ -163,6 +190,13 @@ main (void) TEST_ASSERT (jerry_value_is_boolean (result) && jerry_get_boolean_value (result)); TEST_ASSERT (counter == 32); jerry_release_value (result); + + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_LINKED); + TEST_ASSERT (jerry_module_get_number_of_requests (module) == 1); + result = jerry_module_get_request (module, 0); + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_LINKED); + jerry_release_value (result); + jerry_release_value (module); module = create_module (1); @@ -176,6 +210,82 @@ main (void) jerry_release_value (result); jerry_release_value (module); + TEST_ASSERT (jerry_module_get_state (number) == JERRY_MODULE_STATE_INVALID); + + jerry_parse_options_t module_parse_options; + module_parse_options.options = JERRY_PARSE_MODULE; + + jerry_char_t source1[] = TEST_STRING_LITERAL ( + "import a from '16_module.mjs'\n" + "export * from '07_module.mjs'\n" + "export * from '44_module.mjs'\n" + "import * as b from '36_module.mjs'\n" + ); + module = jerry_parse (source1, sizeof (source1) - 1, &module_parse_options); + TEST_ASSERT (!jerry_value_is_error (module)); + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_UNLINKED); + + TEST_ASSERT (jerry_module_get_number_of_requests (number) == 0); + TEST_ASSERT (jerry_module_get_number_of_requests (module) == 4); + + result = jerry_module_get_request (object, 0); + TEST_ASSERT (jerry_value_is_error (result)); + jerry_release_value (result); + + result = jerry_module_get_request (module, 0); + compare_specifier (result, 16); + jerry_release_value (result); + + result = jerry_module_get_request (module, 1); + compare_specifier (result, 7); + jerry_release_value (result); + + result = jerry_module_get_request (module, 2); + compare_specifier (result, 44); + jerry_release_value (result); + + result = jerry_module_get_request (module, 3); + compare_specifier (result, 36); + jerry_release_value (result); + + result = jerry_module_get_request (module, 4); + TEST_ASSERT (jerry_value_is_error (result)); + jerry_release_value (result); + + jerry_release_value (module); + + result = jerry_module_get_namespace (number); + TEST_ASSERT (jerry_value_is_error (result)); + jerry_release_value (result); + + jerry_char_t source2[] = TEST_STRING_LITERAL ( + "export let a = 6\n" + "export let b = 8.5\n" + ); + module = jerry_parse (source2, sizeof (source2) - 1, &module_parse_options); + TEST_ASSERT (!jerry_value_is_error (module)); + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_UNLINKED); + + result = jerry_module_link (module, resolve_callback3, NULL); + TEST_ASSERT (!jerry_value_is_error (result)); + jerry_release_value (result); + + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_LINKED); + + result = jerry_module_evaluate (module); + TEST_ASSERT (!jerry_value_is_error (result)); + jerry_release_value (result); + + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_EVALUATED); + + result = jerry_module_get_namespace (module); + TEST_ASSERT (jerry_value_is_object (result)); + compare_property (result, "a", 6); + compare_property (result, "b", 8.5); + jerry_release_value (result); + + jerry_release_value (module); + jerry_release_value (object); jerry_release_value (number);