jerryscript/tests/unit-ext/module/jerry-module-test.c
Gabriel "_|Nix|_" Schulhof 66b2a7670f New extension: module (#1863)
This extension provides the following facilities:
  - registering modules,
  - module resolvers, and
  - an API for retrieving a module instance given its name.

A module is defined as a global static structure containing a pointer
to a string which is the name of the module, and a pointer to a function
which will be called when an instance of the module is needed.

A module resolver is a function that accepts a string holding the name
of the module and returns a `jerry_value_t` in an out-parameter and
`true` if the module was found, or `false` if it was not. If it returns
`true` and the out-parameter has the error flag set then the API will
pass it through without caching.

This extension provides a built-in module resolver which attempts to
load modules that follow the above module definition.

The API provided by this extension invokes all module resolvers it
receives in sequence to attempt to resolve the name of a single module.
After one resolver returns `true` and a `jerry_value_t` that represents
the module the API stops iterating over the remaining resolvers and
caches the value if its error flag is not set. It then returns the
`jerry_value_t`. The API will return a `jerry_value_t` containing an
error indicating that the module was not found if it reaches the end of
the list of resolvers. The error it returns has an extra property
`"moduleName"` the value of which is a string containing the name of the
module that the API was asked to resolve.

JerryScript-DCO-1.0-Signed-off-by: Gabriel Schulhof gabriel.schulhof@intel.com
2017-07-18 15:54:24 +02:00

169 lines
4.9 KiB
C

/* 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"
#ifdef JERRYX_NATIVE_MODULES_SUPPORTED
/* Load a module. */
const char eval_string1[] = "require ('my_custom_module');";
/* Load a module using a different resolver. */
const char eval_string2[] = "require ('differently-handled-module');";
/* Load a broken module using the built-in resolver. */
const char eval_string3[] =
"(function() {"
" var theError;"
" try {"
" require ('my_broken_module');"
" } catch (anError) {"
" theError = anError;"
" }"
" return (((theError.message === 'Module on_resolve () must not be NULL') &&"
" (theError.moduleName === 'my_broken_module') &&"
" (theError instanceof TypeError)) ? 1 : 0);"
"}) ();";
/* Load a non-existent module. */
const char eval_string4[] =
"(function() {"
" var theError;"
" try {"
" require ('some_missing_module_xyzzy');"
" } catch (anError) {"
" theError = anError;"
" }"
" return (((theError.message === 'Module not found') &&"
" (theError.moduleName === 'some_missing_module_xyzzy')) ? 1 : 0);"
"}) ();";
/* Make sure the result of a module load is cached. */
const char eval_string5[] =
"(function() {"
" var x = require('cache-check');"
" var y = require('cache-check');"
" return x === y ? 1 : 0;"
"}) ();";
static bool
resolve_differently_handled_module (const jerry_char_t *name,
jerry_value_t *result)
{
if (!strcmp ((char *) name, "differently-handled-module"))
{
(*result) = jerry_create_number (29);
return true;
}
return false;
} /* 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,
jerry_value_t *result)
{
if (!strcmp ((char *) name, "cache-check"))
{
(*result) = jerry_create_object ();
return true;
}
return false;
} /* cache_check */
static jerryx_module_resolver_t resolvers[3] =
{
jerryx_module_native_resolver,
resolve_differently_handled_module,
cache_check
};
static jerry_value_t
handle_require (const jerry_value_t js_function,
const jerry_value_t this_val,
const jerry_value_t args_p[],
const jerry_length_t args_count)
{
(void) js_function;
(void) this_val;
(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 return_value;
} /* handle_require */
static void
assert_number (jerry_value_t js_value, double expected_result)
{
TEST_ASSERT (!jerry_value_has_error_flag (js_value));
TEST_ASSERT (jerry_get_number_value (js_value) == expected_result);
} /* assert_number */
static void
eval_one (const char *the_string, double expected_result)
{
jerry_value_t js_eval_result = jerry_eval ((const jerry_char_t *) the_string, strlen (the_string), true);
assert_number (js_eval_result, expected_result);
jerry_release_value (js_eval_result);
} /* eval_one */
int
main (int argc, char **argv)
{
(void) argc;
(void) argv;
jerry_value_t js_global = 0, js_function = 0, js_property_name = 0;
jerry_init (JERRY_INIT_EMPTY);
js_global = jerry_get_global_object ();
js_function = jerry_create_external_function (handle_require);
js_property_name = jerry_create_string ((const jerry_char_t *) "require");
jerry_set_property (js_global, js_property_name, js_function);
eval_one (eval_string1, 42);
eval_one (eval_string2, 29);
eval_one (eval_string3, 1);
eval_one (eval_string4, 1);
eval_one (eval_string5, 1);
jerry_release_value (js_property_name);
jerry_release_value (js_function);
jerry_release_value (js_global);
jerry_cleanup ();
} /* main */
#endif /* JERRYX_NATIVE_MODULES_SUPPORTED */