• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#  Module API
2
3This is a JerryScript extension that provides a means of loading modules. Fundamentally, a module is a name (stored as
4a string) that resolves to a `jerry_value_t`. This extension provides the function `jerryx_module_resolve()` which
5accepts the name of the module being requested as well as an array of so-called "resolvers" - structures containing two
6function pointers: one for a function which computes a canonical name for the requested module or returns a reference
7to the requested name, and one that converts a canonical name to a `jerry_value_t`, thus "resolving" or "loading" the
8requested module.
9
10The resolvers are first called in sequence to each compute the canonical name of the requested module. This is
11accomplished by calling the `get_canonical_name` function pointer they provide. If the function pointer is `NULL`, the
12requested module name is assumed to be what the resolver considers to be its canonical name. `jerryx_module_resolve`
13searches its cache of loaded modules for each canonical name as returned by a `get_canonical_name` function pointer. If
14one of the loaded modules in the cache corresponds to a canonical name, it is returned.
15
16If no cached module is found, `jerryx_module_resolve` calls each resolver's `resolve` function pointer, passing it its
17previously computed interpretation of the requested module's canonical name. If the resolver successfully creates the
18`jerry_value_t` that represents the loaded module, it returns `true` and the `jerry_value_t` in its out parameter.
19
20When `jerryx_module_resolve` receives a value of `true` from a resolver, it stops iterating over the remaining
21resolvers in the sequence and, if the `jerry_value_t` returned from the resolver's `resolve` does not have the error
22flag set, it will add the `jerry_value_t` to its cache under the module's canonical name and return it. Thus, on
23subsequent calls to `jerryx_module_resolve` with a module name whose canonical name is associated with the
24`jerry_value_t`, no `resolve` callback need be called again.
25
26The purpose of having resolvers is to be able to account for the fact that different types of modules may be structured
27differently and thus, for each type of module a module resolver must be supplied at the point where an instance of that
28type of module is requested.
29
30Individual modules may be removed from the cache by calling `jerryx_module_clear_cache`. This function behaves
31identically to `jerryx_module_resolve` in that it first checks the cache for the requested module, except that it
32removes the module if found. Additionally, it clears the entire cache of all modules if called using a JavaScript value
33of `undefined` as its first parameter.
34
35Additionally, this extension provides a means of easily defining so-called "native" JerryScript modules which can be
36resolved using the native JerryScript module resolver `jerryx_module_native_resolver`, which can be passed to
37`jerryx_module_resolve()`. Native modules are registered during application startup and by calling `dlopen()` by means
38of library constructors, support for which can be turned on using the `FEATURE_INIT_FINI` build flag. In the absence of
39such a flag, the module registration and unregistration functions are exposed as global symbols which can be called
40explicitly. Note: `FEATURE_INIT_FINI` build flag isn't supported on Windows, because Microsoft Visual C/C++ Compiler
41doesn't support library constructors and destructors.
42
43## jerryx_module_resolve
44
45**Summary**
46
47Load a copy of a module into the current context or return one that was already loaded if it is found.
48
49For each resolver passed in via `resolvers_p`, its `get_canonical_name` function pointer gets called in order to
50establish the resolver's interpretation of what the canonical name for the module should be. If `get_canonical_name` is
51`NULL`, it is assumed that the requested module's name as passed in is its canonical name.
52
53Then, for each resolver passed in via `resolvers_p`, its `resolve` function pointer gets called with its interpretation
54of what the module's canonical name should be, as computed in the previous step.
55
56If the resolver's `resolve` function pointer returns `true`, the `jerry_value_t` returned in its out-parameter will be
57returned by `jerryx_module_resolve` as the result of the request. If no error flag is set on the `jerry_value_t` it
58will be cached under its canonical name so as to avoid loading the same module twice in the event of a subsequent call
59to `jerryx_module_resolve` with a module name whose canonical name matches an already loaded module.
60
61**Prototype**
62
63```c
64jerry_value_t
65jerryx_module_resolve (const jerry_value_t name,
66                       const jerryx_module_resolver_t *resolvers_p,
67                       size_t resolver_count);
68```
69
70- `name` - the name of the module to load
71- `resolvers_p` - the list of resolvers to call in sequence
72- `resolver_count` - the number of resolvers in `resolvers_p`
73- return value - `jerry_value_t` representing the module that was loaded, or the error that occurred in the process.
74
75
76## jerryx_module_clear_cache
77
78**Summary**
79
80Remove a module from the current context's cache, or clear the cache entirely.
81
82**Prototype**
83
84```c
85void
86jerryx_module_clear_cache (const jerry_value_t name,
87                           const jerryx_module_resolver_t *resolvers_p,
88                           size_t resolver_count);
89```
90
91- `name` - the name of the module to remove from cache or a JavaScript `undefined` to clear the entire cache
92- `resolvers_p` - the list of resolvers to call in sequence
93- `resolver_count` - the number of resolvers in `resolvers_p`
94
95
96## jerryx_module_native_resolver
97
98**Summary**
99
100The resolver for native JerryScript modules. A pointer to this structure can be passed in the second parameter to
101`jerryx_module_resolve` to search for the module among the native JerryScript modules built into the binary. This
102function is available only if the preprocessor directive `JERRYX_NATIVE_MODULES_SUPPORTED` is defined.
103
104**Prototype**
105
106```c
107extern jerry_module_resolver_t jerryx_native_module_resolver;
108```
109
110# Module data types
111
112## jerryx_module_get_canonical_name_t
113
114**Summary**
115
116The function pointer type for converting a module's requested name to its canonical name.
117
118**Prototype**
119
120```c
121typedef jerry_value_t (*jerryx_module_get_canonical_name_t) (const jerry_value_t name);
122```
123
124## jerryx_module_resolve_t
125
126**Summary**
127
128Function pointer type for module resolution.
129
130**Prototype**
131
132```c
133typedef bool (*jerryx_module_resolve_t) (const jerry_value_t canonical_name,
134                                        jerry_value_t *result);
135```
136
137## jerryx_module_resolver_t
138
139**Summary**
140
141Structure defining a module resolver.
142
143**Prototype**
144
145```c
146typedef struct
147{
148  jerryx_module_get_canonical_name_t get_canonical_name_p;
149  jerryx_module_resolve_t resolve_p;
150} jerryx_module_resolver_t;
151```
152
153- `get_canonical_name_p` - function pointer to be called when the canonical name corresponding to the requested name
154of a module must be established.
155- `resolve_p` - function pointer to be called when a module with the given canonical name needs to be converted to the
156`jerry_value_t` that will become the loaded module.
157
158**Example**
159```c
160static bool
161load_and_evaluate_js_file (const jerry_value_t name, jerry_value_t *result)
162{
163  bool return_value = false;
164  char *js_file_contents = NULL;
165  int file_size = 0;
166
167  jerry_size_t name_size = jerry_get_utf8_string_size (name);
168  jerry_char_t name_string[name_size + 1];
169  jerry_string_to_utf8_char_buffer (name, name_string, name_size);
170  name_string[name_size] = 0;
171
172  FILE *js_file = fopen (name_string, "r");
173
174  if (js_file)
175  {
176    /* We have successfully opened the file. Now, we establish its size. */
177    file_size = fseek (js_file, 0, SEEK_END);
178    fseek (js_file, 0, SEEK_SET);
179
180    /* We allocate enough memory to store the contents of the file. */
181    js_file_contents = malloc (file_size);
182    if (js_file_contents)
183    {
184      /* We read the file into memory and call jerry_eval (), assigning the result to the out-parameter. */
185      fread (js_file_contents, file_size, 1, js_file);
186      (*result) = jerry_eval (js_file_contents, file_size, JERRY_PARSE_NO_OPTS);
187
188      /* We release the memory holding the contents of the file. */
189      free (js_file_contents);
190      return_value = true;
191    }
192
193    /* We close the file. */
194    fclose (js_file);
195  }
196
197  return return_value;
198}
199
200static jerry_value_t
201canonicalize_file_path (const jerry_value_t name)
202{
203  jerry_value_t absolute_path;
204
205  /**
206   * Since a file on the file system can be referred to by multiple relative paths, but only by one absolute path, the
207   * absolute path becomes the canonical name for the module. Thus, to establish this canonical name, we must search
208   * name for "./" and "../", follow symlinks, etc., then create absolute_path via jerry_create_string () and return
209   * it, because it is the canonical name for this module. Thus, we avoid loading the same JavaScript file twice.
210   */
211
212  return absolute_path;
213}
214
215static jerryx_module_resolver_t js_file_loader
216{
217  canonicalize_file_path,
218  load_and_evaluate_js_file
219};
220```
221
222We can now load JavaScript files:
223```c
224static const jerryx_module_resolver_t *resolvers[] =
225{
226  /*
227   * Consult the resolver for native JerryScript modules first, in case the requested module is a native JerryScript
228   * module.
229   */
230  &jerryx_module_native_resolver,
231
232  /*
233   * If the requested module is not a native JerryScript module, assume it is a JavaScript file on disk and use the
234   * above-defined JavaScript file loader to load it.
235   */
236  &js_file_loader
237};
238jerry_value_t js_module = jerryx_module_resolve (requested_module, resolvers, 2);
239```
240
241# Module helper macros
242
243## JERRYX_NATIVE_MODULE
244
245**Summary**
246
247Helper macro to define a native JerryScript module. Currently declares a global static structure of type
248`jerryx_native_module_t` and a constructor/destructor pair that calls `jerryx_native_module_register()` resp.
249`jerryx_native_module_unregister()`. If the extension is built without the FEATURE_INIT_FINI flag, indicating that
250support for library constructors and destructors is absent, the constructor and destructor are declared as global
251symbols so that they may be called explicitly from within the application.
252
253**Note**: The helper macro must appear at the bottom of a source file, and no semicolon must follow it.
254
255**Prototype**
256```c
257#define JERRYX_NATIVE_MODULE(module_name, on_resolve_cb)
258```
259
260- `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
261first forward-declaring them.
262- `on_resolve_cb` - the function of type `jerryx_native_module_on_resolve_t` that will be called when the module needs to be
263loaded.
264
265**Example**
266
267```c
268#include "jerryscript.h"
269#include "jerryscript-ext/module.h"
270
271static jerry_value_t
272my_module_on_resolve (void)
273{
274  return jerry_create_external_function (very_useful_function);
275} /* my_module_on_resolve */
276
277/* Note that there is no semicolon at the end of the next line. This is how it must be. */
278JERRYX_NATIVE_MODULE (my_module, my_module_on_resolve)
279```
280
281**Example Usage When Library Constructors Are Unavailable**
282
283```c
284#include "jerryscript.h"
285#include "jerryscript-ext/module.h"
286
287/**
288 * Forward-declare the module registration and unregistration function.
289 */
290extern void my_module_register (void);
291extern void my_module_unregister (void);
292int
293main (int argc, char **argv)
294{
295  jerryx_module_resolver_t resolvers[] =
296  {
297    jerryx_native_module_resolver
298  };
299
300  /* This plays the role of the library constructor. */
301  my_module_register ();
302
303  jerry_init (JERRY_INIT_EMPTY);
304  ...
305  jerry_value_t my_module = jerryx_module_resolve ("my_module", resolvers, 1);
306  ...
307  jerry_cleanup ();
308
309  /* This plays the role of the library destructor */
310  my_module_unregister();
311
312  return 0;
313}
314```
315