diff --git a/fd.c b/fd.c index a8a48915..252610f3 100644 --- a/fd.c +++ b/fd.c @@ -668,17 +668,21 @@ JSC_CCALL(fd_realpath, #ifdef _WIN32 char resolved[PATH_MAX]; DWORD len = GetFullPathNameA(path, PATH_MAX, resolved, NULL); - JS_FreeCString(js, path); if (len == 0 || len >= PATH_MAX) { - return JS_ThrowInternalError(js, "realpath failed for %s: %s", path, strerror(errno)); + JSValue err = JS_ThrowInternalError(js, "realpath failed for %s: %s", path, strerror(errno)); + JS_FreeCString(js, path); + return err; } + JS_FreeCString(js, path); return JS_NewString(js, resolved); #else char *resolved = realpath(path, NULL); - JS_FreeCString(js, path); if (!resolved) { - return JS_ThrowInternalError(js, "realpath failed for %s: %s", path, strerror(errno)); + JSValue err = JS_ThrowInternalError(js, "realpath failed for %s: %s", path, strerror(errno)); + JS_FreeCString(js, path); + return err; } + JS_FreeCString(js, path); JSValue result = JS_NewString(js, resolved); free(resolved); return result; diff --git a/internal/engine.cm b/internal/engine.cm index 0c6fd3b1..011fdbbc 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -818,6 +818,7 @@ function enet_check() actor_mod.setname(_cell.args.program) var prog = _cell.args.program +if (ends_with(prog, '.ce')) prog = text(prog, 0, -3) var package = use_core('package') diff --git a/internal/engine.mach b/internal/engine.mach index 774ef07b..3ec759ec 100644 Binary files a/internal/engine.mach and b/internal/engine.mach differ diff --git a/source/runtime.c b/source/runtime.c index 708bcf0f..efca93e0 100644 --- a/source/runtime.c +++ b/source/runtime.c @@ -8255,12 +8255,17 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc, if (!result) { FMT_CLEANUP(); return JS_EXCEPTION; } res_ref.val = JS_MKPTR (result); } else { - JSValue orig = js_sub_string_val (ctx, text_ref.val, brace_start, brace_end + 1); - if (JS_IsException (orig)) { FMT_CLEANUP(); return JS_EXCEPTION; } + /* No substitution — treat the '{' as a literal character and rescan + from brace_start + 1 so that real placeholders like {0} inside + the skipped range are still found. */ + JSValue ch = js_sub_string_val (ctx, text_ref.val, brace_start, brace_start + 1); + if (JS_IsException (ch)) { FMT_CLEANUP(); return JS_EXCEPTION; } result = (JSText *)chase (res_ref.val); - result = pretext_concat_value (ctx, result, orig); + result = pretext_concat_value (ctx, result, ch); if (!result) { FMT_CLEANUP(); return JS_EXCEPTION; } res_ref.val = JS_MKPTR (result); + pos = brace_start + 1; + continue; } pos = brace_end + 1; diff --git a/vm_suite.ce b/vm_suite.ce index 9a9a2eeb..09a42edf 100644 --- a/vm_suite.ce +++ b/vm_suite.ce @@ -738,6 +738,95 @@ run("disruption re-raise", function() { if (!outer_caught) fail("disruption re-raise failed") }) +run("disruption handler reads outer vars", function() { + var msg = "hello" + var result = null + var fn = function() { + disrupt + } disruption { + result = msg + } + fn() + if (result != "hello") fail("handler could not read outer var") +}) + +run("disruption handler modifies outer vars", function() { + var count = 0 + var name = "before" + var fn = function() { + count = count + 1 + disrupt + } disruption { + count = count + 10 + name = "after" + } + fn() + if (count != 11) fail("expected 11 got " + text(count)) + if (name != "after") fail("expected 'after' got " + name) +}) + +run("disruption handler reads function locals", function() { + var result = null + var fn = function() { + var local_val = 42 + var inner = function() { + disrupt + } disruption { + result = local_val + } + inner() + } + fn() + if (result != 42) fail("handler could not read local, got " + text(result)) +}) + +run("disruption handler with closure", function() { + var results = [] + var make_fn = function(tag) { + return function() { + disrupt + } disruption { + results[] = tag + } + } + var fn_a = make_fn("a") + var fn_b = make_fn("b") + fn_a() + fn_b() + if (length(results) != 2) fail("expected 2 results") + if (results[0] != "a") fail("first closure tag wrong") + if (results[1] != "b") fail("second closure tag wrong") +}) + +run("disruption handler modifies loop state", function() { + var total = 0 + var i = 0 + var fn = null + while (i < 3) { + fn = function() { + disrupt + } disruption { + total = total + i + } + fn() + i = i + 1 + } + if (total != 3) fail("expected 3 got " + text(total)) +}) + +run("disruption handler accesses object from outer scope", function() { + var obj = {x: 1, y: 2} + var fn = function() { + obj.x = 10 + disrupt + } disruption { + obj.y = 20 + } + fn() + if (obj.x != 10) fail("body mutation lost, x=" + text(obj.x)) + if (obj.y != 20) fail("handler mutation lost, y=" + text(obj.y)) +}) + // ============================================================================ // TYPE CHECKING WITH is_* FUNCTIONS // ============================================================================