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
This commit is contained in:
committed by
Zoltan Herczeg
parent
5f2c72c472
commit
66b2a7670f
@@ -0,0 +1,186 @@
|
||||
# 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()`. Note, however, that native JerryScript modules are only supported and
|
||||
`jerryx_module_native_resolver()` is only compiled in if compiler support for `__attribute__` extensions is present. In
|
||||
effect this means that native JerryScript modules are available only when this extension is built with GCC or
|
||||
LLVM/clang. In the absence of such support, you may construct alternative module systems and provide your own resolver
|
||||
to `jerryx_module_resolve()`.
|
||||
|
||||
`jerryscript-ext/module.h` defines the preprocessor directive `JERRYX_NATIVE_MODULES_SUPPORTED` only if support for
|
||||
native JerryScript modules is available.
|
||||
|
||||
## 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**
|
||||
|
||||
```c
|
||||
jerry_value_t
|
||||
jerryx_module_resolve (const jerry_char_t *name,
|
||||
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 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 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)
|
||||
```
|
||||
- `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. This type is only defined if the
|
||||
preprocessor directive `JERRYX_NATIVE_MODULES_SUPPORTED` is defined.
|
||||
|
||||
**Prototype**
|
||||
|
||||
```c
|
||||
typedef jerry_value_t (*jerryx_native_module_on_resolve_t) (void);
|
||||
```
|
||||
|
||||
## jerryx_module_resolver_t
|
||||
|
||||
**Summary**
|
||||
|
||||
Function pointer type for a module resolver
|
||||
|
||||
**Prototype**
|
||||
|
||||
```c
|
||||
typedef bool (*jerryx_module_resolver_t) (const jerry_char_t *name, jerry_value_t *result);
|
||||
```
|
||||
|
||||
**Example**
|
||||
```c
|
||||
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:
|
||||
```c
|
||||
static const jerryx_module_resolver_t resolvers =
|
||||
{
|
||||
/* Consult the JerryScript module resolver first, in case the requested module is a compiled-in JerryScript module. */
|
||||
jerryx_module_native_resolver,
|
||||
|
||||
/*
|
||||
* If the requested module is not a 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 JerryScript module. Currently stores the name of the module and its initializer in an
|
||||
executable linker section. This macro is available only if the preprocessor directive `JERRYX_NATIVE_MODULES_SUPPORTED`
|
||||
is defined.
|
||||
|
||||
**Note**: The helper macro must appear at the bottom of a source file, and no semicolon must follow it.
|
||||
|
||||
**Prototype**
|
||||
```c
|
||||
#define JERRYX_NATIVE_MODULE(module_name, on_resolve_cb)
|
||||
```
|
||||
|
||||
- `module_name` - the name of the module without quotes
|
||||
- `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**
|
||||
|
||||
```c
|
||||
#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)
|
||||
```
|
||||
@@ -21,10 +21,12 @@ set(INCLUDE_EXT "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
||||
# Source directories
|
||||
file(GLOB SOURCE_EXT_ARG arg/*.c)
|
||||
file(GLOB SOURCE_EXT_MODULE module/*.c)
|
||||
file(GLOB SOURCE_EXT_HANDLER handler/*.c)
|
||||
|
||||
set(SOURCE_EXT
|
||||
${SOURCE_EXT_ARG}
|
||||
${SOURCE_EXT_MODULE}
|
||||
${SOURCE_EXT_HANDLER})
|
||||
|
||||
add_library(${JERRY_EXT_NAME} STATIC ${SOURCE_EXT})
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef JERRYX_MODULE_H
|
||||
#define JERRYX_MODULE_H
|
||||
|
||||
#include "jerryscript.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define JERRYX_NATIVE_MODULES_SUPPORTED
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
#ifdef JERRYX_NATIVE_MODULES_SUPPORTED
|
||||
#include "jerryscript-ext/section.impl.h"
|
||||
|
||||
/**
|
||||
* Declare the signature for the module initialization function.
|
||||
*/
|
||||
typedef jerry_value_t (*jerryx_native_module_on_resolve_t) (void);
|
||||
|
||||
/**
|
||||
* Declare the structure used to define a module. One should only make use of this structure via the
|
||||
* JERRYX_NATIVE_MODULE macro declared below.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
jerry_char_t *name; /**< name of the module */
|
||||
jerryx_native_module_on_resolve_t on_resolve; /**< function that returns a new instance of the module */
|
||||
} jerryx_native_module_t;
|
||||
|
||||
/**
|
||||
* Declare a helper macro that expands to the declaration of a variable of type jerryx_native_module_t placed into the
|
||||
* specially-named linker section "jerryx_modules" where the JerryScript module resolver
|
||||
* jerryx_module_native_resolver () will look for it.
|
||||
*/
|
||||
#define JERRYX_NATIVE_MODULE(module_name, on_resolve_cb) \
|
||||
static const jerryx_native_module_t _module JERRYX_SECTION_ATTRIBUTE(jerryx_modules) = \
|
||||
{ \
|
||||
.name = ((jerry_char_t *) #module_name), \
|
||||
.on_resolve = (on_resolve_cb) \
|
||||
};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
#endif /* JERRYX_NATIVE_MODULES_SUPPORTED */
|
||||
|
||||
/**
|
||||
* Declare the function pointer type for module resolvers.
|
||||
*/
|
||||
typedef bool (*jerryx_module_resolver_t) (const jerry_char_t *name, jerry_value_t *result);
|
||||
|
||||
/**
|
||||
* 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, jerryx_module_resolver_t *resolvers, size_t count);
|
||||
|
||||
#endif /* !JERRYX_MODULE_H */
|
||||
@@ -0,0 +1,56 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef JERRYX_SECTION_IMPL_H
|
||||
#define JERRYX_SECTION_IMPL_H
|
||||
|
||||
/**
|
||||
* Define the name of a section
|
||||
*
|
||||
* @param name the name of the section without quotes
|
||||
*/
|
||||
#ifdef __MACH__
|
||||
#define JERRYX_SECTION_NAME(name) "__DATA," #name
|
||||
#else /* !__MACH__ */
|
||||
#define JERRYX_SECTION_NAME(name) #name
|
||||
#endif /* __MACH__ */
|
||||
|
||||
/**
|
||||
* Expands to the proper __attribute__(()) qualifier for appending a variable to a section.
|
||||
*/
|
||||
#define JERRYX_SECTION_ATTRIBUTE(name) \
|
||||
__attribute__ ((used, section (JERRYX_SECTION_NAME (name)), aligned (sizeof (void *))))
|
||||
|
||||
/**
|
||||
* Declare references to a section that contains an array of items.
|
||||
*
|
||||
* @param name the name of the section (without quotes)
|
||||
* @param type the type of the elements stored in the array
|
||||
*
|
||||
* This macro declares two extern const variables such that their type is an array of @p type and their names are
|
||||
* constructed by prefixing @p name with "__start_" and "__stop_". They evaluate to the starting and ending address
|
||||
* of the section @p name.
|
||||
*/
|
||||
#ifdef __MACH__
|
||||
#define JERRYX_SECTION_DECLARE(name, type) \
|
||||
extern const type __start_ ## name[] __asm("section$start$__DATA$" #name); \
|
||||
extern const type __stop_ ## name[] __asm("section$end$__DATA$" #name);
|
||||
#else /* !__MACH__ */
|
||||
#define JERRYX_SECTION_DECLARE(name, type) \
|
||||
extern const type __start_ ## name[] __attribute__((weak)); \
|
||||
extern const type __stop_ ## name[] __attribute__((weak));
|
||||
#endif /* __MACH__ */
|
||||
|
||||
#endif /* !JERRYX_SECTION_IMPL_H */
|
||||
@@ -0,0 +1,217 @@
|
||||
/* 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 "jerryscript-ext/module.h"
|
||||
|
||||
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";
|
||||
|
||||
/**
|
||||
* Create an error related to modules
|
||||
*
|
||||
* Creates an error object of the requested type with the additional property "moduleName" the value of which is a
|
||||
* string containing the name of the module that was requested when the error occurred.
|
||||
*
|
||||
* @return the error
|
||||
*/
|
||||
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 */
|
||||
{
|
||||
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 (property_name);
|
||||
jerry_release_value (property_value);
|
||||
return ret;
|
||||
} /* jerryx_module_create_error */
|
||||
|
||||
/**
|
||||
* Initialize the module manager extension.
|
||||
*/
|
||||
static void
|
||||
jerryx_module_manager_init (void *user_data_p)
|
||||
{
|
||||
*((jerry_value_t *) user_data_p) = jerry_create_object ();
|
||||
} /* jerryx_module_manager_init */
|
||||
|
||||
/**
|
||||
* Deinitialize the module manager extension.
|
||||
*/
|
||||
static void
|
||||
jerryx_module_manager_deinit (void *user_data_p) /**< context pointer to deinitialize */
|
||||
{
|
||||
jerry_release_value (*(jerry_value_t *) user_data_p);
|
||||
} /* jerryx_module_manager_deinit */
|
||||
|
||||
/**
|
||||
* Declare the context data manager for modules.
|
||||
*/
|
||||
static const jerry_context_data_manager_t jerryx_module_manager =
|
||||
{
|
||||
.init_cb = jerryx_module_manager_init,
|
||||
.deinit_cb = jerryx_module_manager_deinit,
|
||||
.bytes_needed = sizeof (jerry_value_t)
|
||||
};
|
||||
|
||||
/**
|
||||
* Declare the linker section where module definitions are stored.
|
||||
*/
|
||||
JERRYX_SECTION_DECLARE (jerryx_modules, jerryx_native_module_t)
|
||||
|
||||
/**
|
||||
* Attempt to retrieve a module by name from a cache, and return false if not found.
|
||||
*/
|
||||
static bool
|
||||
jerryx_module_check_cache (jerry_value_t cache, /**< cache from which to attempt to retrieve the module by name */
|
||||
jerry_value_t module_name, /**< JerryScript string value holding the module name */
|
||||
jerry_value_t *result) /**< Resulting value */
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
/* Check if the cache has the module. */
|
||||
jerry_value_t js_has_property = jerry_has_property (cache, module_name);
|
||||
|
||||
/* If we succeed in getting an answer, we examine the answer. */
|
||||
if (!jerry_value_has_error_flag (js_has_property))
|
||||
{
|
||||
bool has_property = jerry_get_boolean_value (js_has_property);
|
||||
|
||||
/* If the module is indeed in the cache, we return it. */
|
||||
if (has_property)
|
||||
{
|
||||
(*result) = jerry_get_property (cache, module_name);
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
jerry_release_value (js_has_property);
|
||||
|
||||
return ret;
|
||||
} /* jerryx_module_check_cache */
|
||||
|
||||
/**
|
||||
* Attempt to cache a loaded module.
|
||||
*
|
||||
* @return the module on success, otherwise the error encountered when attempting to cache. In the latter case, the
|
||||
* @p module is released.
|
||||
*/
|
||||
static jerry_value_t
|
||||
jerryx_module_add_to_cache (jerry_value_t cache, /**< cache to which to add the module */
|
||||
jerry_value_t module_name, /**< key at which to cache the module */
|
||||
jerry_value_t module) /**< the module to cache */
|
||||
{
|
||||
jerry_value_t ret = jerry_set_property (cache, module_name, module);
|
||||
|
||||
if (jerry_value_has_error_flag (ret))
|
||||
{
|
||||
jerry_release_value (module);
|
||||
}
|
||||
else
|
||||
{
|
||||
jerry_release_value (ret);
|
||||
ret = module;
|
||||
}
|
||||
|
||||
return ret;
|
||||
} /* jerryx_module_add_to_cache */
|
||||
|
||||
#ifdef JERRYX_NATIVE_MODULES_SUPPORTED
|
||||
static const jerry_char_t *on_resolve_absent = (jerry_char_t *) "Module on_resolve () must not be NULL";
|
||||
|
||||
/**
|
||||
* Declare and define the default module resolver - one which examines what modules are defined in the above linker
|
||||
* 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 */
|
||||
{
|
||||
int index;
|
||||
const jerryx_native_module_t *module_p = NULL;
|
||||
|
||||
/* Look for the module by its name in the list of module definitions. */
|
||||
for (index = 0, module_p = &__start_jerryx_modules[0];
|
||||
&__start_jerryx_modules[index] < __stop_jerryx_modules;
|
||||
index++, module_p = &__start_jerryx_modules[index])
|
||||
{
|
||||
if (module_p->name != NULL && !strcmp ((char *) module_p->name, (char *) name))
|
||||
{
|
||||
/* 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));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} /* jerryx_module_native_resolver */
|
||||
#endif /* JERRYX_NATIVE_MODULES_SUPPORTED */
|
||||
|
||||
/**
|
||||
* Resolve a single module using the module resolvers available in the section declared above and load it into the
|
||||
* current context.
|
||||
*
|
||||
* @p name - name of the module to resolve
|
||||
* @p resolvers - list of resolvers to invoke
|
||||
* @p count - number of resolvers in the list
|
||||
*
|
||||
* @return a jerry_value_t containing one of the followings:
|
||||
* - the result of having loaded the module named @p name, or
|
||||
* - the result of a previous successful load, or
|
||||
* - 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 */
|
||||
jerryx_module_resolver_t *resolvers_p, /**< list of resolvers */
|
||||
size_t resolver_count) /**< number of resolvers in @p resolvers */
|
||||
{
|
||||
size_t index;
|
||||
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);
|
||||
|
||||
/* Return the cached instance if present. */
|
||||
if (jerryx_module_check_cache (instances, module_name, &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))
|
||||
{
|
||||
if (!jerry_value_has_error_flag (ret))
|
||||
{
|
||||
ret = jerryx_module_add_to_cache (instances, module_name, ret);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* If none of the resolvers manage to find the module, complain with "Module not found" */
|
||||
ret = jerryx_module_create_error (JERRY_ERROR_COMMON, module_not_found, name);
|
||||
|
||||
done:
|
||||
jerry_release_value (module_name);
|
||||
return ret;
|
||||
} /* jerryx_module_resolve */
|
||||
@@ -15,6 +15,8 @@
|
||||
cmake_minimum_required (VERSION 2.8.12)
|
||||
project (unit-ext C)
|
||||
|
||||
set(INCLUDE_UNIT_EXT ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# Unit tests main modules
|
||||
file(GLOB SOURCE_UNIT_TEST_EXT_MODULES *.c)
|
||||
|
||||
@@ -33,3 +35,10 @@ foreach(SOURCE_UNIT_TEST_EXT ${SOURCE_UNIT_TEST_EXT_MODULES})
|
||||
|
||||
add_dependencies(unittests-ext ${TARGET_NAME})
|
||||
endforeach()
|
||||
|
||||
file(GLOB CONTENTS_UNIT_TEST_EXT *)
|
||||
foreach(CONTENT_UNIT_TEST_EXT ${CONTENTS_UNIT_TEST_EXT})
|
||||
if(IS_DIRECTORY ${CONTENT_UNIT_TEST_EXT})
|
||||
add_subdirectory(${CONTENT_UNIT_TEST_EXT})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# 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.
|
||||
|
||||
cmake_minimum_required (VERSION 2.8.12)
|
||||
set(JERRYX_MODULE_UNITTEST_NAME unit-test-jerry-module)
|
||||
project (${JERRYX_MODULE_UNITTEST_NAME} C)
|
||||
|
||||
file(GLOB JERRYX_MODULE_UNIT_TEST_SOURCES *.c)
|
||||
|
||||
add_executable(${JERRYX_MODULE_UNITTEST_NAME} ${JERRYX_MODULE_UNIT_TEST_SOURCES})
|
||||
set_property(TARGET ${JERRYX_MODULE_UNITTEST_NAME} PROPERTY LINK_FLAGS "${LINKER_FLAGS_COMMON}")
|
||||
target_link_libraries(${JERRYX_MODULE_UNITTEST_NAME} jerry-ext jerry-core jerry-port-default-minimal)
|
||||
target_include_directories(${JERRYX_MODULE_UNITTEST_NAME} PRIVATE ${INCLUDE_UNIT_EXT})
|
||||
@@ -0,0 +1,168 @@
|
||||
/* 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 */
|
||||
@@ -0,0 +1,24 @@
|
||||
/* 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 "jerryscript.h"
|
||||
#include "jerryscript-ext/module.h"
|
||||
|
||||
#ifdef JERRYX_NATIVE_MODULES_SUPPORTED
|
||||
/*
|
||||
* A broken module to test that the loader complains about the absence of on_resolve ()
|
||||
*/
|
||||
JERRYX_NATIVE_MODULE (my_broken_module, NULL)
|
||||
#endif /* JERRYX_NATIVE_MODULES_SUPPORTED */
|
||||
@@ -0,0 +1,29 @@
|
||||
/* 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 "jerryscript.h"
|
||||
#include "jerryscript-ext/module.h"
|
||||
|
||||
#ifdef JERRYX_NATIVE_MODULES_SUPPORTED
|
||||
|
||||
static jerry_value_t
|
||||
my_custom_module_on_resolve (void)
|
||||
{
|
||||
return jerry_create_number (42);
|
||||
} /* my_custom_module_on_resolve */
|
||||
|
||||
JERRYX_NATIVE_MODULE (my_custom_module, my_custom_module_on_resolve)
|
||||
|
||||
#endif /* JERRYX_NATIVE_MODULES_SUPPORTED */
|
||||
@@ -0,0 +1,70 @@
|
||||
/* 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"
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
jerry_char_t buffer[256];
|
||||
jerry_size_t bytes_copied;
|
||||
jerryx_module_resolver_t resolver = jerryx_module_native_resolver;
|
||||
|
||||
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);
|
||||
|
||||
TEST_ASSERT (jerry_value_has_error_flag (module));
|
||||
|
||||
/* Retrieve the error message. */
|
||||
jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) "message");
|
||||
jerry_value_t prop = jerry_get_property (module, prop_name);
|
||||
|
||||
/* Assert that the error message is a string with specific contents. */
|
||||
TEST_ASSERT (jerry_value_is_string (prop));
|
||||
|
||||
bytes_copied = jerry_substring_to_utf8_char_buffer (prop, 0, 254, buffer, 256);
|
||||
buffer[bytes_copied] = 0;
|
||||
TEST_ASSERT (!strcmp ((const char *) buffer, "Module not found"));
|
||||
|
||||
/* Release the error message property name and value. */
|
||||
jerry_release_value (prop);
|
||||
jerry_release_value (prop_name);
|
||||
|
||||
/* Retrieve the moduleName property. */
|
||||
prop_name = jerry_create_string ((const jerry_char_t *) "moduleName");
|
||||
prop = jerry_get_property (module, prop_name);
|
||||
|
||||
/* Assert that the moduleName property is a string containing the requested module name. */
|
||||
TEST_ASSERT (jerry_value_is_string (prop));
|
||||
|
||||
bytes_copied = jerry_substring_to_utf8_char_buffer (prop, 0, 254, buffer, 256);
|
||||
buffer[bytes_copied] = 0;
|
||||
TEST_ASSERT (!strcmp ((const char *) buffer, "some-unknown-module-name"));
|
||||
|
||||
/* Release everything. */
|
||||
jerry_release_value (prop);
|
||||
jerry_release_value (prop_name);
|
||||
jerry_release_value (module);
|
||||
|
||||
return 0;
|
||||
} /* main */
|
||||
@@ -33,6 +33,7 @@ CODING_STANDARDS_MD="08.CODING-STANDARDS.md"
|
||||
EXT_REFERENCE_ARG_MD="09.EXT-REFERENCE-ARG.md"
|
||||
EXT_REFERENCE_HANDLER_MD="10.EXT-REFERENCE-HANDLER.md"
|
||||
EXT_REFERENCE_AUTORELEASE_MD="11.EXT-REFERENCE-AUTORELEASE.md"
|
||||
EXT_REFERENCE_MODULE_MD="12.EXT-REFERENCE-MODULE.md"
|
||||
|
||||
declare -A titles
|
||||
|
||||
@@ -47,6 +48,7 @@ titles[$CODING_STANDARDS_MD]="Coding Standards"
|
||||
titles[$EXT_REFERENCE_ARG_MD]="'Extension API: Argument Validation'"
|
||||
titles[$EXT_REFERENCE_HANDLER_MD]="'Extension API: External Function Handlers'"
|
||||
titles[$EXT_REFERENCE_AUTORELEASE_MD]="'Extension API: Autorelease Values'"
|
||||
titles[$EXT_REFERENCE_MODULE_MD]="'Extension API: Module Support'"
|
||||
|
||||
for docfile in $docs_dir/*.md; do
|
||||
docfile_base=`basename $docfile`
|
||||
|
||||
Reference in New Issue
Block a user