Support dynamic import calls (#4652)

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg
2021-07-24 09:26:46 +02:00
committed by GitHub
parent d9360f51d0
commit d4178ae386
21 changed files with 698 additions and 114 deletions
+168
View File
@@ -830,6 +830,48 @@ 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_import_callback_t
**Summary**
Callback which is called when an import is resolved dynamically to get the referenced module.
*Note*:
- If the function returns with a promise, the import call returns with this promise. The
application should try to resolve the requested module later. If the module is evaluated
successfully, the returned promise should be resolved with the namespace object of the
module. Otherwise, the returned promise should be rejected with an error.
- If the function returns with a resolved module, a promise is created and resolved with the
namespace object of the module. The import call returns with the resolved promise.
- If the function returns with an error, a promise is created and rejected with the
return error. The import call returns with the rejected promise.
- All other return values are considered invalid. In this case the import call returns
with a rejected promise. The rejected promise has a fixed error message, it does not
specify the reason of the fail.
- If realms are enabled, the returned module should be created in the current realm
(see: [jerry_get_global_object](#jerry_get_global_object))
**Prototype**
```c
typedef jerry_value_t (*jerry_module_import_callback_t) (const jerry_value_t specifier,
const jerry_value_t user_value,
void *user_p);
```
- `specifier` - a module specifier string (usually used as a path to the module)
- `user_value` - the user value assigned to the script (see [jerry_parse_options_t](#jerry_parse_options_t))
- `user_p` - pointer passed to [jerry_module_set_import_callback](#jerry_module_set_import_callback).
- return value
- promise or resolved module - if the operation is successful
- an error - otherwise
*New in version [[NEXT_RELEASE]]*.
**See also**
- [jerry_module_set_import_callback](#jerry_module_set_import_callback)
- [jerry_get_global_object](#jerry_get_global_object)
## jerry_module_state_changed_callback_t
**Summary**
@@ -5018,6 +5060,132 @@ main (void)
- [jerry_module_link](#jerry_module_link)
- [jerry_module_evaluate](#jerry_module_evaluate)
## jerry_module_set_import_callback
Sets the callback which is called when dynamic imports are resolved. The resolver
receives the `user_value` assigned to the currently executed script, which should
provide all the information that is necessary for the resolve.
*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).
- The possible return values of the callback is explained
in [jerry_module_import_callback_t](#jerry_module_import_callback_t)
**Prototype**
```c
void
jerry_module_set_import_callback (jerry_module_import_callback_t callback_p,
void *user_p)
```
- `callback_p` - a [jerry_module_import_callback_t](#jerry_module_import_callback_t) callback which handles `import()` calls
- `user_p` - user pointer passed to the callback
*New in version [[NEXT_RELEASE]]*.
**Example**
[doctest]: # (test="compile")
```c
#include <jerryscript.h>
#include <stdio.h>
typedef struct {
jerry_value_t specifier;
jerry_value_t user_value;
jerry_value_t promise;
} resolve_module_task_t;
static jerry_value_t
resolve_dynamic (const jerry_value_t specifier, /**< module specifier */
const jerry_value_t user_value, /**< user value assigned to the script */
void *user_p) /**< user data */
{
/* If the specified module has already been evaluated, this callback can
* return with it and the promise creation is automatically done by the engine.
* Otherwise the application usually adds a resolve task to a command queue. */
/* This very simple command queue supports only one task. */
resolve_module_task_t *task_p = (resolve_module_task_t *) user_p;
task_p->specifier = jerry_acquire_value (specifier);
task_p->user_value = jerry_acquire_value (user_value);
/* This Promise should be evaluated later. */
jerry_value_t promise = jerry_create_promise ();
task_p->promise = jerry_acquire_value (promise);
return promise;
}
int
main (void)
{
jerry_init (JERRY_INIT_EMPTY);
resolve_module_task_t task;
jerry_module_set_import_callback (resolve_dynamic, &task);
const jerry_char_t script[] = "import('modules/my_module.mjs').then(\n"
" function (namespace) { /* use namespace */},\n"
" function (error) { /* handle error */}\n"
")";
const jerry_char_t resource[] = "dir/my_script.js";
jerry_parse_options_t parse_options;
parse_options.options = JERRY_PARSE_HAS_RESOURCE | JERRY_PARSE_HAS_USER_VALUE;
/* Resource is usually used for debugging purposes, e.g. for generating backtrace. */
parse_options.resource_name_p = resource;
parse_options.resource_name_length = sizeof (resource) - 1;
/* User value should provide information for resolving dynamic imports.
* In this case it contains the full path excluding the filename. */
parse_options.user_value = jerry_create_string ((const jerry_char_t *) "/home/user/dir");
jerry_value_t script_value = jerry_parse (script, sizeof (script) - 1, &parse_options);
jerry_release_value (parse_options.user_value);
jerry_release_value (jerry_run (script_value));
jerry_release_value (script_value);
/* The application resolves both the module and the promise using the specifier
* and the user_value. In this example the specifier is modules/my_module.mjs. */
const jerry_char_t module_script[] = "export var a = 5";
const jerry_char_t module_resource[] = "modules/my_module.mjs";
parse_options.options = JERRY_PARSE_MODULE | JERRY_PARSE_HAS_RESOURCE | JERRY_PARSE_HAS_USER_VALUE;
parse_options.resource_name_p = module_resource;
parse_options.resource_name_length = sizeof (module_resource) - 1;
parse_options.user_value = jerry_create_string ((const jerry_char_t *) "/home/user/dir/modules");
jerry_value_t module_value = jerry_parse (module_script, sizeof (module_script) - 1, &parse_options);
jerry_release_value (parse_options.user_value);
jerry_release_value (jerry_module_link (module_value, NULL, NULL));
jerry_release_value (jerry_module_evaluate (module_value));
/* The promise must be resolved with the namespace object, not the module. */
jerry_value_t namespace_value = jerry_module_get_namespace (module_value);
jerry_release_value (jerry_resolve_or_reject_promise (task.promise, namespace_value, true));
jerry_release_value (namespace_value);
jerry_release_value (module_value);
jerry_release_value (task.specifier);
jerry_release_value (task.user_value);
jerry_release_value (task.promise);
/* Process promise handlers. */
jerry_release_value (jerry_run_all_enqueued_jobs ());
jerry_cleanup ();
return 0;
}
```
**See also**
- [jerry_module_import_callback_t](#jerry_module_import_callback_t)
## jerry_native_module_create
Creates a native module with a list of exports. The initial state of the module is linked.