module extension: add support for canonical name resolution (#2013)
Before attempting to load a module, each provided resolver must be given an opportunity to examine the name of the requested module without actually loading it so as to canonicalize it, in case a module can be referred to by multiple names. Then, modules are loaded and cached by their canonical name. JerryScript-DCO-1.0-Signed-off-by: Gabriel Schulhof gabriel.schulhof@intel.com
This commit is contained in:
committed by
Zoltan Herczeg
parent
e527e41bac
commit
6d53931055
@@ -2,17 +2,33 @@
|
||||
|
||||
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
|
||||
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
|
||||
@@ -24,10 +40,17 @@ explicitly.
|
||||
|
||||
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**
|
||||
|
||||
@@ -48,56 +71,79 @@ jerryx_module_resolve (const jerry_char_t *name,
|
||||
|
||||
**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.
|
||||
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.
|
||||
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)
|
||||
{
|
||||
@@ -124,23 +170,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 native module resolver first, in case the requested module is a native JerryScript
|
||||
* Consult the resolver for native JerryScript modules first, in case the requested module is a native JerryScript
|
||||
* module.
|
||||
*/
|
||||
jerryx_module_native_resolver,
|
||||
&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
|
||||
&js_file_loader
|
||||
};
|
||||
jerry_value_t js_module = jerryx_module_resolve (requested_module, resolvers, 2);
|
||||
```
|
||||
|
||||
@@ -30,7 +30,7 @@ typedef jerry_value_t (*jerryx_native_module_on_resolve_t) (void);
|
||||
typedef struct jerryx_native_module_t
|
||||
{
|
||||
const jerry_char_t *name_p; /**< name of the module */
|
||||
const jerryx_native_module_on_resolve_t on_resolve; /**< function that returns a new instance of the module */
|
||||
const jerryx_native_module_on_resolve_t on_resolve_p; /**< function that returns a new instance of the module */
|
||||
struct jerryx_native_module_t *next_p; /**< pointer to next module in the list */
|
||||
} jerryx_native_module_t;
|
||||
|
||||
@@ -58,7 +58,7 @@ typedef struct jerryx_native_module_t
|
||||
static jerryx_native_module_t _ ## module_name ## _definition = \
|
||||
{ \
|
||||
.name_p = (jerry_char_t *) #module_name, \
|
||||
.on_resolve = (on_resolve_cb), \
|
||||
.on_resolve_p = (on_resolve_cb), \
|
||||
.next_p = NULL \
|
||||
}; \
|
||||
\
|
||||
@@ -91,21 +91,42 @@ void jerryx_native_module_register (jerryx_native_module_t *module_p);
|
||||
*/
|
||||
void jerryx_native_module_unregister (jerryx_native_module_t *module_p);
|
||||
|
||||
/**
|
||||
* Declare the function pointer type for canonical name resolution.
|
||||
*/
|
||||
typedef jerry_value_t (*jerryx_module_get_canonical_name_t) (const jerry_value_t name); /**< The name for which to
|
||||
* compute the canonical
|
||||
* name */
|
||||
|
||||
/**
|
||||
* Declare the function pointer type for module resolution.
|
||||
*/
|
||||
typedef bool (*jerryx_module_resolve_t) (const jerry_value_t canonical_name, /**< The module's canonical name */
|
||||
jerry_value_t *result); /**< The resulting module, if the function returns
|
||||
* true */
|
||||
|
||||
/**
|
||||
* Declare the structure for module resolvers.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
jerryx_module_get_canonical_name_t get_canonical_name_p; /**< function pointer to establish the canonical name of a
|
||||
* module */
|
||||
jerryx_module_resolve_t resolve_p; /**< function pointer to resolve a module */
|
||||
} jerryx_module_resolver_t;
|
||||
|
||||
/**
|
||||
* Declare the JerryScript module resolver so that it may be added to an array of jerryx_module_resolver_t items and
|
||||
* thus passed to jerryx_module_resolve.
|
||||
*/
|
||||
bool jerryx_module_native_resolver (const jerry_char_t *name, jerry_value_t *result);
|
||||
|
||||
/**
|
||||
* Declare the function pointer type for module resolvers.
|
||||
*/
|
||||
typedef bool (*jerryx_module_resolver_t) (const jerry_char_t *name, jerry_value_t *result);
|
||||
extern jerryx_module_resolver_t jerryx_module_native_resolver;
|
||||
|
||||
/**
|
||||
* Load a copy of a module into the current context using the provided module resolvers, or return one that was already
|
||||
* loaded if it is found.
|
||||
*/
|
||||
jerry_value_t jerryx_module_resolve (const jerry_char_t *name, const jerryx_module_resolver_t *resolvers, size_t count);
|
||||
jerry_value_t jerryx_module_resolve (const jerry_value_t name,
|
||||
const jerryx_module_resolver_t **resolvers,
|
||||
size_t count);
|
||||
|
||||
#endif /* !JERRYX_MODULE_H */
|
||||
|
||||
+58
-20
@@ -19,6 +19,7 @@
|
||||
|
||||
static const jerry_char_t *module_name_property_name = (jerry_char_t *) "moduleName";
|
||||
static const jerry_char_t *module_not_found = (jerry_char_t *) "Module not found";
|
||||
static const jerry_char_t *module_name_not_string = (jerry_char_t *) "Module name is not a string";
|
||||
|
||||
/**
|
||||
* Create an error related to modules
|
||||
@@ -31,15 +32,13 @@ static const jerry_char_t *module_not_found = (jerry_char_t *) "Module not found
|
||||
static jerry_value_t
|
||||
jerryx_module_create_error (jerry_error_t error_type, /**< the type of error to create */
|
||||
const jerry_char_t *message, /**< the error message */
|
||||
const jerry_char_t *module_name) /**< the module name */
|
||||
const jerry_value_t module_name) /**< the module name */
|
||||
{
|
||||
jerry_value_t ret = jerry_create_error (error_type, message);
|
||||
jerry_value_t property_name = jerry_create_string (module_name_property_name);
|
||||
jerry_value_t property_value = jerry_create_string_from_utf8 (module_name);
|
||||
|
||||
jerry_release_value (jerry_set_property (ret, property_name, property_value));
|
||||
jerry_release_value (jerry_set_property (ret, property_name, module_name));
|
||||
jerry_release_value (property_name);
|
||||
jerry_release_value (property_value);
|
||||
return ret;
|
||||
} /* jerryx_module_create_error */
|
||||
|
||||
@@ -167,26 +166,38 @@ static const jerry_char_t *on_resolve_absent = (jerry_char_t *) "Module on_resol
|
||||
* section and loads one that matches the requested name, caching the result for subsequent requests using the context
|
||||
* data mechanism.
|
||||
*/
|
||||
bool
|
||||
jerryx_module_native_resolver (const jerry_char_t *name, /**< name of the module */
|
||||
jerry_value_t *result) /**< [out] where to put the resulting module instance */
|
||||
static bool
|
||||
jerryx_resolve_native_module (const jerry_value_t canonical_name, /**< canonical name of the module */
|
||||
jerry_value_t *result) /**< [out] where to put the resulting module instance */
|
||||
{
|
||||
const jerryx_native_module_t *module_p = NULL;
|
||||
|
||||
jerry_size_t name_size = jerry_get_utf8_string_size (canonical_name);
|
||||
jerry_char_t name_string[name_size];
|
||||
jerry_string_to_utf8_char_buffer (canonical_name, name_string, name_size);
|
||||
|
||||
/* Look for the module by its name in the list of module definitions. */
|
||||
for (module_p = first_module_p; module_p != NULL; module_p = module_p->next_p)
|
||||
{
|
||||
if (module_p->name_p != NULL && !strcmp ((char *) module_p->name_p, (char *) name))
|
||||
if (module_p->name_p != NULL && !strncmp ((char *) module_p->name_p, (char *) name_string, name_size))
|
||||
{
|
||||
/* If we find the module by its name we load it and cache it if it has an on_resolve () and complain otherwise. */
|
||||
(*result) = ((module_p->on_resolve) ? module_p->on_resolve ()
|
||||
: jerryx_module_create_error (JERRY_ERROR_TYPE, on_resolve_absent, name));
|
||||
(*result) = ((module_p->on_resolve_p) ? module_p->on_resolve_p ()
|
||||
: jerryx_module_create_error (JERRY_ERROR_TYPE,
|
||||
on_resolve_absent,
|
||||
canonical_name));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} /* jerryx_module_native_resolver */
|
||||
} /* jerryx_resolve_native_module */
|
||||
|
||||
jerryx_module_resolver_t jerryx_module_native_resolver =
|
||||
{
|
||||
.get_canonical_name_p = NULL,
|
||||
.resolve_p = jerryx_resolve_native_module
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve a single module using the module resolvers available in the section declared above and load it into the
|
||||
@@ -202,29 +213,52 @@ jerryx_module_native_resolver (const jerry_char_t *name, /**< name of the module
|
||||
* - an error indicating that something went wrong during the attempt to load the module.
|
||||
*/
|
||||
jerry_value_t
|
||||
jerryx_module_resolve (const jerry_char_t *name, /**< name of the module to load */
|
||||
const jerryx_module_resolver_t *resolvers_p, /**< list of resolvers */
|
||||
jerryx_module_resolve (const jerry_value_t name, /**< name of the module to load */
|
||||
const jerryx_module_resolver_t **resolvers_p, /**< list of resolvers */
|
||||
size_t resolver_count) /**< number of resolvers in @p resolvers */
|
||||
{
|
||||
size_t index;
|
||||
size_t canonical_names_used = 0;
|
||||
jerry_value_t ret;
|
||||
jerry_value_t instances = *(jerry_value_t *) jerry_get_context_data (&jerryx_module_manager);
|
||||
jerry_value_t module_name = jerry_create_string_from_utf8 (name);
|
||||
jerry_value_t instances;
|
||||
jerry_value_t canonical_names[resolver_count];
|
||||
jerry_value_t (*get_canonical_name_p) (const jerry_value_t name);
|
||||
bool (*resolve_p) (const jerry_value_t canonical_name,
|
||||
jerry_value_t *result);
|
||||
|
||||
/* Return the cached instance if present. */
|
||||
if (jerryx_module_check_cache (instances, module_name, &ret))
|
||||
if (!jerry_value_is_string (name))
|
||||
{
|
||||
ret = jerryx_module_create_error (JERRY_ERROR_COMMON, module_name_not_string, name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
instances = *(jerry_value_t *) jerry_get_context_data (&jerryx_module_manager);
|
||||
|
||||
/**
|
||||
* Establish the canonical name for the requested module. Each resolver presents its own canonical name. If one of
|
||||
* the canonical names matches a cached module, it is returned as the result.
|
||||
*/
|
||||
for (index = 0; index < resolver_count; index++)
|
||||
{
|
||||
get_canonical_name_p = (resolvers_p[index] == NULL ? NULL : resolvers_p[index]->get_canonical_name_p);
|
||||
canonical_names[index] = ((get_canonical_name_p == NULL) ? jerry_acquire_value (name)
|
||||
: get_canonical_name_p (name));
|
||||
canonical_names_used++;
|
||||
if (jerryx_module_check_cache (instances, canonical_names[index], &ret))
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try each resolver until one manages to find the module. */
|
||||
for (index = 0; index < resolver_count; index++)
|
||||
{
|
||||
if ((*resolvers_p[index]) (name, &ret))
|
||||
resolve_p = (resolvers_p[index] == NULL ? NULL : resolvers_p[index]->resolve_p);
|
||||
if (resolve_p != NULL && resolve_p (canonical_names[index], &ret))
|
||||
{
|
||||
if (!jerry_value_has_error_flag (ret))
|
||||
{
|
||||
ret = jerryx_module_add_to_cache (instances, module_name, ret);
|
||||
ret = jerryx_module_add_to_cache (instances, canonical_names[index], ret);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
@@ -234,6 +268,10 @@ jerryx_module_resolve (const jerry_char_t *name, /**< name of the module to load
|
||||
ret = jerryx_module_create_error (JERRY_ERROR_COMMON, module_not_found, name);
|
||||
|
||||
done:
|
||||
jerry_release_value (module_name);
|
||||
/* Release the canonical names as returned by the various resolvers. */
|
||||
for (index = 0; index < canonical_names_used; index++)
|
||||
{
|
||||
jerry_release_value (canonical_names[index]);
|
||||
}
|
||||
return ret;
|
||||
} /* jerryx_module_resolve */
|
||||
|
||||
@@ -60,11 +60,18 @@ const char eval_string5[] =
|
||||
" return x === y ? 1 : 0;"
|
||||
"}) ();";
|
||||
|
||||
/*
|
||||
* Define a resolver for a module named "differently-handled-module" to check that custom resolvers work.
|
||||
*/
|
||||
static bool
|
||||
resolve_differently_handled_module (const jerry_char_t *name,
|
||||
jerry_value_t *result)
|
||||
resolve_differently_handled_module (const jerry_value_t name,
|
||||
jerry_value_t *result)
|
||||
{
|
||||
if (!strcmp ((char *) name, "differently-handled-module"))
|
||||
jerry_size_t name_size = jerry_get_utf8_string_size (name);
|
||||
jerry_char_t name_string[name_size];
|
||||
jerry_string_to_utf8_char_buffer (name, name_string, name_size);
|
||||
|
||||
if (!strncmp ((char *) name_string, "differently-handled-module", name_size))
|
||||
{
|
||||
(*result) = jerry_create_number (29);
|
||||
return true;
|
||||
@@ -72,16 +79,26 @@ resolve_differently_handled_module (const jerry_char_t *name,
|
||||
return false;
|
||||
} /* resolve_differently_handled_module */
|
||||
|
||||
static jerryx_module_resolver_t differently_handled_module_resolver =
|
||||
{
|
||||
NULL,
|
||||
resolve_differently_handled_module
|
||||
};
|
||||
|
||||
/*
|
||||
* Define module "cache-check" via its own resolver as an empty object. Since objects are accessible only via references
|
||||
* we can strictly compare the object returned on subsequent attempts at loading "cache-check" with the object returned
|
||||
* on the first attempt and establish that the two are in fact the same object - which in turn shows that caching works.
|
||||
*/
|
||||
static bool
|
||||
cache_check (const jerry_char_t *name,
|
||||
cache_check (const jerry_value_t name,
|
||||
jerry_value_t *result)
|
||||
{
|
||||
if (!strcmp ((char *) name, "cache-check"))
|
||||
jerry_size_t name_size = jerry_get_utf8_string_size (name);
|
||||
jerry_char_t name_string[name_size];
|
||||
jerry_string_to_utf8_char_buffer (name, name_string, name_size);
|
||||
|
||||
if (!strncmp ((char *) name_string, "cache-check", name_size))
|
||||
{
|
||||
(*result) = jerry_create_object ();
|
||||
return true;
|
||||
@@ -89,13 +106,19 @@ cache_check (const jerry_char_t *name,
|
||||
return false;
|
||||
} /* cache_check */
|
||||
|
||||
static const jerryx_module_resolver_t resolvers[3] =
|
||||
static jerryx_module_resolver_t cache_check_resolver =
|
||||
{
|
||||
jerryx_module_native_resolver,
|
||||
resolve_differently_handled_module,
|
||||
NULL,
|
||||
cache_check
|
||||
};
|
||||
|
||||
static const jerryx_module_resolver_t *resolvers[3] =
|
||||
{
|
||||
&jerryx_module_native_resolver,
|
||||
&differently_handled_module_resolver,
|
||||
&cache_check_resolver
|
||||
};
|
||||
|
||||
static jerry_value_t
|
||||
handle_require (const jerry_value_t js_function,
|
||||
const jerry_value_t this_val,
|
||||
@@ -107,16 +130,9 @@ handle_require (const jerry_value_t js_function,
|
||||
(void) args_count;
|
||||
|
||||
jerry_value_t return_value = 0;
|
||||
jerry_char_t module_name[256] = "";
|
||||
jerry_size_t bytes_copied = 0;
|
||||
|
||||
TEST_ASSERT (args_count == 1);
|
||||
bytes_copied = jerry_string_to_char_buffer (args_p[0], module_name, 256);
|
||||
if (bytes_copied < 256)
|
||||
{
|
||||
module_name[bytes_copied] = 0;
|
||||
return_value = jerryx_module_resolve (module_name, resolvers, 3);
|
||||
}
|
||||
return_value = jerryx_module_resolve (args_p[0], resolvers, 3);
|
||||
|
||||
return return_value;
|
||||
} /* handle_require */
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "jerryscript.h"
|
||||
#include "test-common.h"
|
||||
#include "jerryscript-ext/module.h"
|
||||
|
||||
#define ACTUAL_NAME "alice"
|
||||
#define ALIAS_NAME "bob"
|
||||
|
||||
static jerry_value_t
|
||||
get_canonical_name (const jerry_value_t name)
|
||||
{
|
||||
jerry_size_t name_size = jerry_get_string_size (name);
|
||||
jerry_char_t name_string[name_size + 1];
|
||||
jerry_string_to_char_buffer (name, name_string, name_size);
|
||||
name_string[name_size] = 0;
|
||||
|
||||
if (!strcmp ((char *) name_string, ACTUAL_NAME))
|
||||
{
|
||||
return jerry_acquire_value (name);
|
||||
}
|
||||
else if (!strcmp ((char *) name_string, ALIAS_NAME))
|
||||
{
|
||||
return jerry_create_string ((jerry_char_t *) ACTUAL_NAME);
|
||||
}
|
||||
else
|
||||
{
|
||||
return jerry_create_undefined ();
|
||||
}
|
||||
} /* get_canonical_name */
|
||||
|
||||
static bool
|
||||
resolve (const jerry_value_t canonical_name, jerry_value_t *result)
|
||||
{
|
||||
jerry_size_t name_size = jerry_get_string_size (canonical_name);
|
||||
jerry_char_t name_string[name_size + 1];
|
||||
jerry_string_to_char_buffer (canonical_name, name_string, name_size);
|
||||
name_string[name_size] = 0;
|
||||
|
||||
if (!strcmp ((char *) name_string, ACTUAL_NAME))
|
||||
{
|
||||
*result = jerry_create_object ();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} /* resolve */
|
||||
|
||||
static const jerryx_module_resolver_t canonical_test =
|
||||
{
|
||||
.get_canonical_name_p = get_canonical_name,
|
||||
.resolve_p = resolve
|
||||
};
|
||||
|
||||
#define TEST_VALUE 95.0
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
const jerryx_module_resolver_t *resolver = &canonical_test;
|
||||
|
||||
jerry_init (JERRY_INIT_EMPTY);
|
||||
|
||||
jerry_value_t actual_name = jerry_create_string ((jerry_char_t *) ACTUAL_NAME);
|
||||
jerry_value_t alias_name = jerry_create_string ((jerry_char_t *) ALIAS_NAME);
|
||||
|
||||
/* It's important that we resolve by the non-canonical name first. */
|
||||
jerry_value_t result2 = jerryx_module_resolve (alias_name, &resolver, 1);
|
||||
jerry_value_t result1 = jerryx_module_resolve (actual_name, &resolver, 1);
|
||||
jerry_release_value (actual_name);
|
||||
jerry_release_value (alias_name);
|
||||
|
||||
/* An elaborate way of doing strict equal - set a property on one object and it "magically" appears on the other. */
|
||||
jerry_value_t prop_name = jerry_create_string ((jerry_char_t *) "something");
|
||||
jerry_value_t prop_value = jerry_create_number (TEST_VALUE);
|
||||
jerry_release_value (jerry_set_property (result1, prop_name, prop_value));
|
||||
jerry_release_value (prop_value);
|
||||
|
||||
prop_value = jerry_get_property (result2, prop_name);
|
||||
TEST_ASSERT (jerry_get_number_value (prop_value) == TEST_VALUE);
|
||||
jerry_release_value (prop_value);
|
||||
|
||||
jerry_release_value (prop_name);
|
||||
jerry_release_value (result1);
|
||||
jerry_release_value (result2);
|
||||
|
||||
jerry_cleanup ();
|
||||
|
||||
return 0;
|
||||
} /* main */
|
||||
@@ -26,12 +26,15 @@ main (int argc, char **argv)
|
||||
(void) argv;
|
||||
jerry_char_t buffer[256];
|
||||
jerry_size_t bytes_copied;
|
||||
jerryx_module_resolver_t resolver = jerryx_module_native_resolver;
|
||||
const jerryx_module_resolver_t *resolver = &jerryx_module_native_resolver;
|
||||
jerry_value_t module_name;
|
||||
|
||||
jerry_init (JERRY_INIT_EMPTY);
|
||||
|
||||
/* Attempt to load a non-existing module. */
|
||||
jerry_value_t module = jerryx_module_resolve ((jerry_char_t *) "some-unknown-module-name", &resolver, 1);
|
||||
module_name = jerry_create_string ((jerry_char_t *) "some-unknown-module-name");
|
||||
jerry_value_t module = jerryx_module_resolve (module_name, &resolver, 1);
|
||||
jerry_release_value (module_name);
|
||||
|
||||
TEST_ASSERT (jerry_value_has_error_flag (module));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user