modules: add ability to clear cache (#2300)

This adds the ability to remove a single module from the cache, or to
clear the entire module cache.

JerryScript-DCO-1.0-Signed-off-by: Gabriel Schulhof gabriel.schulhof@intel.com
This commit is contained in:
Gabriel "_|Nix|_" Schulhof
2018-05-03 21:12:17 -04:00
committed by yichoi
parent 369447aa09
commit 6dc2764a94
4 changed files with 186 additions and 62 deletions
+26 -1
View File
@@ -27,6 +27,11 @@ The purpose of having resolvers is to be able to account for the fact that diffe
differently and thus, for each type of module a module resolver must be supplied at the point where an instance of that 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. type of module is requested.
Individual modules may be removed from the cache by calling `jerryx_module_clear_cache`. This function behaves
identically to `jerryx_module_resolve` in that it first checks the cache for the requested module, except that it
removes the module if found. Additionally, it clears the entire cache of all modules if called using a JavaScript value
of `undefined` as its first parameter.
Additionally, this extension provides a means of easily defining so-called "native" JerryScript modules which can be Additionally, this extension provides a means of easily defining so-called "native" JerryScript modules which can be
resolved using the native JerryScript 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 `jerryx_module_resolve()`. Native modules are registered during application startup and by calling `dlopen()` by means
@@ -56,7 +61,7 @@ to `jerryx_module_resolve` with a module name whose canonical name matches an al
```c ```c
jerry_value_t jerry_value_t
jerryx_module_resolve (const jerry_char_t *name, jerryx_module_resolve (const jerry_value_t name,
const jerryx_module_resolver_t *resolvers_p, const jerryx_module_resolver_t *resolvers_p,
size_t resolver_count); size_t resolver_count);
``` ```
@@ -67,6 +72,26 @@ jerryx_module_resolve (const jerry_char_t *name,
- return value - `jerry_value_t` representing the module that was loaded, or the error that occurred in the process. - return value - `jerry_value_t` representing the module that was loaded, or the error that occurred in the process.
## jerryx_module_clear_cache
**Summary**
Remove a module from the current context's cache, or clear the cache entirely.
**Prototype**
```c
void
jerryx_module_clear_cache (const jerry_value_t name,
const jerryx_module_resolver_t *resolvers_p,
size_t resolver_count);
```
- `name` - the name of the module to remove from cache or a JavaScript `undefined` to clear the entire cache
- `resolvers_p` - the list of resolvers to call in sequence
- `resolver_count` - the number of resolvers in `resolvers_p`
## jerryx_module_native_resolver ## jerryx_module_native_resolver
**Summary** **Summary**
@@ -134,6 +134,13 @@ jerry_value_t jerryx_module_resolve (const jerry_value_t name,
const jerryx_module_resolver_t **resolvers, const jerryx_module_resolver_t **resolvers,
size_t count); size_t count);
/**
* Delete a module from the cache or, if name has the JavaScript value of undefined, clear the entire cache.
*/
void jerryx_module_clear_cache (const jerry_value_t name,
const jerryx_module_resolver_t **resolvers,
size_t count);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */
+105 -56
View File
@@ -122,8 +122,11 @@ jerryx_module_check_cache (jerry_value_t cache, /**< cache from which to attempt
/* If the module is indeed in the cache, we return it. */ /* If the module is indeed in the cache, we return it. */
if (has_property) if (has_property)
{
if (result != NULL)
{ {
(*result) = jerry_get_property (cache, module_name); (*result) = jerry_get_property (cache, module_name);
}
ret = true; ret = true;
} }
} }
@@ -201,6 +204,86 @@ jerryx_module_resolver_t jerryx_module_native_resolver =
.resolve_p = jerryx_resolve_native_module .resolve_p = jerryx_resolve_native_module
}; };
static void
jerryx_module_resolve_local (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 */
jerry_value_t *result) /**< location to store the result, or NULL to remove the module */
{
size_t index;
size_t canonical_names_used = 0;
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);
if (!jerry_value_is_string (name))
{
if (result != NULL)
{
*result = 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], result))
{
/* A NULL for result indicates that we are to delete the module from the cache if found. Let's do that here.*/
if (result == NULL)
{
jerry_delete_property (instances, canonical_names[index]);
}
goto done;
}
}
if (result == NULL)
{
goto done;
}
/**
* Past this point we assume a module is wanted, and therefore result is not NULL. So, we try each resolver until one
* manages to resolve the module.
*/
for (index = 0; index < resolver_count; index++)
{
resolve_p = (resolvers_p[index] == NULL ? NULL : resolvers_p[index]->resolve_p);
if (resolve_p != NULL && resolve_p (canonical_names[index], result))
{
if (!jerry_value_is_error (*result))
{
*result = jerryx_module_add_to_cache (instances, canonical_names[index], *result);
}
goto done;
}
}
/* If none of the resolvers manage to find the module, complain with "Module not found" */
*result = jerryx_module_create_error (JERRY_ERROR_COMMON, module_not_found, name);
done:
/* Release the canonical names as returned by the various resolvers. */
for (index = 0; index < canonical_names_used; index++)
{
jerry_release_value (canonical_names[index]);
}
} /* jerryx_module_resolve_local */
/** /**
* Resolve a single module using the module resolvers available in the section declared above and load it into the * Resolve a single module using the module resolvers available in the section declared above and load it into the
* current context. * current context.
@@ -219,61 +302,27 @@ jerryx_module_resolve (const jerry_value_t name, /**< name of the module to load
const jerryx_module_resolver_t **resolvers_p, /**< list of resolvers */ const jerryx_module_resolver_t **resolvers_p, /**< list of resolvers */
size_t resolver_count) /**< number of resolvers in @p resolvers */ size_t resolver_count) /**< number of resolvers in @p resolvers */
{ {
size_t index; /* Set to zero to circumvent fatal warning. */
size_t canonical_names_used = 0; jerry_value_t ret = 0;
jerry_value_t ret; jerryx_module_resolve_local (name, resolvers_p, resolver_count, &ret);
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);
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++)
{
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_is_error (ret))
{
ret = jerryx_module_add_to_cache (instances, canonical_names[index], 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:
/* 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; return ret;
} /* jerryx_module_resolve */ } /* jerryx_module_resolve */
void
jerryx_module_clear_cache (const jerry_value_t name, /**< name of the module to remove, or undefined */
const jerryx_module_resolver_t **resolvers_p, /**< list of resolvers */
size_t resolver_count) /**< number of resolvers in @p resolvers */
{
void *instances_p = jerry_get_context_data (&jerryx_module_manager);
if (jerry_value_is_undefined (name))
{
/* We were requested to clear the entire cache, so we bounce the context data in the most agnostic way possible. */
jerryx_module_manager.deinit_cb (instances_p);
jerryx_module_manager.init_cb (instances_p);
return;
}
/* Delete the requested module from the cache if it's there. */
jerryx_module_resolve_local (name, resolvers_p, resolver_count, NULL);
} /* jerryx_module_clear_cache */
+47 -4
View File
@@ -60,6 +60,24 @@ const char eval_string5[] =
" return x === y ? 1 : 0;" " return x === y ? 1 : 0;"
"}) ();"; "}) ();";
/* Make sure the result of a module load is removed from the cache. */
const char eval_string6[] =
"(function() {"
" var x = require('cache-check');"
" clear_require_cache('cache-check');"
" var y = require('cache-check');"
" return x !== y ? 1 : 0;"
"}) ();";
/* Make sure the entire cache is cleared. */
const char eval_string7[] =
"(function() {"
" var x = require('cache-check');"
" clear_require_cache(undefined);"
" var y = require('cache-check');"
" return x !== y ? 1 : 0;"
"}) ();";
/* /*
* Define a resolver for a module named "differently-handled-module" to check that custom resolvers work. * Define a resolver for a module named "differently-handled-module" to check that custom resolvers work.
*/ */
@@ -119,6 +137,22 @@ static const jerryx_module_resolver_t *resolvers[3] =
&cache_check_resolver &cache_check_resolver
}; };
static jerry_value_t
handle_clear_require_cache (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;
TEST_ASSERT (args_count == 1);
jerryx_module_clear_cache (args_p[0], resolvers, 3);
return 0;
} /* handle_clear_require_cache */
static jerry_value_t static jerry_value_t
handle_require (const jerry_value_t js_function, handle_require (const jerry_value_t js_function,
const jerry_value_t this_val, const jerry_value_t this_val,
@@ -172,19 +206,28 @@ main (int argc, char **argv)
jerry_init (JERRY_INIT_EMPTY); jerry_init (JERRY_INIT_EMPTY);
js_global = jerry_get_global_object (); js_global = jerry_get_global_object ();
js_function = jerry_create_external_function (handle_require); js_function = jerry_create_external_function (handle_require);
js_property_name = jerry_create_string ((const jerry_char_t *) "require"); js_property_name = jerry_create_string ((const jerry_char_t *) "require");
jerry_set_property (js_global, js_property_name, js_function); jerry_set_property (js_global, js_property_name, js_function);
jerry_release_value (js_property_name);
jerry_release_value (js_function);
js_function = jerry_create_external_function (handle_clear_require_cache);
js_property_name = jerry_create_string ((const jerry_char_t *) "clear_require_cache");
jerry_set_property (js_global, js_property_name, js_function);
jerry_release_value (js_property_name);
jerry_release_value (js_function);
jerry_release_value (js_global);
eval_one (eval_string1, 42); eval_one (eval_string1, 42);
eval_one (eval_string2, 29); eval_one (eval_string2, 29);
eval_one (eval_string3, 1); eval_one (eval_string3, 1);
eval_one (eval_string4, 1); eval_one (eval_string4, 1);
eval_one (eval_string5, 1); eval_one (eval_string5, 1);
eval_one (eval_string6, 1);
jerry_release_value (js_property_name); eval_one (eval_string7, 1);
jerry_release_value (js_function);
jerry_release_value (js_global);
jerry_cleanup (); jerry_cleanup ();
} /* main */ } /* main */