Skip to content

Commit 08303d8

Browse files
committed
importer: Add 'clear_cache' function to allow reloading of modules.
We used to do something similar in very old versions of cjs before linuxmint/cinnamon@f01bd803d5ae89.
1 parent 3cb5802 commit 08303d8

2 files changed

Lines changed: 64 additions & 4 deletions

File tree

cjs/atoms.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class JSTracer;
1919
// clang-format off
2020
#define FOR_EACH_ATOM(macro) \
2121
macro(cause, "cause") \
22+
macro(clear_cache, "clearCache") \
2223
macro(code, "code") \
2324
macro(column_number, "columnNumber") \
2425
macro(connect_after, "connect_after") \

cjs/importer.cpp

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,50 @@ seal_import(JSContext *cx,
228228
return true;
229229
}
230230

231+
/* Clear a cached module so it can be re-imported.
232+
* This allows extension/applet reloading without restarting.
233+
*/
234+
GJS_JSAPI_RETURN_CONVENTION
235+
static bool
236+
importer_clear_cache(JSContext *cx,
237+
unsigned argc,
238+
JS::Value *vp)
239+
{
240+
GJS_GET_THIS(cx, argc, vp, args, importer);
241+
242+
if (args.length() < 1 || !args[0].isString()) {
243+
gjs_throw(cx, "clearCache requires a string argument");
244+
return false;
245+
}
246+
247+
JS::RootedString name_str(cx, args[0].toString());
248+
JS::UniqueChars name(JS_EncodeStringToUTF8(cx, name_str));
249+
if (!name)
250+
return false;
251+
252+
// Check if the property exists
253+
bool has_prop;
254+
if (!JS_HasOwnProperty(cx, importer, name.get(), &has_prop))
255+
return false;
256+
257+
if (!has_prop) {
258+
args.rval().setBoolean(false);
259+
return true;
260+
}
261+
262+
gjs_debug(GJS_DEBUG_IMPORTER,
263+
"Clearing cached import '%s'",
264+
name.get());
265+
266+
// Delete the cached module property
267+
JS::ObjectOpResult result;
268+
if (!JS_DeleteProperty(cx, importer, name.get(), result))
269+
return false;
270+
271+
args.rval().setBoolean(result.succeed());
272+
return true;
273+
}
274+
231275
/* An import failed. Delete the property pointing to the import
232276
* from the parent namespace. In complicated situations this might
233277
* not be sufficient to get us fully back to a sane state. If:
@@ -431,9 +475,23 @@ static bool attempt_import(JSContext* cx, JS::HandleObject obj,
431475

432476
GjsAutoChar full_path = g_file_get_parse_name(file);
433477

434-
return define_meta_properties(cx, module_obj, full_path, module_name,
435-
obj) &&
436-
seal_import(cx, obj, module_id, module_name);
478+
if (!define_meta_properties(cx, module_obj, full_path, module_name, obj))
479+
return false;
480+
481+
// Only seal imports on the root importer (where parent is null).
482+
// Sub-importers (like xlet importers) remain unsealed so their modules
483+
// can be cleared from cache and re-imported for xlet reloading.
484+
const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
485+
JS::RootedValue parent(cx);
486+
if (!JS_GetPropertyById(cx, obj, atoms.parent_module(), &parent))
487+
return false;
488+
489+
if (parent.isNull()) {
490+
if (!seal_import(cx, obj, module_id, module_name))
491+
return false;
492+
}
493+
494+
return true;
437495
}
438496

439497
GJS_JSAPI_RETURN_CONVENTION
@@ -728,7 +786,7 @@ importer_resolve(JSContext *context,
728786

729787
const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
730788
if (id == atoms.module_init() || id == atoms.to_string() ||
731-
id == atoms.value_of()) {
789+
id == atoms.value_of() || id == atoms.clear_cache()) {
732790
*resolved = false;
733791
return true;
734792
}
@@ -768,6 +826,7 @@ static const JSPropertySpec gjs_importer_proto_props[] = {
768826

769827
JSFunctionSpec gjs_importer_proto_funcs[] = {
770828
JS_FN("toString", importer_to_string, 0, 0),
829+
JS_FN("clearCache", importer_clear_cache, 1, 0),
771830
JS_FS_END};
772831

773832
[[nodiscard]] static const std::vector<std::string>& gjs_get_search_path() {

0 commit comments

Comments
 (0)