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:
committed by
yichoi
parent
369447aa09
commit
6dc2764a94
@@ -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
@@ -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 */
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
Reference in New Issue
Block a user