From 012b5074155c29168e41c22e5d1076cc7f9bbfea Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sun, 22 Feb 2026 11:24:27 -0600 Subject: [PATCH] add -e flag --- internal/engine.cm | 72 +++++++++++++++++++++++++++++++++++++++------- source/cell.c | 17 +++++++++-- vm_suite.ce | 38 ++++++++++++++++++++---- 3 files changed, 110 insertions(+), 17 deletions(-) diff --git a/internal/engine.cm b/internal/engine.cm index 009172b6..604afdc5 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -435,6 +435,12 @@ if (args != null && (_init == null || !_init.program)) { } } +// -e flag: eval script string directly +var _eval_script = (_init != null && _init.eval_script) ? _init.eval_script : null +if (_eval_script && !_init.program) { + _init.program = "-e" +} + use_cache['core/internal/os'] = os // Extra env properties added as engine initializes (log, runtime fns, etc.) @@ -1426,6 +1432,47 @@ function enet_check() // Finally, run the program actor_mod.setname(_cell.args.program) +if (_eval_script) { + // -e flag: compile and run inline script + $_.clock(_ => { + var env = {} + arrfor(array(runtime_env), function(k) { env[k] = runtime_env[k] }) + env.use = function(path) { + var ck = 'core/' + path + var _use_core_result = null + var _use_core_ok = false + if (use_cache[ck]) return use_cache[ck] + var _try_core = function() { + _use_core_result = use_core(path) + _use_core_ok = true + } disruption {} + _try_core() + if (_use_core_ok && _use_core_result) return _use_core_result + var _shop_use = function() { + return shop.use(path, null) + } disruption { + log.error(`use('${path}') failed`) + disrupt + } + return _shop_use() + } + env.args = _cell.args.arg + env.log = log + env = stone(env) + + var ast = analyze(_eval_script, "-e") + var val = null + var _compile = function() { + var mach_blob = compile_user_blob("-e", ast, null) + val = mach_load(mach_blob, env) + } disruption { + os.exit(1) + } + _compile() + }) + return null +} + var prog = _cell.args.program if (ends_with(prog, '.cm')) { os.print(`error: ${prog} is a module (.cm), not a program (.ce)\n`) @@ -1562,18 +1609,23 @@ $_.clock(_ => { var script = null var ast = null var mach_blob = null - if (cached_path && fd.is_file(cached_path)) { - val = mach_load(fd.slurp(cached_path), env) - } else { - script = text(source_blob) - ast = analyze(script, prog_path) - mach_blob = compile_user_blob(prog, ast, pkg) - if (cached_path) { - ensure_build_dir() - fd.slurpwrite(cached_path, mach_blob) + var _compile = function() { + if (cached_path && fd.is_file(cached_path)) { + val = mach_load(fd.slurp(cached_path), env) + } else { + script = text(source_blob) + ast = analyze(script, prog_path) + mach_blob = compile_user_blob(prog, ast, pkg) + if (cached_path) { + ensure_build_dir() + fd.slurpwrite(cached_path, mach_blob) + } + val = mach_load(mach_blob, env) } - val = mach_load(mach_blob, env) + } disruption { + os.exit(1) } + _compile() if (val) { log.error('Program must not return anything') disrupt diff --git a/source/cell.c b/source/cell.c index ab221cc3..9e51b866 100644 --- a/source/cell.c +++ b/source/cell.c @@ -455,6 +455,7 @@ static void print_usage(const char *prog) printf(" --native Use AOT native code instead of bytecode\n"); printf(" --heap Initial heap size (e.g. 256MB, 1GB)\n"); printf(" --test [heap_size] Run C test suite\n"); + printf(" -e Evaluate code string as a program\n"); printf(" -h, --help Show this help message\n"); printf("\nEnvironment:\n"); printf(" CELL_CORE Core path (default: /packages/core)\n"); @@ -490,6 +491,7 @@ int cell_init(int argc, char **argv) size_t heap_size = 1024 * 1024; /* 1MB default */ const char *shop_override = NULL; const char *core_override = NULL; + const char *eval_script = NULL; // Parse flags (order-independent) while (arg_start < argc && argv[arg_start][0] == '-') { @@ -536,12 +538,19 @@ int cell_init(int argc, char **argv) } else if (strcmp(argv[arg_start], "--no-warn") == 0) { warn_mode = 0; arg_start++; + } else if (strcmp(argv[arg_start], "-e") == 0) { + if (arg_start + 1 >= argc) { + printf("ERROR: -e requires a code string argument\n"); + return 1; + } + eval_script = argv[arg_start + 1]; + arg_start += 2; } else { break; } } - if (arg_start >= argc) { + if (arg_start >= argc && !eval_script) { print_usage(argv[0]); return 1; } @@ -675,7 +684,7 @@ int cell_init(int argc, char **argv) JS_SetPropertyStr(ctx, env_ref.val, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val)); tmp = js_core_json_use(ctx); JS_SetPropertyStr(ctx, env_ref.val, "json", tmp); - if (native_mode || !warn_mode) { + if (native_mode || !warn_mode || eval_script) { JSGCRef init_ref; JS_AddGCRef(ctx, &init_ref); init_ref.val = JS_NewObject(ctx); @@ -683,6 +692,10 @@ int cell_init(int argc, char **argv) JS_SetPropertyStr(ctx, init_ref.val, "native_mode", JS_NewBool(ctx, 1)); if (!warn_mode) JS_SetPropertyStr(ctx, init_ref.val, "no_warn", JS_NewBool(ctx, 1)); + if (eval_script) { + JSValue es = JS_NewString(ctx, eval_script); + JS_SetPropertyStr(ctx, init_ref.val, "eval_script", es); + } JS_SetPropertyStr(ctx, env_ref.val, "init", init_ref.val); JS_DeleteGCRef(ctx, &init_ref); } else { diff --git a/vm_suite.ce b/vm_suite.ce index 6774f4ce..70225535 100644 --- a/vm_suite.ce +++ b/vm_suite.ce @@ -7434,11 +7434,39 @@ run("disruption propagation - runtime error direct vs nested", function() { } }) -// BUG: invoking a non-function (e.g. `var x = 42; x()`) crashes the VM -// instead of cleanly disrupting. The compiler warns "invoking int — will -// always disrupt" but the generated code causes a hard crash that kills -// the entire actor, bypassing disruption handlers. -// Cannot add a runtime test for this without crashing the suite. +run("disruption propagation - invoke non-function at runtime", function() { + // Use an array to hide the type from the compiler so it can't + // statically prove the invoke will fail. This tests the VM's + // MACH_FRAME runtime check, not the compile-time diagnostic. + var vals = [42, null, "hello", true] + var i = 0 + var count = 0 + for (i = 0; i < length(vals); i++) { + if (should_disrupt(function() { vals[i]() })) { + count = count + 1 + } + } + assert_eq(count, 4, "all non-function values should disrupt when invoked") +}) + +run("disruption propagation - invoke non-function direct handler", function() { + // Same test but with a direct disruption handler to verify consistency. + var vals = [42, null, "hello", true] + var i = 0 + var count = 0 + var caught = false + var try_call = function() { + vals[i]() + } disruption { + caught = true + } + for (i = 0; i < length(vals); i++) { + caught = false + try_call() + if (caught) count = count + 1 + } + assert_eq(count, 4, "all non-function values should disrupt with direct handler") +}) run("disruption propagation - comparison error direct vs nested", function() { var via_should = should_disrupt(function() {