jerryscript/docs/12.EXT-REFERENCE-MODULE.md
Gabriel "_|Nix|_" Schulhof 81952f3cd0 module: Re-implement using library constructors/destructors (#2018)
By using constructors/destructors we unify the case of static linking with
that of dynamic linking, and we reuse the build flag FEATURE_INIT_FINI. Using
constructors/destructors also allows us to cover the case where library
constructor/destructor functionality is unavailable, because we can expose the
module registration/unregistration functions as global symbols, to be called
explicitly from within the application.

Fixes https://github.com/jerryscript-project/jerryscript/issues/1952

JerryScript-DCO-1.0-Signed-off-by: Gabriel Schulhof gabriel.schulhof@intel.com
2017-09-22 12:35:38 +02:00

7.5 KiB

Module API

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.

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(). 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

Summary

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.

Prototype

jerry_value_t
jerryx_module_resolve (const jerry_char_t *name,
                       const jerryx_module_resolver_t *resolvers_p,
                       size_t resolver_count);
  • name - the name of the module to load
  • resolvers_p - the list of resolvers to call in sequence
  • resolver_count - the number of resolvers in resolvers_p
  • return value - jerry_value_t representing the module that was loaded, or the error that occurred in the process.

jerryx_module_native_resolver

Summary

The resolver for native 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 native JerryScript modules loaded so far.

Prototype

bool
jerryx_module_native_resolver (const jerry_char_t *name,
                               jerry_value_t *result)
  • 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

Summary

Function pointer type for a function that will create an instance of a native module.

Prototype

typedef jerry_value_t (*jerryx_native_module_on_resolve_t) (void);

jerryx_module_resolver_t

Summary

Function pointer type for a module resolver

Prototype

typedef bool (*jerryx_module_resolver_t) (const jerry_char_t *name, jerry_value_t *result);

Example

bool
load_and_evaluate_js_file (const jerry_char_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");

  if (js_file)
  {
    /* We have successfully opened the file. Now, we establish its size. */
    file_size = fseek (js_file, 0, SEEK_END);
    fseek (js_file, 0, SEEK_SET);

    /* We allocate enough memory to store the contents of the file. */
    js_file_contents = malloc (file_size);
    if (js_file_contents)
    {
      /* We read the file into memory and call jerry_eval (), assigning the result to the out-parameter. */
      fread (js_file_contents, file_size, 1, js_file);
      (*result) = jerry_eval (js_file_contents, file_size, false);

      /* We release the memory holding the contents of the file. */
      free (js_file_contents);
      return_value = true;
    }

    /* We close the file. */
    fclose (js_file);
  }

  return return_value;
}

We can now load JavaScript files:

static const jerryx_module_resolver_t resolvers[] =
{
  /*
   * Consult the JerryScript native module resolver first, in case the requested module is a native JerryScript
   * module.
   */
  jerryx_module_native_resolver,

  /*
   * 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
};
jerry_value_t js_module = jerryx_module_resolve (requested_module, resolvers, 2);

Module helper macros

JERRYX_NATIVE_MODULE

Summary

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.

Prototype

#define JERRYX_NATIVE_MODULE(module_name, on_resolve_cb)
  • 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.

Example

#include "jerryscript.h"
#include "jerryscript-ext/module.h"

static jerry_value_t
my_module_on_resolve (void)
{
  return jerry_create_external_function (very_useful_function);
} /* my_module_on_resolve */

/* 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

#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;
}