aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel "_|Nix|_" Schulhof <gabriel.schulhof@intel.com>2017-09-29 13:02:34 +0300
committerZoltan Herczeg <zherczeg.u-szeged@partner.samsung.com>2017-09-29 12:02:34 +0200
commit6d53931055747d54c78620629714ea2e72c3ff30 (patch)
tree534635cce91e1057fa1a7730f2478b09665aa014
parente527e41bacad86619f9336acc60a3204b8af7d32 (diff)
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
-rw-r--r--docs/12.EXT-REFERENCE-MODULE.md127
-rw-r--r--jerry-ext/include/jerryscript-ext/module.h37
-rw-r--r--jerry-ext/module/module.c78
-rw-r--r--tests/unit-ext/module/jerry-module-test.c48
-rw-r--r--tests/unit-ext/test-ext-module-canonical.c107
-rw-r--r--tests/unit-ext/test-ext-module-empty.c7
6 files changed, 328 insertions, 76 deletions
diff --git a/docs/12.EXT-REFERENCE-MODULE.md b/docs/12.EXT-REFERENCE-MODULE.md
index 96ed5698..0abbe04f 100644
--- a/docs/12.EXT-REFERENCE-MODULE.md
+++ b/docs/12.EXT-REFERENCE-MODULE.md
@@ -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**
+
+The function pointer type for converting a module's requested name to its canonical name.
+
+**Prototype**
+
+```c
+typedef jerry_value_t (*jerryx_module_get_canonical_name_t) (const jerry_value_t name);
+```
+
+## jerryx_module_resolve_t
**Summary**
-Function pointer type for a function that will create an instance of a native module.
+Function pointer type for module resolution.
**Prototype**
```c
-typedef jerry_value_t (*jerryx_native_module_on_resolve_t) (void);
+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);
```
diff --git a/jerry-ext/include/jerryscript-ext/module.h b/jerry-ext/include/jerryscript-ext/module.h
index 778a3bf9..31df799e 100644
--- a/jerry-ext/include/jerryscript-ext/module.h
+++ b/jerry-ext/include/jerryscript-ext/module.h
@@ -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 \
}; \
\
@@ -92,20 +92,41 @@ void jerryx_native_module_register (jerryx_native_module_t *module_p);
void jerryx_native_module_unregister (jerryx_native_module_t *module_p);
/**
- * 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.
+ * 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.
*/
-bool jerryx_module_native_resolver (const jerry_char_t *name, jerry_value_t *result);
+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 function pointer type for module resolvers.
+ * 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.
*/
-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 */
diff --git a/jerry-ext/module/module.c b/jerry-ext/module/module.c
index 406e9c7c..30e75ede 100644
--- a/jerry-ext/module/module.c
+++ b/jerry-ext/module/module.c
@@ -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 */
diff --git a/tests/unit-ext/module/jerry-module-test.c b/tests/unit-ext/module/jerry-module-test.c
index 492ad086..b60936ee 100644
--- a/tests/unit-ext/module/jerry-module-test.c
+++ b/tests/unit-ext/module/jerry-module-test.c
@@ -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 */
diff --git a/tests/unit-ext/test-ext-module-canonical.c b/tests/unit-ext/test-ext-module-canonical.c
new file mode 100644
index 00000000..9cdbd32d
--- /dev/null
+++ b/tests/unit-ext/test-ext-module-canonical.c
@@ -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 */
diff --git a/tests/unit-ext/test-ext-module-empty.c b/tests/unit-ext/test-ext-module-empty.c
index 9b108a54..aaf6cddd 100644
--- a/tests/unit-ext/test-ext-module-empty.c
+++ b/tests/unit-ext/test-ext-module-empty.c
@@ -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));