diff --git a/02.API-REFERENCE.md b/02.API-REFERENCE.md index bfdd98bd0..c44ddaff1 100644 --- a/02.API-REFERENCE.md +++ b/02.API-REFERENCE.md @@ -677,7 +677,7 @@ jerry_parse (const jerry_char_t *source_p, bool is_strict); ``` -- `source_p` - string, containing source code to parse. It must be a valid utf8 string. +- `source_p` - string, containing source code to parse (must be a valid UTF8 string). - `source_size` - size of the string, in bytes. - `is_strict` - defines strict mode. - return value @@ -716,8 +716,8 @@ main (void) **Summary** Parse script and construct an ECMAScript function. The lexical -environment is set to the global lexical environment. The name -(usually a file name) is also passed to this function which is +environment is set to the global lexical environment. The resource +name (usually a file name) is also passed to this function which is used by the debugger to find the source code. *Note*: The returned value must be freed with [jerry_release_value](#jerry_release_value) when it @@ -727,25 +727,62 @@ is no longer needed. ```c jerry_value_t -jerry_parse_named_resource (const jerry_char_t *name_p, /**< name (usually a file name) */ - size_t name_length, /**< length of name */ +jerry_parse_named_resource (const jerry_char_t *resource_name_p, /**< resource name (usually a file name) */ + size_t resource_name_length, /**< length of resource name */ const jerry_char_t *source_p, /**< script source */ size_t source_size, /**< script source size */ bool is_strict) /**< strict mode */ -{ ``` -- `name_p` - name, usually a file name -- `name_length` - size of the file name, in bytes -- `source_p` - string, containing source code to parse. It must be a valid UTF8 string -- `source_size` - size of the string, in bytes -- `is_strict` - defines strict mode +- `resource_name_p` - resource name, usually a file name (must be a valid UTF8 string). +- `resource_name_length` - size of the resource name, in bytes. +- `source_p` - string, containing source code to parse (must be a valid UTF8 string). +- `source_size` - size of the string, in bytes. +- `is_strict` - defines strict mode. - return value - function object value, if script was parsed successfully, - thrown error, otherwise This function is identical to [jerry_parse](#jerry_parse), except that an additional filename parameter has been added. +## jerry_parse_function + +**Summary** + +Parse function source code and construct an ECMAScript +function. The function arguments and function body are +passed as separated arguments. The lexical environment +is set to the global lexical environment. The resource +name (usually a file name) is also passed to this function +which is used by the debugger to find the source code. + +*Note*: The returned value must be freed with [jerry_release_value](#jerry_release_value) when it +is no longer needed. + +**Prototype** + +```c +jerry_value_t +jerry_parse_function (const jerry_char_t *resource_name_p, /**< resource name (usually a file name) */ + size_t resource_name_length, /**< length of resource name */ + const jerry_char_t *arg_list_p, /**< script source */ + size_t arg_list_size, /**< script source size */ + const jerry_char_t *source_p, /**< script source */ + size_t source_size, /**< script source size */ + bool is_strict) /**< strict mode */ +``` + +- `resource_name_p` - resource name, usually a file name (must be a valid UTF8 string). +- `resource_name_length` - size of the resource name, in bytes. +- `arg_list_p` - argument list of the function (must be a valid UTF8 string). +- `arg_list_size` - size of the argument list, in bytes. +- `source_p` - string, containing source code to parse (must be a valid UTF8 string). +- `source_size` - size of the string, in bytes. +- `is_strict` - defines strict mode. +- return value + - function object value, if script was parsed successfully, + - thrown error, otherwise + ## jerry_run **Summary** diff --git a/07.DEBUGGER.md b/07.DEBUGGER.md index 9351b4f6e..2130cee95 100644 --- a/07.DEBUGGER.md +++ b/07.DEBUGGER.md @@ -93,6 +93,35 @@ debugger is disabled. The following section describes the debugger functions available for the host application. +## JerryScript debugger types + +## jerry_debugger_wait_for_source_callback_t + +**Summary** + +This callback function is called by +[jerry_debugger_wait_for_client_source](#jerry_debugger_wait_for_client_source) +when a source code is received successfully. + +**Prototype** + +```c +typedef jerry_value_t +(*jerry_debugger_wait_for_source_callback_t) (const jerry_char_t *resource_name_p, + size_t resource_name_size, + const jerry_char_t *source_p, + size_t source_size, void *user_p); +``` + +- `resource_name_p` - resource (usually a file) name of the source code +- `resource_name_size` - size of resource name +- `source_p` - source code character data +- `source_size` - size of source code +- `user_p` - custom pointer passed to [jerry_debugger_wait_for_client_source](#jerry_debugger_wait_for_client_source) + + +## JerryScript debugger functions + ### jerry_debugger_init **Summary** @@ -254,46 +283,86 @@ jerry_debugger_stop_at_breakpoint (bool enable_stop_at_breakpoint) } ``` -### jerry_debugger_wait_and_run_client_source +### jerry_debugger_wait_for_client_source **Summary** -Stops the engine and puts it into a waiting loop. If the client sends -a source code and JerryScript receives that, then the function will -run the source with the initialized options, after that the engine will -wait for a new source until the client send a close signal. +Asks the client to provide the next source code. The function +waits until the whole source code is received. As a reply the +the client may request a context reset or notify that no more +source is available. These notifications are passed back as the +return value of the function. **Prototype** ```c -jerry_debugger_wait_and_run_type_t -jerry_debugger_wait_and_run_client_source (jerry_value_t *return_value) +jerry_debugger_wait_for_source_status_t +jerry_debugger_wait_for_client_source (jerry_debugger_wait_for_source_callback_t callback_p, + void *user_p, jerry_value_t *return_value) ``` **Example** ```c - jerry_init (JERRY_INIT_EMPTY); - jerry_debugger_init (5001); +/** + * Runs the source code received by jerry_debugger_wait_for_client_source. + */ +static jerry_value_t +wait_for_source_callback (const jerry_char_t *resource_name_p, /**< resource name */ + size_t resource_name_size, /**< size of resource name */ + const jerry_char_t *source_p, /**< source code */ + size_t source_size, /**< source code size */ + void *user_p __attribute__((unused))) /**< user pointer */ +{ + jerry_value_t ret_val = jerry_parse_named_resource (resource_name_p, + resource_name_size, + source_p, + source_size, + false); - jerry_value_t run_result; - jerry_debugger_wait_and_run_type_t receive_status; + if (!jerry_value_has_error_flag (ret_val)) + { + jerry_value_t func_val = ret_val; + ret_val = jerry_run (func_val); + jerry_release_value (func_val); + } + return ret_val; +} /* wait_for_source_callback */ + +int main () +{ do { - receive_status = jerry_debugger_wait_and_run_client_source (&run_result); + /* Create a new JerryScript instance when a context reset is + * received. Applications usually registers their core bindings + * here as well (e.g. print, setTimeout). */ + jerry_init (JERRY_INIT_EMPTY); + jerry_debugger_init (5001); - if (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED) + do { - // Handle the failure (e.g. create an error). + jerry_value_t run_result; + jerry_debugger_wait_for_source_status_t receive_status; + + receive_status = jerry_debugger_wait_for_client_source (wait_for_source_callback, + NULL, + &run_result); + + jerry_release_value (run_result); } - } + while (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVED); - jerry_release_value (run_result); + jerry_cleanup (); } - while (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVED); + while (receive_status == JERRY_DEBUGGER_CONTEXT_RESET_RECEIVED); - jerry_cleanup (); + if (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED) + { + // Handle the failure (e.g. display an error). + } + return 0; +} ``` ### jerry_debugger_send_output diff --git a/12.EXT-REFERENCE-MODULE.md b/12.EXT-REFERENCE-MODULE.md index c462c3751..b3fd28169 100644 --- a/12.EXT-REFERENCE-MODULE.md +++ b/12.EXT-REFERENCE-MODULE.md @@ -12,25 +12,37 @@ permalink: /ext-reference-module/ This is a JerryScript extension that provides a means of loading modules. Fundamentally, a module is a name (stored as a string) that resolves to a `jerry_value_t`. This extension provides the function `jerryx_module_resolve()` which -accepts the name of the module being requested as well as an array of so-called "resolvers" - functions which satisfy -the signature `jerryx_module_resolver_t`. The resolvers in the list are called in sequence until one of them returns -`true` and a `jerry_value_t` in its out parameter. The value is cached if it is not an error, so subsequent requests -for the same name will not result in additional calls to the resolvers. +accepts the name of the module being requested as well as an array of so-called "resolvers" - structures containing two +function pointers: one for a function which computes a canonical name for the requested module or returns a reference +to the requested name, and one that converts a canonical name to a `jerry_value_t`, thus "resolving" or "loading" the +requested module. + +The resolvers are first called in sequence to each compute the canonical name of the requested module. This is +accomplished by calling the `get_canonical_name` function pointer they provide. If the function pointer is `NULL`, the +requested module name is assumed to be what the resolver considers to be its canonical name. `jerryx_module_resolve` +searches its cache of loaded modules for each canonical name as returned by a `get_canonical_name` function pointer. If +one of the loaded modules in the cache corresponds to a canonical name, it is returned. + +If no cached module is found, `jerryx_module_resolve` calls each resolver's `resolve` function pointer, passing it its +previously computed interpretation of the requested module's canonical name. If the resolver successfully creates the +`jerry_value_t` that represents the loaded module, it returns `true` and the `jerry_value_t` in its out parameter. + +When `jerryx_module_resolve` receives a value of `true` from a resolver, it stops iterating over the remaining +resolvers in the sequence and, if the `jerry_value_t` returned from the resolver's `resolve` does not have the error +flag set, it will add the `jerry_value_t` to its cache under the module's canonical name and return it. Thus, on +subsequent calls to `jerryx_module_resolve` with a module name whose canonical name is associated with the +`jerry_value_t`, no `resolve` callback need be called again. The purpose of having resolvers is to be able to account for the fact that different types of modules may be structured differently and thus, for each type of module a module resolver must be supplied at the point where an instance of that type of module is requested. Additionally, this extension provides a means of easily defining so-called "native" JerryScript modules which can be -resolved using the JerryScript native module resolver `jerryx_module_native_resolver()`, which can be passed to -`jerryx_module_resolve()`. Note, however, that native JerryScript modules are only supported and -`jerryx_module_native_resolver()` is only compiled in if compiler support for `__attribute__` extensions is present. In -effect this means that native JerryScript modules are available only when this extension is built with GCC or -LLVM/clang. In the absence of such support, you may construct alternative module systems and provide your own resolver -to `jerryx_module_resolve()`. - -`jerryscript-ext/module.h` defines the preprocessor directive `JERRYX_NATIVE_MODULES_SUPPORTED` only if support for -native JerryScript modules is available. +resolved using the native JerryScript module resolver `jerryx_module_native_resolver`, which can be passed to +`jerryx_module_resolve()`. Native modules are registered during application startup and by calling `dlopen()` by means +of library constructors, support for which can be turned on using the `FEATURE_INIT_FINI` build flag. In the absence of +such a flag, the module registration and unregistration functions are exposed as global symbols which can be called +explicitly. ## jerryx_module_resolve @@ -38,10 +50,17 @@ native JerryScript modules is available. Load a copy of a module into the current context or return one that was already loaded if it is found. -Each function in `resolvers_p` will be called in sequence until one returns `true` and fills out its out-parameter with -the `jerry_value_t` representing the requested module. If the `jerry_value_t` does not have the error flag set it will -be cached. Thus, on subsequent calls with the same value for `name`, none of the functions in `resolvers_p` will be -called. +For each resolver passed in via `resolvers_p`, its `get_canonical_name` function pointer gets called in order to +establish the resolver's interpretation of what the canonical name for the module should be. If `get_canonical_name` is +`NULL`, it is assumed that the requested module's name as passed in is its canonical name. + +Then, for each resolver passed in via `resolvers_p`, its `resolve` function pointer gets called with its interpretation +of what the module's canonical name should be, as computed in the previous step. + +If the resolver's `resolve` function pointer returns `true`, the `jerry_value_t` returned in its out-parameter will be +returned by `jerryx_module_resolve` as the result of the request. If no error flag is set on the `jerry_value_t` it +will be cached under its canonical name so as to avoid loading the same module twice in the event of a subsequent call +to `jerryx_module_resolve` with a module name whose canonical name matches an already loaded module. **Prototype** @@ -62,58 +81,79 @@ jerryx_module_resolve (const jerry_char_t *name, **Summary** -The resolver for JerryScript modules. A pointer to this function can be passed in the second parameter to -`jerryx_module_resolve` to search for the module among the JerryScript modules built into the binary. This function is -available only if the preprocessor directive `JERRYX_NATIVE_MODULES_SUPPORTED` is defined. +The resolver for native JerryScript modules. A pointer to this structure can be passed in the second parameter to +`jerryx_module_resolve` to search for the module among the native JerryScript modules built into the binary. This +function is available only if the preprocessor directive `JERRYX_NATIVE_MODULES_SUPPORTED` is defined. **Prototype** ```c -bool -jerryx_module_native_resolver (const jerry_char_t *name, - jerry_value_t *result) +extern jerry_module_resolver_t jerryx_native_module_resolver; ``` -- `name` - the name of the module to find -- `result` - out - place where to store the resulting module instance -- return value - `true` if the module was found and stored in `result`, and `false` otherwise - # Module data types -## jerryx_native_module_on_resolve_t +## jerryx_module_get_canonical_name_t **Summary** -Function pointer type for a function that will create an instance of a native module. This type is only defined if the -preprocessor directive `JERRYX_NATIVE_MODULES_SUPPORTED` is defined. +The function pointer type for converting a module's requested name to its canonical name. **Prototype** ```c -typedef jerry_value_t (*jerryx_native_module_on_resolve_t) (void); +typedef jerry_value_t (*jerryx_module_get_canonical_name_t) (const jerry_value_t name); +``` + +## jerryx_module_resolve_t + +**Summary** + +Function pointer type for module resolution. + +**Prototype** + +```c +typedef bool (*jerryx_module_resolve_t) (const jerry_value_t canonical_name, + jerry_value_t *result); ``` ## jerryx_module_resolver_t **Summary** -Function pointer type for a module resolver +Structure defining a module resolver. **Prototype** ```c -typedef bool (*jerryx_module_resolver_t) (const jerry_char_t *name, jerry_value_t *result); +typedef struct +{ + jerryx_module_get_canonical_name_t get_canonical_name_p; + jerryx_module_resolve_t resolve_p; +} jerryx_module_resolver_t; ``` +- `get_canonical_name_p` - function pointer to be called when the canonical name corresponding to the requested name +of a module must be established. +- `resolve_p` - function pointer to be called when a module with the given canonical name needs to be converted to the +`jerry_value_t` that will become the loaded module. + **Example** ```c -bool -load_and_evaluate_js_file (const jerry_char_t *name, jerry_value_t *result) +static bool +load_and_evaluate_js_file (const jerry_value_t name, jerry_value_t *result) { bool return_value = false; char *js_file_contents = NULL; int file_size = 0; - FILE *js_file = fopen (name, "r"); + + jerry_size_t name_size = jerry_get_utf8_string_size (name); + jerry_char_t name_string[name_size + 1]; + jerry_string_to_utf8_char_buffer (name, name_string, name_size); + name_string[name_size] = 0; + + FILE *js_file = fopen (name_string, "r"); if (js_file) { @@ -140,20 +180,44 @@ load_and_evaluate_js_file (const jerry_char_t *name, jerry_value_t *result) return return_value; } + +static jerry_value_t +canonicalize_file_path (const jerry_value_t name) +{ + jerry_value_t absolute_path; + + /** + * Since a file on the file system can be referred to by multiple relative paths, but only by one absolute path, the + * absolute path becomes the canonical name for the module. Thus, to establish this canonical name, we must search + * name for "./" and "../", follow symlinks, etc., then create absolute_path via jerry_create_string () and return + * it, because it is the canonical name for this module. Thus, we avoid loading the same JavaScript file twice. + */ + + return absolute_path; +} + +static jerryx_module_resolver_t js_file_loader +{ + canonicalize_file_path, + load_and_evaluate_js_file +}; ``` We can now load JavaScript files: ```c -static const jerryx_module_resolver_t resolvers[] = +static const jerryx_module_resolver_t *resolvers[] = { - /* Consult the JerryScript module resolver first, in case the requested module is a compiled-in JerryScript module. */ - jerryx_module_native_resolver, + /* + * Consult the resolver for native JerryScript modules first, in case the requested module is a native JerryScript + * module. + */ + &jerryx_module_native_resolver, /* - * If the requested module is not a JerryScript module, assume it is a JavaScript file on disk and use the above- - * defined JavaScript file loader to load it. + * If the requested module is not a native JerryScript module, assume it is a JavaScript file on disk and use the + * above-defined JavaScript file loader to load it. */ - load_and_evaluate_js_file + &js_file_loader }; jerry_value_t js_module = jerryx_module_resolve (requested_module, resolvers, 2); ``` @@ -164,9 +228,11 @@ jerry_value_t js_module = jerryx_module_resolve (requested_module, resolvers, 2) **Summary** -Helper macro to define a JerryScript module. Currently stores the name of the module and its initializer in an -executable linker section. This macro is available only if the preprocessor directive `JERRYX_NATIVE_MODULES_SUPPORTED` -is defined. +Helper macro to define a native JerryScript module. Currently declares a global static structure of type +`jerryx_native_module_t` and a constructor/destructor pair that calls `jerryx_native_module_register()` resp. +`jerryx_native_module_unregister()`. If the extension is built without the FEATURE_INIT_FINI flag, indicating that +support for library constructors and destructors is absent, the constructor and destructor are declared as global +symbols so that they may be called explicitly from within the application. **Note**: The helper macro must appear at the bottom of a source file, and no semicolon must follow it. @@ -175,7 +241,8 @@ is defined. #define JERRYX_NATIVE_MODULE(module_name, on_resolve_cb) ``` -- `module_name` - the name of the module without quotes +- `module_name` - the name of the module without quotes. This value is used as the prefix for the registration and unregistration funtions. For example, when `module_name` is `example_module`, this results in the declaration of two functions `example_module_register()` and `example_module_unregister()`. These functions are declared global if support for library constructors/destructors is absent, allowing you to call them from other parts of the code by +first forward-declaring them. - `on_resolve_cb` - the function of type `jerryx_native_module_on_resolve_t` that will be called when the module needs to be loaded. @@ -194,3 +261,38 @@ my_module_on_resolve (void) /* Note that there is no semicolon at the end of the next line. This is how it must be. */ JERRYX_NATIVE_MODULE (my_module, my_module_on_resolve) ``` + +**Example Usage When Library Constructors Are Unavailable** + +```c +#include "jerryscript.h" +#include "jerryscript-ext/module.h" + +/** + * Forward-declare the module registration and unregistration function. + */ +extern void my_module_register (void); +extern void my_module_unregister (void); +int +main (int argc, char **argv) +{ + jerryx_module_resolver_t resolvers[] = + { + jerryx_native_module_resolver + }; + + /* This plays the role of the library constructor. */ + my_module_register (); + + jerry_init (JERRY_INIT_EMPTY); + ... + jerry_value_t my_module = jerryx_module_resolve ("my_module", resolvers, 1); + ... + jerry_cleanup (); + + /* This plays the role of the library destructor */ + my_module_unregister(); + + return 0; +} +```