diff --git a/internal/bootstrap.cm b/internal/bootstrap.cm new file mode 100644 index 00000000..3b53d01d --- /dev/null +++ b/internal/bootstrap.cm @@ -0,0 +1,40 @@ +// Hidden vars (os, program) come from env +var load_internal = os.load_internal +function use_embed(name) { + return load_internal("js_" + name + "_use") +} + +var fd = use_embed('fd') + +var use_cache = {} +use_cache['fd'] = fd +use_cache['os'] = os + +function use(path) { + if (use_cache[path]) + return use_cache[path]; + + var file_path = path + '.cm' + var script = null + var result = null + var exports = null + + if (fd.is_file(file_path)) { + script = text(fd.slurp(file_path)) + exports = {} + mach_eval(path, script, {use: use, exports: exports}) + use_cache[path] = exports + return exports + } + + // Try embedded C module + result = use_embed(replace(path, '/', '_')) + use_cache[path] = result + return result +} + +// Load and run the user's program +var blob = fd.slurp(program) +stone(blob) +var script = text(blob) +mach_eval(program, script, {use: use}) diff --git a/internal/os.c b/internal/os.c index 7c6e8538..267c01c5 100644 --- a/internal/os.c +++ b/internal/os.c @@ -578,7 +578,7 @@ static const JSCFunctionListEntry js_os_funcs[] = { JSValue js_os_use(JSContext *js) { JS_NewClassID(&js_dylib_class_id); JS_NewClass(js, js_dylib_class_id, &js_dylib_class); - + JSValue mod = JS_NewObject(js); JS_SetPropertyFunctionList(js,mod,js_os_funcs,countof(js_os_funcs)); return mod; diff --git a/source/cell.c b/source/cell.c index 16ad612a..dd669f9d 100644 --- a/source/cell.c +++ b/source/cell.c @@ -558,59 +558,54 @@ int cell_init(int argc, char **argv) /* Check for --run-mcode flag to execute via MCODE interpreter */ if (argc >= 3 && strcmp(argv[1], "--run-mcode") == 0) { - const char *script_or_file = argv[2]; - char *script = NULL; - char *allocated_script = NULL; - const char *filename = ""; + const char *filename = argv[2]; + if (!find_cell_shop()) return 1; - struct stat st; - if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) { - FILE *f = fopen(script_or_file, "r"); - if (!f) { printf("Failed to open file: %s\n", script_or_file); return 1; } - allocated_script = malloc(st.st_size + 1); - if (!allocated_script) { fclose(f); printf("Failed to allocate memory\n"); return 1; } - size_t read_size = fread(allocated_script, 1, st.st_size, f); - fclose(f); - allocated_script[read_size] = '\0'; - script = allocated_script; - filename = script_or_file; - } else { - script = (char *)script_or_file; - } - - cJSON *ast = JS_ASTTree(script, strlen(script), filename); - if (!ast) { - printf("Failed to parse AST\n"); - free(allocated_script); + size_t boot_size; + char *boot_data = load_core_file("internal/bootstrap.cm", &boot_size); + if (!boot_data) { + printf("ERROR: Could not load internal/bootstrap.cm from %s\n", core_path); return 1; } - if (print_tree_errors(ast)) { - cJSON_Delete(ast); free(allocated_script); + cJSON *boot_ast = JS_ASTTree(boot_data, boot_size, "internal/bootstrap.cm"); + free(boot_data); + if (!boot_ast) { + printf("Failed to parse internal/bootstrap.cm\n"); return 1; } - cJSON *mcode = JS_McodeTree(ast); - cJSON_Delete(ast); + if (print_tree_errors(boot_ast)) { + cJSON_Delete(boot_ast); + return 1; + } + + cJSON *mcode = JS_McodeTree(boot_ast); + cJSON_Delete(boot_ast); if (!mcode) { printf("Failed to generate MCODE\n"); - free(allocated_script); return 1; } if (print_tree_errors(mcode)) { - cJSON_Delete(mcode); free(allocated_script); + cJSON_Delete(mcode); return 1; } - /* Use a larger heap context for execution */ JSRuntime *rt = JS_NewRuntime(); - if (!rt) { printf("Failed to create JS runtime\n"); cJSON_Delete(mcode); free(allocated_script); return 1; } - JSContext *ctx = JS_NewContextWithHeapSize(rt, 64 * 1024); - if (!ctx) { printf("Failed to create execution context\n"); cJSON_Delete(mcode); JS_FreeRuntime(rt); free(allocated_script); return 1; } + if (!rt) { printf("Failed to create JS runtime\n"); cJSON_Delete(mcode); return 1; } + JSContext *ctx = JS_NewContextWithHeapSize(rt, 256 * 1024); + if (!ctx) { printf("Failed to create execution context\n"); cJSON_Delete(mcode); JS_FreeRuntime(rt); return 1; } - JSValue result = JS_CallMcodeTree(ctx, mcode); /* takes ownership of mcode */ + JS_FreeValue(ctx, js_blob_use(ctx)); + + JSValue hidden_env = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, hidden_env, "os", js_os_use(ctx)); + JS_SetPropertyStr(ctx, hidden_env, "program", JS_NewString(ctx, filename)); + hidden_env = JS_Stone(ctx, hidden_env); + + JSValue result = JS_CallMcodeTreeEnv(ctx, mcode, hidden_env); if (JS_IsException(result)) { JSValue exc = JS_GetException(ctx); @@ -637,7 +632,6 @@ int cell_init(int argc, char **argv) JS_FreeContext(ctx); JS_FreeRuntime(rt); - free(allocated_script); return JS_IsException(result) ? 1 : 0; } @@ -707,61 +701,50 @@ int cell_init(int argc, char **argv) /* Check for --mach-run flag to compile and run through MACH VM */ if (argc >= 3 && strcmp(argv[1], "--mach-run") == 0) { - const char *script_or_file = argv[2]; - char *script = NULL; - char *allocated_script = NULL; - const char *filename = ""; + const char *filename = argv[2]; + if (!find_cell_shop()) return 1; - struct stat st; - if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) { - FILE *f = fopen(script_or_file, "r"); - if (!f) { - printf("Failed to open file: %s\n", script_or_file); - return 1; - } - allocated_script = malloc(st.st_size + 1); - if (!allocated_script) { - fclose(f); - printf("Failed to allocate memory for script\n"); - return 1; - } - size_t read_size = fread(allocated_script, 1, st.st_size, f); - fclose(f); - allocated_script[read_size] = '\0'; - script = allocated_script; - filename = script_or_file; - } else { - script = (char *)script_or_file; - } - - cJSON *ast = JS_ASTTree(script, strlen(script), filename); - if (!ast) { - printf("Failed to parse AST\n"); - free(allocated_script); + size_t boot_size; + char *boot_data = load_core_file("internal/bootstrap.cm", &boot_size); + if (!boot_data) { + printf("ERROR: Could not load internal/bootstrap.cm from %s\n", core_path); return 1; } - if (print_tree_errors(ast)) { - cJSON_Delete(ast); - free(allocated_script); + cJSON *boot_ast = JS_ASTTree(boot_data, boot_size, "internal/bootstrap.cm"); + free(boot_data); + if (!boot_ast) { + printf("Failed to parse internal/bootstrap.cm\n"); + return 1; + } + + if (print_tree_errors(boot_ast)) { + cJSON_Delete(boot_ast); return 1; } JSRuntime *rt = JS_NewRuntime(); if (!rt) { printf("Failed to create JS runtime\n"); - cJSON_Delete(ast); free(allocated_script); + cJSON_Delete(boot_ast); return 1; } - JSContext *ctx = JS_NewContext(rt); + JSContext *ctx = JS_NewContextWithHeapSize(rt, 256 * 1024); if (!ctx) { printf("Failed to create JS context\n"); - cJSON_Delete(ast); JS_FreeRuntime(rt); free(allocated_script); + cJSON_Delete(boot_ast); JS_FreeRuntime(rt); return 1; } - JSValue result = JS_RunMachTree(ctx, ast, JS_NULL); - cJSON_Delete(ast); + JS_FreeValue(ctx, js_blob_use(ctx)); + + JSValue hidden_env = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, hidden_env, "os", js_os_use(ctx)); + JS_SetPropertyStr(ctx, hidden_env, "program", JS_NewString(ctx, filename)); + hidden_env = JS_Stone(ctx, hidden_env); + + JSValue result = JS_RunMachTree(ctx, boot_ast, hidden_env); + cJSON_Delete(boot_ast); int exit_code = 0; if (JS_IsException(result)) { @@ -796,7 +779,6 @@ int cell_init(int argc, char **argv) JS_FreeContext(ctx); JS_FreeRuntime(rt); - free(allocated_script); return exit_code; } diff --git a/source/mcode.c b/source/mcode.c index ea957d3d..2e55b473 100644 --- a/source/mcode.c +++ b/source/mcode.c @@ -3444,6 +3444,34 @@ JSValue JS_CallMcodeTree(JSContext *ctx, cJSON *root) { return result; } +JSValue JS_CallMcodeTreeEnv(JSContext *ctx, cJSON *root, JSValue env) { + if (!root) return JS_ThrowSyntaxError(ctx, "invalid MCODE tree"); + + cJSON *main_obj = cJSON_GetObjectItemCaseSensitive(root, "main"); + if (!main_obj) { + cJSON_Delete(root); + return JS_ThrowSyntaxError(ctx, "MCODE tree missing 'main' section"); + } + + cJSON *functions = cJSON_GetObjectItemCaseSensitive(root, "functions"); + + /* Parse main code */ + JSMCode *code = jsmcode_parse(main_obj, functions); + if (!code) { + cJSON_Delete(root); + return JS_ThrowInternalError(ctx, "failed to parse MCODE"); + } + code->json_root = root; /* Keep tree alive — instrs point into it */ + + /* Execute with global_obj as this, passing env as outer_frame */ + JSValue result = mcode_exec(ctx, code, ctx->global_obj, 0, NULL, env); + + /* Clear frame ref before freeing mcode — stack trace data is inside code */ + ctx->reg_current_frame = JS_NULL; + jsmcode_free(code); + return result; +} + JSValue JS_CallMcode(JSContext *ctx, const char *mcode_json) { cJSON *root = cJSON_Parse(mcode_json); if (!root) return JS_ThrowSyntaxError(ctx, "invalid MCODE JSON"); diff --git a/source/quickjs-internal.h b/source/quickjs-internal.h index 9036b1e1..54f70090 100644 --- a/source/quickjs-internal.h +++ b/source/quickjs-internal.h @@ -101,7 +101,7 @@ // #define DUMP_ROPE_REBALANCE /* test the GC by forcing it before each object allocation */ -#define FORCE_GC_AT_MALLOC +// #define FORCE_GC_AT_MALLOC #define POISON_HEAP /* POISON_HEAP: Use ASan's memory poisoning to detect stale pointer access */ diff --git a/source/quickjs.h b/source/quickjs.h index f9f75919..d00ce026 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -1268,6 +1268,9 @@ char *JS_Mcode (const char *ast_json); /* Execute MCODE from cJSON tree. Takes ownership of root. */ JSValue JS_CallMcodeTree (JSContext *ctx, struct cJSON *root); +/* Execute MCODE from cJSON tree with hidden env. Takes ownership of root. */ +JSValue JS_CallMcodeTreeEnv (JSContext *ctx, struct cJSON *root, JSValue env); + /* Parse and execute MCODE JSON string. Returns result of execution, or JS_EXCEPTION on error. */ JSValue JS_CallMcode (JSContext *ctx, const char *mcode_json); diff --git a/source/runtime.c b/source/runtime.c index e3ec5137..359edebf 100644 --- a/source/runtime.c +++ b/source/runtime.c @@ -10655,7 +10655,8 @@ static JSValue js_mach_eval (JSContext *ctx, JSValue this_val, int argc, JSValue return JS_ThrowSyntaxError (ctx, "mach_eval: failed to parse AST"); } - JSValue result = JS_RunMachTree (ctx, ast, JS_NULL); + JSValue env = (argc >= 3 && JS_IsObject (argv[2])) ? argv[2] : JS_NULL; + JSValue result = JS_RunMachTree (ctx, ast, env); cJSON_Delete (ast); JS_FreeCString (ctx, name); return result;