From 1cfd5b8133ef041b35b7be8340cbef7a9a8d1d9a Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Wed, 25 Feb 2026 17:28:11 -0600 Subject: [PATCH] add hot reload to util --- internal/os.c | 22 ++++++++++++++++++++++ internal/shop.cm | 30 ++++++++++++++++++++++-------- util.cm | 3 ++- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/internal/os.c b/internal/os.c index 6b99b1fd..5d7c0a70 100644 --- a/internal/os.c +++ b/internal/os.c @@ -703,6 +703,27 @@ static JSValue js_os_stack(JSContext *js, JSValue self, int argc, JSValue *argv) JS_RETURN(arr.val); } +static JSValue js_os_unstone(JSContext *js, JSValue self, int argc, JSValue *argv) { + if (argc < 1) return JS_NULL; + JSValue obj = argv[0]; + if (mist_is_blob(obj)) { + JSBlob *bd = (JSBlob *)chase(obj); + bd->mist_hdr = objhdr_set_s(bd->mist_hdr, false); + return obj; + } + if (JS_IsArray(obj)) { + JSArray *arr = JS_VALUE_GET_ARRAY(obj); + arr->mist_hdr = objhdr_set_s(arr->mist_hdr, false); + return obj; + } + if (mist_is_gc_object(obj)) { + JSRecord *rec = JS_VALUE_GET_RECORD(obj); + rec->mist_hdr = objhdr_set_s(rec->mist_hdr, false); + return obj; + } + return JS_NULL; +} + static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, platform, 0), MIST_FUNC_DEF(os, arch, 0), @@ -731,6 +752,7 @@ static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, getenv, 1), MIST_FUNC_DEF(os, qbe, 1), MIST_FUNC_DEF(os, stack, 1), + MIST_FUNC_DEF(os, unstone, 1), }; JSValue js_core_internal_os_use(JSContext *js) { diff --git a/internal/shop.cm b/internal/shop.cm index 1f98c20c..890c29e5 100644 --- a/internal/shop.cm +++ b/internal/shop.cm @@ -455,6 +455,7 @@ Shop.extract_commit_hash = function(pkg, response) { var open_dls = {} var package_dylibs = {} // pkg -> [{file, symbol, dylib}, ...] +var reload_hashes = {} // cache_key -> content hash for reload change detection function open_dylib_cached(path) { var handle = open_dls[path] @@ -1982,19 +1983,27 @@ Shop.file_reload = function(file) } Shop.module_reload = function(path, package) { - if (!Shop.is_loaded(path,package)) return + if (!Shop.is_loaded(path, package)) return false - // Clear the module info cache for this path var lookup_key = package ? package + ':' + path : ':' + path - module_info_cache[lookup_key] = null + var info = resolve_module_info(path, package) + if (!info) return false - // Invalidate package dylib cache so next resolve triggers rebuild - if (package) { - package_dylibs[package] = null + // Check if source actually changed + var mod_path = null + var source = null + var new_hash = null + if (info.mod_resolve) mod_path = info.mod_resolve.path + if (mod_path && fd.is_file(mod_path)) { + source = fd.slurp(mod_path) + new_hash = content_hash(stone(blob(text(source)))) + if (reload_hashes[info.cache_key] == new_hash) return false + reload_hashes[info.cache_key] = new_hash } - var info = resolve_module_info(path, package) - if (!info) return + // Clear caches + module_info_cache[lookup_key] = null + if (package) package_dylibs[package] = null var cache_key = info.cache_key var old = use_cache[cache_key] @@ -2003,13 +2012,18 @@ Shop.module_reload = function(path, package) { var newmod = get_module(path, package) use_cache[cache_key] = newmod + // Smart update: unstone -> merge -> re-stone to preserve references if (old && is_object(old) && is_object(newmod)) { + os.unstone(old) arrfor(array(newmod), function(k) { old[k] = newmod[k] }) arrfor(array(old), function(k) { if (!(k in newmod)) old[k] = null }) + stone(old) use_cache[cache_key] = old } + + return true } function get_package_scripts(package) diff --git a/util.cm b/util.cm index 1135c111..564d71ec 100644 --- a/util.cm +++ b/util.cm @@ -1,5 +1,6 @@ var shop = use('internal/shop') return { - file_reload: shop.file_reload + file_reload: shop.file_reload, + reload: shop.module_reload } \ No newline at end of file