From 13a6f6c79d19fbb6b92cfec81c93021d40f12f55 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sat, 7 Feb 2026 15:49:09 -0600 Subject: [PATCH] faster mach bytecode generation --- Makefile | 2 +- source/cell.c | 68 ++++++++++++++++++++++++------------------------ source/quickjs.c | 52 ++++++++++++++++++++++++++++-------- source/quickjs.h | 32 ++++++++++++++--------- source/suite.c | 20 +++++++------- 5 files changed, 105 insertions(+), 69 deletions(-) diff --git a/Makefile b/Makefile index 287f1cdc..261bd62a 100755 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ static: # Bootstrap: build cell from scratch using meson (only needed once) # Also installs core scripts to ~/.cell/core bootstrap: - meson setup build_bootstrap -Dbuildtype=debug -Db_sanitize=address + meson setup build_bootstrap -Dbuildtype=debugoptimized meson compile -C build_bootstrap cp build_bootstrap/cell . cp build_bootstrap/libcell_runtime.dylib . diff --git a/source/cell.c b/source/cell.c index b58d513c..58bde784 100644 --- a/source/cell.c +++ b/source/cell.c @@ -105,15 +105,11 @@ static char* load_core_file(const char *filename, size_t *out_size) { return data; } -static int print_json_errors(const char *json) { - if (!json) return 0; - cJSON *root = cJSON_Parse(json); +static int print_tree_errors(cJSON *root) { if (!root) return 0; cJSON *errors = cJSON_GetObjectItemCaseSensitive(root, "errors"); - if (!cJSON_IsArray(errors) || cJSON_GetArraySize(errors) == 0) { - cJSON_Delete(root); + if (!cJSON_IsArray(errors) || cJSON_GetArraySize(errors) == 0) return 0; - } const char *filename = ""; cJSON *fname = cJSON_GetObjectItemCaseSensitive(root, "filename"); if (cJSON_IsString(fname)) @@ -130,10 +126,18 @@ static int print_json_errors(const char *json) { else if (msg) fprintf(stderr, "%s: error: %s\n", filename, msg); } - cJSON_Delete(root); return 1; } +static int print_json_errors(const char *json) { + if (!json) return 0; + cJSON *root = cJSON_Parse(json); + if (!root) return 0; + int result = print_tree_errors(root); + cJSON_Delete(root); + return result; +} + // Get the core path for use by scripts const char* cell_get_core_path(void) { return core_path; @@ -399,17 +403,13 @@ int cell_init(int argc, char **argv) script = (char *)script_or_file; } - char *json = JS_AST(script, strlen(script), filename); - if (json) { - int has_errors = print_json_errors(json); - cJSON *root = cJSON_Parse(json); - free(json); - if (root) { - char *pretty = cJSON_Print(root); - cJSON_Delete(root); - printf("%s\n", pretty); - free(pretty); - } + cJSON *ast = JS_ASTTree(script, strlen(script), filename); + if (ast) { + int has_errors = print_tree_errors(ast); + char *pretty = cJSON_Print(ast); + cJSON_Delete(ast); + printf("%s\n", pretty); + free(pretty); free(allocated_script); return has_errors ? 1 : 0; } else { @@ -647,15 +647,15 @@ int cell_init(int argc, char **argv) script = (char *)script_or_file; } - char *ast_json = JS_AST(script, strlen(script), filename); - if (!ast_json) { + cJSON *ast = JS_ASTTree(script, strlen(script), filename); + if (!ast) { printf("Failed to parse AST\n"); free(allocated_script); return 1; } - if (print_json_errors(ast_json)) { - free(ast_json); + if (print_tree_errors(ast)) { + cJSON_Delete(ast); free(allocated_script); return 1; } @@ -663,18 +663,18 @@ int cell_init(int argc, char **argv) JSRuntime *rt = JS_NewRuntime(); if (!rt) { printf("Failed to create JS runtime\n"); - free(ast_json); free(allocated_script); + cJSON_Delete(ast); free(allocated_script); return 1; } JSContext *ctx = JS_NewContext(rt); if (!ctx) { printf("Failed to create JS context\n"); - free(ast_json); JS_FreeRuntime(rt); free(allocated_script); + cJSON_Delete(ast); JS_FreeRuntime(rt); free(allocated_script); return 1; } - JS_DumpMach(ctx, ast_json, JS_NULL); - free(ast_json); + JS_DumpMachTree(ctx, ast, JS_NULL); + cJSON_Delete(ast); JS_FreeContext(ctx); JS_FreeRuntime(rt); @@ -711,15 +711,15 @@ int cell_init(int argc, char **argv) script = (char *)script_or_file; } - char *ast_json = JS_AST(script, strlen(script), filename); - if (!ast_json) { + cJSON *ast = JS_ASTTree(script, strlen(script), filename); + if (!ast) { printf("Failed to parse AST\n"); free(allocated_script); return 1; } - if (print_json_errors(ast_json)) { - free(ast_json); + if (print_tree_errors(ast)) { + cJSON_Delete(ast); free(allocated_script); return 1; } @@ -727,18 +727,18 @@ int cell_init(int argc, char **argv) JSRuntime *rt = JS_NewRuntime(); if (!rt) { printf("Failed to create JS runtime\n"); - free(ast_json); free(allocated_script); + cJSON_Delete(ast); free(allocated_script); return 1; } JSContext *ctx = JS_NewContext(rt); if (!ctx) { printf("Failed to create JS context\n"); - free(ast_json); JS_FreeRuntime(rt); free(allocated_script); + cJSON_Delete(ast); JS_FreeRuntime(rt); free(allocated_script); return 1; } - JSValue result = JS_RunMach(ctx, ast_json, JS_NULL); - free(ast_json); + JSValue result = JS_RunMachTree(ctx, ast, JS_NULL); + cJSON_Delete(ast); int exit_code = 0; if (JS_IsException(result)) { diff --git a/source/quickjs.c b/source/quickjs.c index 87b3693b..ce2b4207 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -31153,7 +31153,7 @@ static void ast_semantic_check (cJSON *ast, cJSON **errors_out, *intrinsics_out = intr_arr; } -char *JS_AST (const char *source, size_t len, const char *filename) { +cJSON *JS_ASTTree (const char *source, size_t len, const char *filename) { ASTParseState s; memset (&s, 0, sizeof (s)); @@ -31205,10 +31205,14 @@ char *JS_AST (const char *source, size_t len, const char *filename) { cJSON_AddItemToObject (ast, "errors", sem_errors); } - /* Convert to JSON string */ + return ast; +} + +char *JS_AST (const char *source, size_t len, const char *filename) { + cJSON *ast = JS_ASTTree (source, len, filename); + if (!ast) return NULL; char *json = cJSON_PrintUnformatted (ast); cJSON_Delete (ast); - return json; } @@ -33021,8 +33025,7 @@ static MachCode *mach_compile_program(MachCompState *cs, cJSON *ast) { } /* Public API: compile AST JSON to MachCode (context-free) */ -MachCode *JS_CompileMach(const char *ast_json) { - cJSON *ast = cJSON_Parse(ast_json); +MachCode *JS_CompileMachTree(cJSON *ast) { if (!ast) return NULL; MachCompState cs = {0}; @@ -33036,6 +33039,13 @@ MachCode *JS_CompileMach(const char *ast_json) { sys_free(cs.vars[i].name); sys_free(cs.vars); + return code; +} + +MachCode *JS_CompileMach(const char *ast_json) { + cJSON *ast = cJSON_Parse(ast_json); + if (!ast) return NULL; + MachCode *code = JS_CompileMachTree(ast); cJSON_Delete(ast); return code; } @@ -37386,9 +37396,9 @@ static void dump_register_code(JSContext *ctx, JSCodeRegister *code, int indent) } } -/* Dump MACH bytecode to stdout for debugging. Takes AST JSON. */ -void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) { - MachCode *mc = JS_CompileMach(ast_json); +/* Dump MACH bytecode to stdout for debugging. Takes AST cJSON tree. */ +void JS_DumpMachTree(JSContext *ctx, cJSON *ast, JSValue env) { + MachCode *mc = JS_CompileMachTree(ast); if (!mc) { printf("=== MACH Bytecode ===\nFailed to compile\n=== End MACH Bytecode ===\n"); return; @@ -37400,9 +37410,19 @@ void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) { printf("=== End MACH Bytecode ===\n"); } -/* Compile and execute MACH bytecode. Takes AST JSON. */ -JSValue JS_RunMach(JSContext *ctx, const char *ast_json, JSValue env) { - MachCode *mc = JS_CompileMach(ast_json); +void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) { + cJSON *ast = cJSON_Parse(ast_json); + if (!ast) { + printf("=== MACH Bytecode ===\nFailed to parse\n=== End MACH Bytecode ===\n"); + return; + } + JS_DumpMachTree(ctx, ast, env); + cJSON_Delete(ast); +} + +/* Compile and execute MACH bytecode. Takes AST cJSON tree. */ +JSValue JS_RunMachTree(JSContext *ctx, cJSON *ast, JSValue env) { + MachCode *mc = JS_CompileMachTree(ast); if (!mc) { return JS_ThrowSyntaxError(ctx, "failed to compile AST to MACH bytecode"); } @@ -37411,3 +37431,13 @@ JSValue JS_RunMach(JSContext *ctx, const char *ast_json, JSValue env) { JSValue result = JS_CallRegisterVM(ctx, code, ctx->global_obj, 0, NULL, env, JS_NULL); return result; } + +JSValue JS_RunMach(JSContext *ctx, const char *ast_json, JSValue env) { + cJSON *ast = cJSON_Parse(ast_json); + if (!ast) { + return JS_ThrowSyntaxError(ctx, "failed to parse AST JSON"); + } + JSValue result = JS_RunMachTree(ctx, ast, env); + cJSON_Delete(ast); + return result; +} diff --git a/source/quickjs.h b/source/quickjs.h index b434b527..4f9b8d4c 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -1218,37 +1218,43 @@ CellModule *cell_module_from_bytecode (JSContext *ctx, JSFunctionBytecode *main_ Returns allocated CellModule (caller must free with cell_module_free), or NULL on error. */ CellModule *JS_CompileModule (JSContext *ctx, const char *input, size_t input_len, const char *filename); +/* Parse source code and return AST as cJSON tree. + Caller must call cJSON_Delete() on result. */ +struct cJSON *JS_ASTTree (const char *source, size_t len, const char *filename); + /* Parse source code and return AST as JSON string. - Returns malloc'd JSON string (caller must free), or NULL on error. - No JSContext needed — pure string transformation. */ + Returns malloc'd JSON string (caller must free), or NULL on error. */ char *JS_AST (const char *source, size_t len, const char *filename); /* Tokenize source code and return token array as JSON string. - Returns malloc'd JSON string (caller must free), or NULL on error. - No JSContext needed — pure string transformation. */ + Returns malloc'd JSON string (caller must free), or NULL on error. */ char *JS_Tokenize (const char *source, size_t len, const char *filename); /* Compiled bytecode (context-free, serializable) */ typedef struct MachCode MachCode; -/* Compile AST JSON to context-free MachCode. - Returns MachCode* (caller must free with JS_FreeMachCode), or NULL on error. */ +/* Compile AST cJSON tree to context-free MachCode. */ +MachCode *JS_CompileMachTree(struct cJSON *ast); + +/* Compile AST JSON string to context-free MachCode. */ MachCode *JS_CompileMach(const char *ast_json); /* Free a compiled MachCode tree. */ void JS_FreeMachCode(MachCode *mc); -/* Load compiled MachCode into a JSContext, materializing JSValues. - Returns JSCodeRegister* linked and ready for execution. */ +/* Load compiled MachCode into a JSContext, materializing JSValues. */ struct JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env); -/* Dump MACH bytecode to stdout for debugging. Takes AST JSON. - Internally compiles, loads, and dumps binary bytecode. */ +/* Dump MACH bytecode to stdout. Takes AST cJSON tree. */ +void JS_DumpMachTree (JSContext *ctx, struct cJSON *ast, JSValue env); + +/* Dump MACH bytecode to stdout. Takes AST JSON string. */ void JS_DumpMach (JSContext *ctx, const char *ast_json, JSValue env); -/* Compile and execute MACH bytecode. Takes AST JSON. - Internally compiles, loads, and executes. - Returns result of execution, or JS_EXCEPTION on error. */ +/* Compile and execute MACH bytecode from AST cJSON tree. */ +JSValue JS_RunMachTree (JSContext *ctx, struct cJSON *ast, JSValue env); + +/* Compile and execute MACH bytecode from AST JSON string. */ JSValue JS_RunMach (JSContext *ctx, const char *ast_json, JSValue env); /* Compile AST JSON to MCODE JSON (string-based IR). diff --git a/source/suite.c b/source/suite.c index f2e3ffb0..cdf529e2 100644 --- a/source/suite.c +++ b/source/suite.c @@ -2393,22 +2393,22 @@ TEST(ast_sem_nested_function_scope) { TEST(mach_compile_basic) { const char *src = "var x = 1; x = x + 1"; - char *ast_json = JS_AST(src, strlen(src), ""); - ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL"); - MachCode *mc = JS_CompileMach(ast_json); - free(ast_json); - ASSERT_MSG(mc != NULL, "JS_CompileMach returned NULL"); + cJSON *ast = JS_ASTTree(src, strlen(src), ""); + ASSERT_MSG(ast != NULL, "JS_ASTTree returned NULL"); + MachCode *mc = JS_CompileMachTree(ast); + cJSON_Delete(ast); + ASSERT_MSG(mc != NULL, "JS_CompileMachTree returned NULL"); JS_FreeMachCode(mc); return 1; } TEST(mach_compile_function) { const char *src = "function f(x) { return x + 1 }"; - char *ast_json = JS_AST(src, strlen(src), ""); - ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL"); - MachCode *mc = JS_CompileMach(ast_json); - free(ast_json); - ASSERT_MSG(mc != NULL, "JS_CompileMach returned NULL"); + cJSON *ast = JS_ASTTree(src, strlen(src), ""); + ASSERT_MSG(ast != NULL, "JS_ASTTree returned NULL"); + MachCode *mc = JS_CompileMachTree(ast); + cJSON_Delete(ast); + ASSERT_MSG(mc != NULL, "JS_CompileMachTree returned NULL"); JS_FreeMachCode(mc); return 1; }