diff --git a/fold.mach b/fold.mach index 3b4ec013..54952d4c 100644 Binary files a/fold.mach and b/fold.mach differ diff --git a/fold_new.mach b/fold_new.mach new file mode 100644 index 00000000..54952d4c Binary files /dev/null and b/fold_new.mach differ diff --git a/internal/bootstrap.mach b/internal/bootstrap.mach index 2dc682f4..c0f4fbd0 100644 Binary files a/internal/bootstrap.mach and b/internal/bootstrap.mach differ diff --git a/internal/engine.mach b/internal/engine.mach index 3ec759ec..2f76ed14 100644 Binary files a/internal/engine.mach and b/internal/engine.mach differ diff --git a/mcode.mach b/mcode.mach index f5227053..45d0685b 100644 Binary files a/mcode.mach and b/mcode.mach differ diff --git a/meson.build b/meson.build index fbc7f88d..f7ceed8a 100644 --- a/meson.build +++ b/meson.build @@ -46,7 +46,6 @@ src += [ # core 'miniz.c', 'runtime.c', 'mach.c', - 'mcode.c', 'libregexp.c', 'libunicode.c', 'cutils.c', 'dtoa.c' ] diff --git a/parse.cm b/parse.cm index 00bb5bf8..037d0b0f 100644 --- a/parse.cm +++ b/parse.cm @@ -65,7 +65,7 @@ var parse = function(tokens, src, filename, tokenizer) { var errors = [] var error_count = 0 - var function_nr = 1 + var fn_counter = 1 var ast_node = function(kind, token) { return { @@ -422,8 +422,8 @@ var parse = function(tokens, src, filename, tokenizer) { _control_depth = meth_old_cd _control_type = meth_old_ct _expecting_body = meth_old_eb - fn.function_nr = function_nr - function_nr = function_nr + 1 + fn.function_nr = fn_counter + fn_counter = fn_counter + 1 ast_node_end(fn) pair.right = fn } else if (is_ident && (tok.kind == "," || tok.kind == "}")) { @@ -918,8 +918,8 @@ var parse = function(tokens, src, filename, tokenizer) { _control_depth = old_cd _control_type = old_ct _expecting_body = old_eb - node.function_nr = function_nr - function_nr = function_nr + 1 + node.function_nr = fn_counter + fn_counter = fn_counter + 1 ast_node_end(node) return node } @@ -1008,8 +1008,8 @@ var parse = function(tokens, src, filename, tokenizer) { _control_depth = old_cd _control_type = old_ct _expecting_body = old_eb - node.function_nr = function_nr - function_nr = function_nr + 1 + node.function_nr = fn_counter + fn_counter = fn_counter + 1 ast_node_end(node) return node } diff --git a/parse.mach b/parse.mach index 1da26295..8474151c 100644 Binary files a/parse.mach and b/parse.mach differ diff --git a/qbe.mach b/qbe.mach index fd0219c9..ba12ea90 100644 Binary files a/qbe.mach and b/qbe.mach differ diff --git a/qbe_emit.mach b/qbe_emit.mach index 7a41d105..c14c23d3 100644 Binary files a/qbe_emit.mach and b/qbe_emit.mach differ diff --git a/regen.cm b/regen.cm index df227202..2abaeee2 100644 --- a/regen.cm +++ b/regen.cm @@ -6,6 +6,8 @@ var json = use("json") var tokenize = use("tokenize") var parse = use("parse") var fold = use("fold") +var mcode = use("mcode") +var streamline = use("streamline") var files = [ {src: "tokenize.cm", name: "tokenize", out: "tokenize.mach"}, @@ -25,21 +27,49 @@ var src = null var tok_result = null var ast = null var folded = null -var ast_json = null +var compiled = null +var optimized = null +var mcode_json = null var bytecode = null var f = null +var errs = null +var ei = 0 +var e = null +var had_errors = false while (i < length(files)) { entry = files[i] src = text(fd.slurp(entry.src)) tok_result = tokenize(src, entry.src) ast = parse(tok_result.tokens, src, entry.src, tokenize) + // Check for parse/semantic errors + errs = ast.errors + if (errs != null && length(errs) > 0) { + ei = 0 + while (ei < length(errs)) { + e = errs[ei] + if (e.line != null) { + print(`${entry.src}:${text(e.line)}:${text(e.column)}: error: ${e.message}`) + } else { + print(`${entry.src}: error: ${e.message}`) + } + ei = ei + 1 + } + had_errors = true + i = i + 1 + continue + } folded = fold(ast) - ast_json = json.encode(folded) - bytecode = mach_compile_ast(entry.name, ast_json) + compiled = mcode(folded) + optimized = streamline(compiled) + mcode_json = json.encode(optimized) + bytecode = mach_compile_mcode_bin(entry.name, mcode_json) f = fd.open(entry.out, "w") fd.write(f, bytecode) fd.close(f) print(`wrote ${entry.out}`) i = i + 1 } +if (had_errors) { + print("regen aborted: fix errors above") +} diff --git a/source/cell.c b/source/cell.c index e28f18df..5937b01d 100644 --- a/source/cell.c +++ b/source/cell.c @@ -12,7 +12,6 @@ #include "cJSON.h" #define BOOTSTRAP_MACH "internal/bootstrap.mach" -#define BOOTSTRAP_AST "internal/bootstrap.ast.json" #define BOOTSTRAP_SRC "internal/bootstrap.cm" #define CELL_SHOP_DIR ".cell" #define CELL_CORE_DIR "packages/core" @@ -179,14 +178,9 @@ void script_startup(cell_rt *prt) cell_rt *crt = JS_GetContextOpaque(js); JS_FreeValue(js, js_blob_use(js)); - // Load pre-compiled bootstrap bytecode (.mach), fall back to AST JSON + // Load pre-compiled bootstrap bytecode (.mach) size_t boot_size; - int boot_is_bin = 1; char *boot_data = load_core_file(BOOTSTRAP_MACH, &boot_size); - if (!boot_data) { - boot_is_bin = 0; - boot_data = load_core_file(BOOTSTRAP_AST, &boot_size); - } if (!boot_data) { printf("ERROR: Could not load bootstrap from %s!\n", core_path); return; @@ -225,17 +219,8 @@ void script_startup(cell_rt *prt) // Run through MACH VM crt->state = ACTOR_RUNNING; - JSValue v; - if (boot_is_bin) { - v = JS_RunMachBin(js, (const uint8_t *)boot_data, boot_size, hidden_env); - free(boot_data); - } else { - cJSON *ast = cJSON_Parse(boot_data); - free(boot_data); - if (!ast) { printf("ERROR: Failed to parse bootstrap AST\n"); return; } - v = JS_RunMachTree(js, ast, hidden_env); - cJSON_Delete(ast); - } + JSValue v = JS_RunMachBin(js, (const uint8_t *)boot_data, boot_size, hidden_env); + free(boot_data); uncaught_exception(js, v); crt->state = ACTOR_IDLE; set_actor_state(crt); @@ -359,12 +344,7 @@ int cell_init(int argc, char **argv) actor_initialize(); size_t boot_size; - int boot_is_bin = 1; char *boot_data = load_core_file(BOOTSTRAP_MACH, &boot_size); - if (!boot_data) { - boot_is_bin = 0; - boot_data = load_core_file(BOOTSTRAP_AST, &boot_size); - } if (!boot_data) { printf("ERROR: Could not load bootstrap from %s\n", core_path); return 1; @@ -433,17 +413,8 @@ int cell_init(int argc, char **argv) JS_SetPropertyStr(ctx, hidden_env, "args", args_arr); hidden_env = JS_Stone(ctx, hidden_env); - JSValue result; - if (boot_is_bin) { - result = JS_RunMachBin(ctx, (const uint8_t *)boot_data, boot_size, hidden_env); - free(boot_data); - } else { - cJSON *ast = cJSON_Parse(boot_data); - free(boot_data); - if (!ast) { printf("Failed to parse bootstrap AST\n"); JS_FreeContext(ctx); JS_FreeRuntime(g_runtime); return 1; } - result = JS_RunMachTree(ctx, ast, hidden_env); - cJSON_Delete(ast); - } + JSValue result = JS_RunMachBin(ctx, (const uint8_t *)boot_data, boot_size, hidden_env); + free(boot_data); int exit_code = 0; if (JS_IsException(result)) { diff --git a/source/mach.c b/source/mach.c index 4c1f489a..757035d7 100644 --- a/source/mach.c +++ b/source/mach.c @@ -61,161 +61,8 @@ typedef struct MachCode { uint16_t disruption_pc; /* start of disruption handler (0 = none) */ } MachCode; -/* ---- Compiler state ---- */ +/* ---- Helpers ---- */ -typedef struct MachCompState { - /* Instruction buffer (growable) */ - MachInstr32 *code; - int code_count; - int code_capacity; - - /* Constant pool (raw entries, no GC objects) */ - MachCPoolEntry *cpool; - int cpool_count; - int cpool_capacity; - - /* Nested functions */ - MachCode **functions; - int func_count; - int func_capacity; - - /* Variables */ - MachVarInfo *vars; - int var_count; - int var_capacity; - - /* Register allocation (Lua-style) */ - int freereg; /* next free register */ - int maxreg; /* high-water mark */ - int nr_args; /* parameter count */ - - /* Loop labels for break/continue */ - int loop_break; /* instruction index to patch, or -1 */ - int loop_continue; /* instruction index to patch, or -1 */ - - /* Named label stack for labeled break/continue */ -#define MACH_MAX_LABELS 8 - struct { - const char *name; - int break_chain; - int continue_chain; - } labels[MACH_MAX_LABELS]; - int label_count; - int pending_label; /* label index associated with next loop, or -1 */ - - /* Parent for nested function compilation */ - struct MachCompState *parent; - - /* AST semantic annotations */ - int function_nr; /* current function number (0=program body) */ - cJSON *scopes; /* pointer to AST "scopes" array (not owned) */ - - /* Error tracking */ - int has_error; - - /* Line tracking for debug info */ - int cur_line, cur_col; - MachLineEntry *line_info; /* growable, parallel to code[] */ - int line_capacity; - const char *filename; /* pointer into AST cJSON (not owned) */ -} MachCompState; - -/* Forward declarations */ -static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest); -static void mach_compile_stmt(MachCompState *cs, cJSON *stmt); - -/* ---- Compiler helpers ---- */ - -static void mach_set_pos(MachCompState *cs, cJSON *node) { - cJSON *r = cJSON_GetObjectItemCaseSensitive(node, "from_row"); - cJSON *c = cJSON_GetObjectItemCaseSensitive(node, "from_column"); - if (r) cs->cur_line = (int)r->valuedouble + 1; - if (c) cs->cur_col = (int)c->valuedouble + 1; -} - -static void mach_emit(MachCompState *cs, MachInstr32 instr) { - if (cs->code_count >= cs->code_capacity) { - int new_cap = cs->code_capacity ? cs->code_capacity * 2 : 64; - cs->code = sys_realloc(cs->code, new_cap * sizeof(MachInstr32)); - cs->code_capacity = new_cap; - } - if (cs->code_count >= cs->line_capacity) { - int new_cap = cs->line_capacity ? cs->line_capacity * 2 : 64; - cs->line_info = sys_realloc(cs->line_info, new_cap * sizeof(MachLineEntry)); - cs->line_capacity = new_cap; - } - cs->line_info[cs->code_count] = (MachLineEntry){cs->cur_line, cs->cur_col}; - cs->code[cs->code_count++] = instr; -} - -static int mach_current_pc(MachCompState *cs) { - return cs->code_count; -} - -/* Reserve a register at freereg */ -static int mach_reserve_reg(MachCompState *cs) { - int r = cs->freereg++; - if (cs->freereg > cs->maxreg) cs->maxreg = cs->freereg; - return r; -} - -/* Free temporary registers back to a saved freereg level */ -static void mach_free_reg_to(MachCompState *cs, int saved) { - cs->freereg = saved; -} - -/* Add an integer constant to the pool, return its index */ -static int mach_cpool_add_int(MachCompState *cs, int32_t val) { - for (int i = 0; i < cs->cpool_count; i++) { - MachCPoolEntry *e = &cs->cpool[i]; - if (e->type == MACH_CP_INT && e->ival == val) return i; - } - if (cs->cpool_count >= cs->cpool_capacity) { - int new_cap = cs->cpool_capacity ? cs->cpool_capacity * 2 : 16; - cs->cpool = sys_realloc(cs->cpool, new_cap * sizeof(MachCPoolEntry)); - cs->cpool_capacity = new_cap; - } - cs->cpool[cs->cpool_count] = (MachCPoolEntry){ .type = MACH_CP_INT, .ival = val }; - return cs->cpool_count++; -} - -/* Add a float constant to the pool, return its index */ -static int mach_cpool_add_float(MachCompState *cs, double val) { - for (int i = 0; i < cs->cpool_count; i++) { - MachCPoolEntry *e = &cs->cpool[i]; - if (e->type == MACH_CP_FLOAT && e->fval == val) return i; - } - if (cs->cpool_count >= cs->cpool_capacity) { - int new_cap = cs->cpool_capacity ? cs->cpool_capacity * 2 : 16; - cs->cpool = sys_realloc(cs->cpool, new_cap * sizeof(MachCPoolEntry)); - cs->cpool_capacity = new_cap; - } - cs->cpool[cs->cpool_count] = (MachCPoolEntry){ .type = MACH_CP_FLOAT, .fval = val }; - return cs->cpool_count++; -} - -/* Add a string constant, return its cpool index */ -static int mach_cpool_add_str(MachCompState *cs, const char *str) { - /* Check for existing identical string */ - for (int i = 0; i < cs->cpool_count; i++) { - MachCPoolEntry *e = &cs->cpool[i]; - if (e->type == MACH_CP_STR && strcmp(e->str, str) == 0) - return i; - } - if (cs->cpool_count >= cs->cpool_capacity) { - int new_cap = cs->cpool_capacity ? cs->cpool_capacity * 2 : 16; - cs->cpool = sys_realloc(cs->cpool, new_cap * sizeof(MachCPoolEntry)); - cs->cpool_capacity = new_cap; - } - char *dup = sys_malloc(strlen(str) + 1); - memcpy(dup, str, strlen(str) + 1); - cs->cpool[cs->cpool_count] = (MachCPoolEntry){ .type = MACH_CP_STR, .str = dup }; - return cs->cpool_count++; -} - -/* Convert compile-time cpool entries to JSValue array for JSCodeRegister. - Caller takes ownership of the returned array. Frees the raw entries. - Strings are interned into stone memory (no GC allocation). */ static JSValue *mach_materialize_cpool(JSContext *ctx, MachCPoolEntry *entries, int count) { if (count == 0) { sys_free(entries); return NULL; } JSValue *cpool = js_malloc_rt(count * sizeof(JSValue)); @@ -235,1744 +82,6 @@ static JSValue *mach_materialize_cpool(JSContext *ctx, MachCPoolEntry *entries, return cpool; } -/* Add a variable */ -static void mach_add_var(MachCompState *cs, const char *name, int slot, int is_const) { - if (cs->var_count >= cs->var_capacity) { - int new_cap = cs->var_capacity ? cs->var_capacity * 2 : 16; - cs->vars = sys_realloc(cs->vars, new_cap * sizeof(MachVarInfo)); - cs->var_capacity = new_cap; - } - MachVarInfo *v = &cs->vars[cs->var_count++]; - v->name = sys_malloc(strlen(name) + 1); - strcpy(v->name, name); - v->slot = slot; - v->is_const = is_const; - v->is_closure = 0; -} - -/* Find a variable in the current scope */ -static int mach_find_var(MachCompState *cs, const char *name) { - for (int i = cs->var_count - 1; i >= 0; i--) { - if (strcmp(cs->vars[i].name, name) == 0) - return cs->vars[i].slot; - } - return -1; -} - -/* Add a nested function, return its index */ -static int mach_add_function(MachCompState *cs, MachCode *fn) { - if (cs->func_count >= cs->func_capacity) { - int new_cap = cs->func_capacity ? cs->func_capacity * 2 : 4; - cs->functions = sys_realloc(cs->functions, new_cap * sizeof(MachCode*)); - cs->func_capacity = new_cap; - } - cs->functions[cs->func_count] = fn; - return cs->func_count++; -} - -/* Find the scope record for a given function_nr in the scopes array */ -cJSON *mach_find_scope_record(cJSON *scopes, int function_nr) { - if (!scopes) return NULL; - int count = cJSON_GetArraySize(scopes); - for (int i = 0; i < count; i++) { - cJSON *scope = cJSON_GetArrayItem(scopes, i); - cJSON *fn_nr = cJSON_GetObjectItemCaseSensitive(scope, "function_nr"); - if (fn_nr && (int)cJSON_GetNumberValue(fn_nr) == function_nr) - return scope; - } - return NULL; -} - -/* Scan AST scope record for variable declarations. - Variables are direct keys on the scope object with a "make" field. */ -static void mach_scan_scope(MachCompState *cs) { - cJSON *scope = mach_find_scope_record(cs->scopes, cs->function_nr); - if (!scope) return; - cJSON *v; - cJSON_ArrayForEach(v, scope) { - const char *name = v->string; - if (!name || strcmp(name, "function_nr") == 0 || strcmp(name, "nr_close_slots") == 0) continue; - const char *make = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(v, "make")); - if (!make || strcmp(make, "input") == 0) continue; - if (mach_find_var(cs, name) < 0) { - int is_const = (strcmp(make, "def") == 0 || strcmp(make, "function") == 0); - int slot = mach_reserve_reg(cs); - mach_add_var(cs, name, slot, is_const); - } - } -} - -/* ---- Expression compiler ---- */ - -/* Compile an expression into register dest. If dest < 0, allocate a temp. - Returns the register containing the result. */ -static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { - if (!node) { - if (dest < 0) dest = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADNULL, dest, 0)); - return dest; - } - - mach_set_pos(cs, node); - const char *kind = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(node, "kind")); - if (!kind) { - if (dest < 0) dest = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADNULL, dest, 0)); - return dest; - } - - /* Number literal */ - if (strcmp(kind, "number") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - cJSON *num = cJSON_GetObjectItemCaseSensitive(node, "number"); - if (num && cJSON_IsNumber(num)) { - double dval = num->valuedouble; - int ival = (int)dval; - if (dval == (double)ival && ival >= -32768 && ival <= 32767) { - /* Small integer: use LOADI */ - mach_emit(cs, MACH_AsBx(MACH_LOADI, dest, (int16_t)ival)); - } else { - /* Large number: use constant pool */ - int ki; - if (dval == (double)(int32_t)dval) - ki = mach_cpool_add_int(cs, (int32_t)dval); - else - ki = mach_cpool_add_float(cs, dval); - mach_emit(cs, MACH_ABx(MACH_LOADK, dest, ki)); - } - } else { - mach_emit(cs, MACH_AsBx(MACH_LOADI, dest, 0)); - } - return dest; - } - - /* String literal */ - if (strcmp(kind, "string") == 0 || strcmp(kind, "text") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - const char *val = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(node, "value")); - if (val) { - int ki = mach_cpool_add_str(cs, val); - mach_emit(cs, MACH_ABx(MACH_LOADK, dest, ki)); - } else { - int ki = mach_cpool_add_str(cs, ""); - mach_emit(cs, MACH_ABx(MACH_LOADK, dest, ki)); - } - return dest; - } - - /* Template literal with expressions: kind="text literal" - Format: value = "hello {0} world {1}", list = [expr0, expr1] - Compile as: format(fmt_string, [expr0, expr1, ...]) */ - if (strcmp(kind, "text literal") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - int save_freereg = cs->freereg; - - cJSON *list = cJSON_GetObjectItemCaseSensitive(node, "list"); - int nexpr = list ? cJSON_GetArraySize(list) : 0; - - /* Reserve consecutive regs for call: [format_fn, fmt_str, arr] */ - int call_base = mach_reserve_reg(cs); - int arg1_reg = mach_reserve_reg(cs); - int arg2_reg = mach_reserve_reg(cs); - - /* Reserve consecutive regs for NEWARRAY: arr_reg, then elem slots */ - int arr_base = mach_reserve_reg(cs); - for (int i = 0; i < nexpr; i++) mach_reserve_reg(cs); - /* Now arr_base+1..arr_base+nexpr are the element slots */ - - /* Compile expressions into arr_base+1..arr_base+nexpr */ - for (int i = 0; i < nexpr; i++) { - cJSON *expr = cJSON_GetArrayItem(list, i); - int slot = arr_base + 1 + i; - int r = mach_compile_expr(cs, expr, slot); - if (r != slot) mach_emit(cs, MACH_ABC(MACH_MOVE, slot, r, 0)); - } - - /* Create array from consecutive element regs */ - mach_emit(cs, MACH_ABC(MACH_NEWARRAY, arr_base, nexpr, 0)); - - /* Load format function */ - int ki_format = mach_cpool_add_str(cs, "format"); - mach_emit(cs, MACH_ABx(MACH_GETINTRINSIC, call_base, ki_format)); - - /* Load format string */ - const char *fmt = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(node, "value")); - int ki_fmt = mach_cpool_add_str(cs, fmt ? fmt : ""); - mach_emit(cs, MACH_ABx(MACH_LOADK, arg1_reg, ki_fmt)); - - /* Move array to arg2 position */ - mach_emit(cs, MACH_ABC(MACH_MOVE, arg2_reg, arr_base, 0)); - - /* Call format(fmt, arr) */ - mach_emit(cs, MACH_ABC(MACH_CALL, call_base, 2, 1)); - - mach_free_reg_to(cs, save_freereg); - if (dest != call_base) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, call_base, 0)); - return dest; - } - - /* Boolean/null literals */ - if (strcmp(kind, "true") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADTRUE, dest, 0)); - return dest; - } - if (strcmp(kind, "false") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADFALSE, dest, 0)); - return dest; - } - if (strcmp(kind, "null") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADNULL, dest, 0)); - return dest; - } - - /* Name (variable reference) */ - if (strcmp(kind, "name") == 0) { - const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(node, "name")); - if (name) { - cJSON *level_node = cJSON_GetObjectItemCaseSensitive(node, "level"); - int level = level_node ? (int)cJSON_GetNumberValue(level_node) : -1; - - if (level == 0) { - /* Local variable */ - int slot = mach_find_var(cs, name); - if (slot >= 0) { - if (dest >= 0 && dest != slot) { - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0)); - return dest; - } - return slot; - } - } else if (level > 0) { - /* Closure variable — walk parent compiler states for slot */ - MachCompState *target = cs; - for (int i = 0; i < level; i++) target = target->parent; - int slot = mach_find_var(target, name); - if (dest < 0) dest = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABC(MACH_GETUP, dest, level, slot)); - return dest; - } - /* Unbound or fallback — emit placeholder, patched at link time */ - if (dest < 0) dest = mach_reserve_reg(cs); - int ki = mach_cpool_add_str(cs, name); - mach_emit(cs, MACH_ABx(MACH_GETNAME, dest, ki)); - return dest; - } - if (dest < 0) dest = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADNULL, dest, 0)); - return dest; - } - - /* Function call: kind="(" */ - if (strcmp(kind, "(") == 0) { - cJSON *fn_expr = cJSON_GetObjectItemCaseSensitive(node, "expression"); - cJSON *args = cJSON_GetObjectItemCaseSensitive(node, "list"); - int nargs = args ? cJSON_GetArraySize(args) : 0; - - /* Check if this is a method call: obj.method(args) or obj[key](args) */ - const char *fn_kind = NULL; - if (fn_expr) - fn_kind = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(fn_expr, "kind")); - - /* Functino: inline operator call */ - if (fn_kind && strcmp(fn_kind, "name") == 0) { - const char *fn_make = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(fn_expr, "make")); - if (fn_make && strcmp(fn_make, "functino") == 0) { - const char *fname = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(fn_expr, "name")); - if (dest < 0) dest = mach_reserve_reg(cs); - - if (strcmp(fname, "~!") == 0) { - int save = cs->freereg; - int r = mach_compile_expr(cs, cJSON_GetArrayItem(args, 0), -1); - mach_emit(cs, MACH_ABC(MACH_BNOT, dest, r, 0)); - mach_free_reg_to(cs, save); - return dest; - } - if (strcmp(fname, "[]!") == 0) { - int save = cs->freereg; - int r0 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 0), -1); - if (cs->freereg <= r0) cs->freereg = r0 + 1; - int r1 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 1), -1); - mach_emit(cs, MACH_ABC(MACH_GETINDEX, dest, r0, r1)); - mach_free_reg_to(cs, save); - return dest; - } - if ((strcmp(fname, "=!") == 0 || strcmp(fname, "!=!") == 0) && nargs == 3) { - int save = cs->freereg; - int base = mach_reserve_reg(cs); - int r0 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 0), base); - if (r0 != base) mach_emit(cs, MACH_ABC(MACH_MOVE, base, r0, 0)); - int r1_reg = mach_reserve_reg(cs); - int r1 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 1), r1_reg); - if (r1 != r1_reg) mach_emit(cs, MACH_ABC(MACH_MOVE, r1_reg, r1, 0)); - int r2_reg = mach_reserve_reg(cs); - int r2 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 2), r2_reg); - if (r2 != r2_reg) mach_emit(cs, MACH_ABC(MACH_MOVE, r2_reg, r2, 0)); - MachOpcode top = (strcmp(fname, "=!") == 0) ? MACH_EQ_TOL : MACH_NEQ_TOL; - mach_emit(cs, MACH_ABC(top, dest, base, 3)); - mach_free_reg_to(cs, save); - return dest; - } - if (strcmp(fname, "&&!") == 0) { - int save = cs->freereg; - int r0 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 0), -1); - if (cs->freereg <= r0) cs->freereg = r0 + 1; - int r1 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 1), -1); - /* Non-short-circuiting: if left is falsy, result=left, else result=right */ - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, r0, 0)); - int jmp_pc = mach_current_pc(cs); - mach_emit(cs, MACH_AsBx(MACH_JMPFALSE, dest, 0)); - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, r1, 0)); - { - int offset = mach_current_pc(cs) - (jmp_pc + 1); - cs->code[jmp_pc] = MACH_AsBx(MACH_GET_OP(cs->code[jmp_pc]), dest, (int16_t)offset); - } - mach_free_reg_to(cs, save); - return dest; - } - if (strcmp(fname, "||!") == 0) { - int save = cs->freereg; - int r0 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 0), -1); - if (cs->freereg <= r0) cs->freereg = r0 + 1; - int r1 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 1), -1); - /* Non-short-circuiting: if left is truthy, result=left, else result=right */ - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, r0, 0)); - int jmp_pc = mach_current_pc(cs); - mach_emit(cs, MACH_AsBx(MACH_JMPTRUE, dest, 0)); - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, r1, 0)); - { - int offset = mach_current_pc(cs) - (jmp_pc + 1); - cs->code[jmp_pc] = MACH_AsBx(MACH_GET_OP(cs->code[jmp_pc]), dest, (int16_t)offset); - } - mach_free_reg_to(cs, save); - return dest; - } - /* Standard 2-arg binary functino */ - { - int save = cs->freereg; - int r0 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 0), -1); - if (cs->freereg <= r0) cs->freereg = r0 + 1; - int r1 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 1), -1); - MachOpcode op; - if (strcmp(fname, "+!") == 0) op = MACH_ADD; - else if (strcmp(fname, "-!") == 0) op = MACH_SUB; - else if (strcmp(fname, "*!") == 0) op = MACH_MUL; - else if (strcmp(fname, "/!") == 0) op = MACH_DIV; - else if (strcmp(fname, "%!") == 0) op = MACH_MOD; - else if (strcmp(fname, "**!") == 0) op = MACH_POW; - else if (strcmp(fname, "!") == 0) op = MACH_GT; - else if (strcmp(fname, "<=!") == 0) op = MACH_LE; - else if (strcmp(fname, ">=!") == 0) op = MACH_GE; - else if (strcmp(fname, "=!") == 0) op = MACH_EQ; - else if (strcmp(fname, "!=!") == 0) op = MACH_NEQ; - else if (strcmp(fname, "&!") == 0) op = MACH_BAND; - else if (strcmp(fname, "|!") == 0) op = MACH_BOR; - else if (strcmp(fname, "^!") == 0) op = MACH_BXOR; - else if (strcmp(fname, "<>!") == 0) op = MACH_SHR; - else op = MACH_USHR; /* >>>! */ - mach_emit(cs, MACH_ABC(op, dest, r0, r1)); - mach_free_reg_to(cs, save); - return dest; - } - } - } - - if (fn_kind && strcmp(fn_kind, ".") == 0) { - /* Method call with dot notation: obj.method(args) */ - int save_freereg = cs->freereg; - int base = mach_reserve_reg(cs); /* R(base) = obj */ - if (dest < 0) dest = base; - mach_reserve_reg(cs); /* R(base+1) = temp slot for VM */ - - /* Compile obj into base */ - cJSON *obj_expr = cJSON_GetObjectItemCaseSensitive(fn_expr, "expression"); - if (!obj_expr) obj_expr = cJSON_GetObjectItemCaseSensitive(fn_expr, "left"); - int obj_r = mach_compile_expr(cs, obj_expr, base); - if (obj_r != base) - mach_emit(cs, MACH_ABC(MACH_MOVE, base, obj_r, 0)); - - /* Extract property name */ - cJSON *prop = cJSON_GetObjectItemCaseSensitive(fn_expr, "name"); - if (!prop) prop = cJSON_GetObjectItemCaseSensitive(fn_expr, "right"); - const char *prop_name = NULL; - if (cJSON_IsString(prop)) prop_name = cJSON_GetStringValue(prop); - else if (prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop, "value")); - if (!prop_name && prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop, "name")); - if (!prop_name) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(fn_expr, "value")); - if (!prop_name) prop_name = "unknown"; - - int ki = mach_cpool_add_str(cs, prop_name); - - /* If ki overflows 8-bit C field, load key into R(base+1) register */ - if (ki >= 0xFF) { - mach_emit(cs, MACH_ABx(MACH_LOADK, base + 1, ki)); - } - - /* Compile args into R(base+2)..R(base+1+nargs) */ - for (int i = 0; i < nargs; i++) { - int arg_reg = mach_reserve_reg(cs); - cJSON *arg = cJSON_GetArrayItem(args, i); - int r = mach_compile_expr(cs, arg, arg_reg); - if (r != arg_reg) - mach_emit(cs, MACH_ABC(MACH_MOVE, arg_reg, r, 0)); - } - - mach_emit(cs, MACH_ABC(MACH_CALLMETHOD, base, nargs, ki >= 0xFF ? 0xFF : ki)); - mach_free_reg_to(cs, save_freereg); - if (dest >= 0 && dest != base) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, base, 0)); - else - dest = base; - return dest; - } - - if (fn_kind && strcmp(fn_kind, "[") == 0) { - /* Method call with bracket notation: obj[expr](args) */ - int save_freereg = cs->freereg; - int base = mach_reserve_reg(cs); /* R(base) = obj */ - if (dest < 0) dest = base; - int key_reg = mach_reserve_reg(cs); /* R(base+1) = key */ - - /* Compile obj into base */ - cJSON *obj_expr = cJSON_GetObjectItemCaseSensitive(fn_expr, "expression"); - if (!obj_expr) obj_expr = cJSON_GetObjectItemCaseSensitive(fn_expr, "left"); - int obj_r = mach_compile_expr(cs, obj_expr, base); - if (obj_r != base) - mach_emit(cs, MACH_ABC(MACH_MOVE, base, obj_r, 0)); - - /* Compile key expr into R(base+1) */ - cJSON *idx_expr = cJSON_GetObjectItemCaseSensitive(fn_expr, "index"); - if (!idx_expr) idx_expr = cJSON_GetObjectItemCaseSensitive(fn_expr, "right"); - int kr = mach_compile_expr(cs, idx_expr, key_reg); - if (kr != key_reg) - mach_emit(cs, MACH_ABC(MACH_MOVE, key_reg, kr, 0)); - - /* Compile args into R(base+2)..R(base+1+nargs) */ - for (int i = 0; i < nargs; i++) { - int arg_reg = mach_reserve_reg(cs); - cJSON *arg = cJSON_GetArrayItem(args, i); - int r = mach_compile_expr(cs, arg, arg_reg); - if (r != arg_reg) - mach_emit(cs, MACH_ABC(MACH_MOVE, arg_reg, r, 0)); - } - - /* C=0xFF signals key is in R(base+1) */ - mach_emit(cs, MACH_ABC(MACH_CALLMETHOD, base, nargs, 0xFF)); - mach_free_reg_to(cs, save_freereg); - if (dest >= 0 && dest != base) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, base, 0)); - else - dest = base; - return dest; - } - - /* Save freereg so we can allocate consecutive regs for call */ - int save_freereg = cs->freereg; - - /* Allocate base register for the function */ - int base = mach_reserve_reg(cs); - if (dest < 0) dest = base; /* result goes to base */ - - /* Compile function expression into base */ - int fn_reg = mach_compile_expr(cs, fn_expr, base); - if (fn_reg != base) { - mach_emit(cs, MACH_ABC(MACH_MOVE, base, fn_reg, 0)); - } - - /* Allocate consecutive arg registers and compile args */ - for (int i = 0; i < nargs; i++) { - int arg_reg = mach_reserve_reg(cs); - cJSON *arg = cJSON_GetArrayItem(args, i); - int r = mach_compile_expr(cs, arg, arg_reg); - if (r != arg_reg) { - mach_emit(cs, MACH_ABC(MACH_MOVE, arg_reg, r, 0)); - } - } - - /* Emit CALL: base=func, B=nargs, C=1 if we want result */ - int keep = (dest >= 0) ? 1 : 0; - mach_emit(cs, MACH_ABC(MACH_CALL, base, nargs, keep)); - - /* Restore freereg */ - mach_free_reg_to(cs, save_freereg); - - /* If we want the result and dest != base, move it */ - if (dest >= 0 && dest != base) { - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, base, 0)); - } else { - dest = base; - } - return dest; - } - - /* Binary operators */ - if (strcmp(kind, "+") == 0 || strcmp(kind, "-") == 0 || - strcmp(kind, "*") == 0 || strcmp(kind, "/") == 0 || - strcmp(kind, "%") == 0 || strcmp(kind, "**") == 0 || - strcmp(kind, "==") == 0 || strcmp(kind, "!=") == 0 || - strcmp(kind, "===") == 0 || strcmp(kind, "!==") == 0 || - strcmp(kind, "<") == 0 || strcmp(kind, "<=") == 0 || - strcmp(kind, ">") == 0 || strcmp(kind, ">=") == 0 || - strcmp(kind, "&") == 0 || strcmp(kind, "|") == 0 || - strcmp(kind, "^") == 0 || strcmp(kind, "<<") == 0 || - strcmp(kind, ">>") == 0 || strcmp(kind, ">>>") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - - int save = cs->freereg; - cJSON *left = cJSON_GetObjectItemCaseSensitive(node, "left"); - cJSON *right = cJSON_GetObjectItemCaseSensitive(node, "right"); - - int lr = mach_compile_expr(cs, left, -1); - if (cs->freereg <= lr) cs->freereg = lr + 1; /* protect lr from reuse */ - int rr = mach_compile_expr(cs, right, -1); - - MachOpcode op; - if (strcmp(kind, "+") == 0) op = MACH_ADD; - else if (strcmp(kind, "-") == 0) op = MACH_SUB; - else if (strcmp(kind, "*") == 0) op = MACH_MUL; - else if (strcmp(kind, "/") == 0) op = MACH_DIV; - else if (strcmp(kind, "%") == 0) op = MACH_MOD; - else if (strcmp(kind, "**") == 0) op = MACH_POW; - else if (strcmp(kind, "==") == 0 || strcmp(kind, "===") == 0) op = MACH_EQ; - else if (strcmp(kind, "!=") == 0 || strcmp(kind, "!==") == 0) op = MACH_NEQ; - else if (strcmp(kind, "<") == 0) op = MACH_LT; - else if (strcmp(kind, "<=") == 0) op = MACH_LE; - else if (strcmp(kind, ">") == 0) op = MACH_GT; - else if (strcmp(kind, ">=") == 0) op = MACH_GE; - else if (strcmp(kind, "&") == 0) op = MACH_BAND; - else if (strcmp(kind, "|") == 0) op = MACH_BOR; - else if (strcmp(kind, "^") == 0) op = MACH_BXOR; - else if (strcmp(kind, "<<") == 0) op = MACH_SHL; - else if (strcmp(kind, ">>") == 0) op = MACH_SHR; - else op = MACH_USHR; /* >>> */ - - mach_emit(cs, MACH_ABC(op, dest, lr, rr)); - mach_free_reg_to(cs, save); - return dest; - } - - /* Short-circuit logical operators */ - if (strcmp(kind, "&&") == 0 || strcmp(kind, "||") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - cJSON *left = cJSON_GetObjectItemCaseSensitive(node, "left"); - cJSON *right = cJSON_GetObjectItemCaseSensitive(node, "right"); - - int lr = mach_compile_expr(cs, left, dest); - if (lr != dest) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, lr, 0)); - - /* Emit conditional jump — patch offset later */ - int jmp_pc = mach_current_pc(cs); - if (strcmp(kind, "&&") == 0) - mach_emit(cs, MACH_AsBx(MACH_JMPFALSE, dest, 0)); /* skip right if false */ - else - mach_emit(cs, MACH_AsBx(MACH_JMPTRUE, dest, 0)); /* skip right if true */ - - int rr = mach_compile_expr(cs, right, dest); - if (rr != dest) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, rr, 0)); - - /* Patch jump offset: target is current PC, offset relative to instruction after jmp */ - int offset = mach_current_pc(cs) - (jmp_pc + 1); - cs->code[jmp_pc] = MACH_AsBx(MACH_GET_OP(cs->code[jmp_pc]), dest, (int16_t)offset); - return dest; - } - - /* Unary operators */ - if (strcmp(kind, "!") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - cJSON *operand = cJSON_GetObjectItemCaseSensitive(node, "expression"); - if (!operand) operand = cJSON_GetObjectItemCaseSensitive(node, "right"); - int save = cs->freereg; - int r = mach_compile_expr(cs, operand, -1); - mach_emit(cs, MACH_ABC(MACH_LNOT, dest, r, 0)); - mach_free_reg_to(cs, save); - return dest; - } - if (strcmp(kind, "~") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - cJSON *operand = cJSON_GetObjectItemCaseSensitive(node, "expression"); - if (!operand) operand = cJSON_GetObjectItemCaseSensitive(node, "right"); - int save = cs->freereg; - int r = mach_compile_expr(cs, operand, -1); - mach_emit(cs, MACH_ABC(MACH_BNOT, dest, r, 0)); - mach_free_reg_to(cs, save); - return dest; - } - if (strcmp(kind, "unary_-") == 0 || strcmp(kind, "-unary") == 0 || (strcmp(kind, "-") == 0 && !cJSON_GetObjectItemCaseSensitive(node, "left"))) { - if (dest < 0) dest = mach_reserve_reg(cs); - cJSON *operand = cJSON_GetObjectItemCaseSensitive(node, "expression"); - if (!operand) operand = cJSON_GetObjectItemCaseSensitive(node, "right"); - int save = cs->freereg; - int r = mach_compile_expr(cs, operand, -1); - mach_emit(cs, MACH_ABC(MACH_NEG, dest, r, 0)); - mach_free_reg_to(cs, save); - return dest; - } - - /* Unary plus: identity for numbers */ - if (strcmp(kind, "+unary") == 0 || strcmp(kind, "pos") == 0) { - cJSON *operand = cJSON_GetObjectItemCaseSensitive(node, "expression"); - return mach_compile_expr(cs, operand, dest); - } - - /* Comma operator: compile left for side effects, return right */ - if (strcmp(kind, ",") == 0) { - cJSON *left = cJSON_GetObjectItemCaseSensitive(node, "left"); - cJSON *right = cJSON_GetObjectItemCaseSensitive(node, "right"); - int save = cs->freereg; - mach_compile_expr(cs, left, -1); - mach_free_reg_to(cs, save); - return mach_compile_expr(cs, right, dest); - } - - /* Increment/Decrement as expression */ - if (strcmp(kind, "++") == 0 || strcmp(kind, "--") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - MachOpcode inc_op = (kind[0] == '+') ? MACH_INC : MACH_DEC; - cJSON *operand = cJSON_GetObjectItemCaseSensitive(node, "expression"); - cJSON *postfix_node = cJSON_GetObjectItemCaseSensitive(node, "postfix"); - int is_postfix = postfix_node && cJSON_IsTrue(postfix_node); - - const char *op_kind = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(operand, "kind")); - if (op_kind && strcmp(op_kind, "name") == 0) { - const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(operand, "name")); - cJSON *level_node = cJSON_GetObjectItemCaseSensitive(operand, "level"); - int level = level_node ? (int)cJSON_GetNumberValue(level_node) : -1; - if (level == 0 && name) { - int slot = mach_find_var(cs, name); - if (slot >= 0) { - if (is_postfix) { - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0)); - mach_emit(cs, MACH_ABC(inc_op, slot, slot, 0)); - } else { - mach_emit(cs, MACH_ABC(inc_op, slot, slot, 0)); - if (dest != slot) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0)); - } - return dest; - } - } else if (level > 0 && name) { - /* Closure variable */ - int save = cs->freereg; - MachCompState *target = cs; - for (int i = 0; i < level; i++) target = target->parent; - int slot = mach_find_var(target, name); - int val_r = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABC(MACH_GETUP, val_r, level, slot)); - if (is_postfix) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); - mach_emit(cs, MACH_ABC(inc_op, val_r, val_r, 0)); - if (!is_postfix) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); - mach_emit(cs, MACH_ABC(MACH_SETUP, val_r, level, slot)); - mach_free_reg_to(cs, save); - return dest; - } - } - /* Property access: obj.prop++ */ - if (op_kind && strcmp(op_kind, ".") == 0) { - int save = cs->freereg; - cJSON *obj_expr = cJSON_GetObjectItemCaseSensitive(operand, "expression"); - if (!obj_expr) obj_expr = cJSON_GetObjectItemCaseSensitive(operand, "left"); - cJSON *prop = cJSON_GetObjectItemCaseSensitive(operand, "name"); - if (!prop) prop = cJSON_GetObjectItemCaseSensitive(operand, "right"); - const char *prop_name = NULL; - if (cJSON_IsString(prop)) prop_name = cJSON_GetStringValue(prop); - else if (prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop, "value")); - if (!prop_name && prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop, "name")); - if (!prop_name) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(operand, "value")); - if (prop_name) { - int obj_r = mach_compile_expr(cs, obj_expr, -1); - if (cs->freereg <= obj_r) cs->freereg = obj_r + 1; - int ki = mach_cpool_add_str(cs, prop_name); - int val_r = mach_reserve_reg(cs); - if (ki < 256) { - mach_emit(cs, MACH_ABC(MACH_GETFIELD, val_r, obj_r, ki)); - } else { - int kr = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADK, kr, ki)); - mach_emit(cs, MACH_ABC(MACH_GETINDEX, val_r, obj_r, kr)); - } - if (is_postfix) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); - mach_emit(cs, MACH_ABC(inc_op, val_r, val_r, 0)); - if (!is_postfix) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); - if (ki < 256) { - mach_emit(cs, MACH_ABC(MACH_SETFIELD, obj_r, ki, val_r)); - } else { - int kr = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADK, kr, ki)); - mach_emit(cs, MACH_ABC(MACH_SETINDEX, obj_r, kr, val_r)); - } - mach_free_reg_to(cs, save); - return dest; - } - } - /* Computed property access: obj[idx]++ */ - if (op_kind && strcmp(op_kind, "[") == 0) { - int save = cs->freereg; - cJSON *obj_expr = cJSON_GetObjectItemCaseSensitive(operand, "expression"); - if (!obj_expr) obj_expr = cJSON_GetObjectItemCaseSensitive(operand, "left"); - cJSON *idx_expr = cJSON_GetObjectItemCaseSensitive(operand, "index"); - if (!idx_expr) idx_expr = cJSON_GetObjectItemCaseSensitive(operand, "right"); - int obj_r = mach_compile_expr(cs, obj_expr, -1); - if (cs->freereg <= obj_r) cs->freereg = obj_r + 1; - int idx_r = mach_compile_expr(cs, idx_expr, -1); - if (cs->freereg <= idx_r) cs->freereg = idx_r + 1; - int val_r = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABC(MACH_GETINDEX, val_r, obj_r, idx_r)); - if (is_postfix) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); - mach_emit(cs, MACH_ABC(inc_op, val_r, val_r, 0)); - if (!is_postfix) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); - mach_emit(cs, MACH_ABC(MACH_SETINDEX, obj_r, idx_r, val_r)); - mach_free_reg_to(cs, save); - return dest; - } - /* Fallback: just compile operand */ - return mach_compile_expr(cs, operand, dest); - } - - /* Compound assignment operators */ - if (strcmp(kind, "+=") == 0 || strcmp(kind, "-=") == 0 || - strcmp(kind, "*=") == 0 || strcmp(kind, "/=") == 0 || - strcmp(kind, "%=") == 0 || strcmp(kind, "**=") == 0 || - strcmp(kind, "&=") == 0 || strcmp(kind, "|=") == 0 || - strcmp(kind, "^=") == 0 || strcmp(kind, "<<=") == 0 || - strcmp(kind, ">>=") == 0 || strcmp(kind, ">>>=") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - cJSON *left = cJSON_GetObjectItemCaseSensitive(node, "left"); - cJSON *right = cJSON_GetObjectItemCaseSensitive(node, "right"); - - /* Map compound op to binary op */ - MachOpcode binop; - if (strcmp(kind, "+=") == 0) binop = MACH_ADD; - else if (strcmp(kind, "-=") == 0) binop = MACH_SUB; - else if (strcmp(kind, "*=") == 0) binop = MACH_MUL; - else if (strcmp(kind, "/=") == 0) binop = MACH_DIV; - else if (strcmp(kind, "%=") == 0) binop = MACH_MOD; - else if (strcmp(kind, "**=") == 0) binop = MACH_POW; - else if (strcmp(kind, "&=") == 0) binop = MACH_BAND; - else if (strcmp(kind, "|=") == 0) binop = MACH_BOR; - else if (strcmp(kind, "^=") == 0) binop = MACH_BXOR; - else if (strcmp(kind, "<<=") == 0) binop = MACH_SHL; - else if (strcmp(kind, ">>=") == 0) binop = MACH_SHR; - else binop = MACH_USHR; /* >>>= */ - - const char *lk = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(left, "kind")); - if (lk && strcmp(lk, "name") == 0) { - const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(left, "name")); - cJSON *level_node = cJSON_GetObjectItemCaseSensitive(left, "level"); - int level = level_node ? (int)cJSON_GetNumberValue(level_node) : -1; - if (level == 0 && name) { - int slot = mach_find_var(cs, name); - if (slot >= 0) { - int save = cs->freereg; - int rr = mach_compile_expr(cs, right, -1); - mach_emit(cs, MACH_ABC(binop, slot, slot, rr)); - mach_free_reg_to(cs, save); - if (dest != slot) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0)); - return dest; - } - } - } - /* Fallback: load null */ - mach_emit(cs, MACH_ABx(MACH_LOADNULL, dest, 0)); - return dest; - } - - /* In operator */ - if (strcmp(kind, "in") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - cJSON *left = cJSON_GetObjectItemCaseSensitive(node, "left"); - cJSON *right = cJSON_GetObjectItemCaseSensitive(node, "right"); - int save = cs->freereg; - int lr = mach_compile_expr(cs, left, -1); - if (cs->freereg <= lr) cs->freereg = lr + 1; - int rr = mach_compile_expr(cs, right, -1); - mach_emit(cs, MACH_ABC(MACH_HASPROP, dest, rr, lr)); - mach_free_reg_to(cs, save); - return dest; - } - - /* Assignment */ - if (strcmp(kind, "assign") == 0) { - cJSON *left = cJSON_GetObjectItemCaseSensitive(node, "left"); - cJSON *right = cJSON_GetObjectItemCaseSensitive(node, "right"); - - /* Push: arr[] = val */ - cJSON *push_node = cJSON_GetObjectItemCaseSensitive(node, "push"); - if (push_node && cJSON_IsTrue(push_node)) { - if (dest < 0) dest = mach_reserve_reg(cs); - int save = cs->freereg; - cJSON *arr_expr = cJSON_GetObjectItemCaseSensitive(left, "left"); - if (!arr_expr) arr_expr = cJSON_GetObjectItemCaseSensitive(left, "expression"); - int arr_r = mach_compile_expr(cs, arr_expr, -1); - int val_r = mach_compile_expr(cs, right, -1); - mach_emit(cs, MACH_ABC(MACH_PUSH, arr_r, val_r, 0)); - if (dest >= 0) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); - mach_free_reg_to(cs, save); - return dest; - } - - const char *lk = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(left, "kind")); - - if (lk && strcmp(lk, "name") == 0) { - const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(left, "name")); - cJSON *level_node = cJSON_GetObjectItemCaseSensitive(left, "level"); - int level = level_node ? (int)cJSON_GetNumberValue(level_node) : -1; - - if (level == 0) { - /* Local assignment */ - int slot = name ? mach_find_var(cs, name) : -1; - if (slot >= 0) { - int r = mach_compile_expr(cs, right, slot); - if (r != slot) - mach_emit(cs, MACH_ABC(MACH_MOVE, slot, r, 0)); - if (dest >= 0 && dest != slot) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0)); - return slot; - } - } else if (level > 0) { - /* Closure assignment — walk parent states for slot */ - MachCompState *target = cs; - for (int i = 0; i < level; i++) target = target->parent; - int slot = mach_find_var(target, name); - if (dest < 0) dest = mach_reserve_reg(cs); - int r = mach_compile_expr(cs, right, dest); - if (r != dest) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, r, 0)); - mach_emit(cs, MACH_ABC(MACH_SETUP, dest, level, slot)); - return dest; - } - /* Unbound (level -1) — error, AST parser should have rejected this */ - if (dest < 0) dest = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADNULL, dest, 0)); - return dest; - } - - /* Property assignment: left kind="." */ - if (lk && strcmp(lk, ".") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - int save = cs->freereg; - cJSON *obj_expr = cJSON_GetObjectItemCaseSensitive(left, "expression"); - if (!obj_expr) obj_expr = cJSON_GetObjectItemCaseSensitive(left, "left"); - cJSON *prop = cJSON_GetObjectItemCaseSensitive(left, "name"); - if (!prop) prop = cJSON_GetObjectItemCaseSensitive(left, "right"); - const char *prop_name = NULL; - if (cJSON_IsString(prop)) prop_name = cJSON_GetStringValue(prop); - else if (prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop, "value")); - if (!prop_name && prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop, "name")); - if (!prop_name) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(left, "value")); - - int obj_r = mach_compile_expr(cs, obj_expr, -1); - if (cs->freereg <= obj_r) cs->freereg = obj_r + 1; - int val_r = mach_compile_expr(cs, right, dest); - if (prop_name) { - int ki = mach_cpool_add_str(cs, prop_name); - if (ki < 256) { - mach_emit(cs, MACH_ABC(MACH_SETFIELD, obj_r, ki, val_r)); - } else { - int kr = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADK, kr, ki)); - mach_emit(cs, MACH_ABC(MACH_SETINDEX, obj_r, kr, val_r)); - } - } - mach_free_reg_to(cs, save); - return val_r; - } - - /* Computed property assignment: left kind="[" */ - if (lk && strcmp(lk, "[") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - int save = cs->freereg; - cJSON *obj_expr = cJSON_GetObjectItemCaseSensitive(left, "expression"); - if (!obj_expr) obj_expr = cJSON_GetObjectItemCaseSensitive(left, "left"); - cJSON *idx_expr = cJSON_GetObjectItemCaseSensitive(left, "index"); - if (!idx_expr) idx_expr = cJSON_GetObjectItemCaseSensitive(left, "right"); - - int obj_r = mach_compile_expr(cs, obj_expr, -1); - if (cs->freereg <= obj_r) cs->freereg = obj_r + 1; - int idx_r = mach_compile_expr(cs, idx_expr, -1); - if (cs->freereg <= idx_r) cs->freereg = idx_r + 1; - int val_r = mach_compile_expr(cs, right, dest); - mach_emit(cs, MACH_ABC(MACH_SETINDEX, obj_r, idx_r, val_r)); - mach_free_reg_to(cs, save); - return val_r; - } - - /* Fallback */ - if (dest < 0) dest = mach_reserve_reg(cs); - mach_compile_expr(cs, right, dest); - return dest; - } - - /* Property access: kind="." */ - if (strcmp(kind, ".") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - int save = cs->freereg; - cJSON *obj_expr = cJSON_GetObjectItemCaseSensitive(node, "expression"); - if (!obj_expr) obj_expr = cJSON_GetObjectItemCaseSensitive(node, "left"); - cJSON *prop = cJSON_GetObjectItemCaseSensitive(node, "name"); - if (!prop) prop = cJSON_GetObjectItemCaseSensitive(node, "right"); - const char *prop_name = NULL; - if (cJSON_IsString(prop)) prop_name = cJSON_GetStringValue(prop); - else if (prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop, "value")); - if (!prop_name && prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop, "name")); - if (!prop_name) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(node, "value")); - - int obj_r = mach_compile_expr(cs, obj_expr, -1); - if (prop_name) { - int ki = mach_cpool_add_str(cs, prop_name); - if (ki < 256) { - mach_emit(cs, MACH_ABC(MACH_GETFIELD, dest, obj_r, ki)); - } else { - int kr = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADK, kr, ki)); - mach_emit(cs, MACH_ABC(MACH_GETINDEX, dest, obj_r, kr)); - } - } - mach_free_reg_to(cs, save); - return dest; - } - - /* Computed property access: kind="[" */ - if (strcmp(kind, "[") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - int save = cs->freereg; - cJSON *obj_expr = cJSON_GetObjectItemCaseSensitive(node, "expression"); - if (!obj_expr) obj_expr = cJSON_GetObjectItemCaseSensitive(node, "left"); - cJSON *idx_expr = cJSON_GetObjectItemCaseSensitive(node, "index"); - if (!idx_expr) idx_expr = cJSON_GetObjectItemCaseSensitive(node, "right"); - - int obj_r = mach_compile_expr(cs, obj_expr, -1); - if (cs->freereg <= obj_r) cs->freereg = obj_r + 1; - int idx_r = mach_compile_expr(cs, idx_expr, -1); - mach_emit(cs, MACH_ABC(MACH_GETINDEX, dest, obj_r, idx_r)); - mach_free_reg_to(cs, save); - return dest; - } - - /* Object literal: kind="object" or "record" */ - if (strcmp(kind, "object") == 0 || strcmp(kind, "record") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABC(MACH_NEWOBJECT, dest, 0, 0)); - cJSON *props = cJSON_GetObjectItemCaseSensitive(node, "list"); - if (props) { - int count = cJSON_GetArraySize(props); - for (int i = 0; i < count; i++) { - cJSON *prop = cJSON_GetArrayItem(props, i); - cJSON *key_node = cJSON_GetObjectItemCaseSensitive(prop, "key"); - if (!key_node) key_node = cJSON_GetObjectItemCaseSensitive(prop, "left"); - cJSON *val_node = cJSON_GetObjectItemCaseSensitive(prop, "value"); - if (!val_node) val_node = cJSON_GetObjectItemCaseSensitive(prop, "right"); - if (!val_node) val_node = cJSON_GetObjectItemCaseSensitive(prop, "expression"); - const char *key = cJSON_GetStringValue(key_node); - if (!key && key_node) key = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(key_node, "value")); - if (!key && key_node) key = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(key_node, "name")); - if (key && val_node) { - int save = cs->freereg; - int vr = mach_compile_expr(cs, val_node, -1); - int ki = mach_cpool_add_str(cs, key); - if (ki < 256) { - mach_emit(cs, MACH_ABC(MACH_SETFIELD, dest, ki, vr)); - } else { - int kr = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADK, kr, ki)); - mach_emit(cs, MACH_ABC(MACH_SETINDEX, dest, kr, vr)); - } - mach_free_reg_to(cs, save); - } - } - } - return dest; - } - - /* Array literal: kind="array" */ - if (strcmp(kind, "array") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - cJSON *elems = cJSON_GetObjectItemCaseSensitive(node, "list"); - int count = elems ? cJSON_GetArraySize(elems) : 0; - - /* Reserve consecutive regs for elements starting at arr_base+1. - If dest is below freereg, other temps occupy dest+1..freereg-1 - so we must use a fresh base to avoid clobbering them. */ - int save = cs->freereg; - int arr_base; - if (dest + 1 >= cs->freereg) { - arr_base = dest; - cs->freereg = dest + 1; - } else { - arr_base = mach_reserve_reg(cs); - } - for (int i = 0; i < count; i++) { - int er = mach_reserve_reg(cs); - cJSON *elem = cJSON_GetArrayItem(elems, i); - int r = mach_compile_expr(cs, elem, er); - if (r != er) mach_emit(cs, MACH_ABC(MACH_MOVE, er, r, 0)); - } - mach_emit(cs, MACH_ABC(MACH_NEWARRAY, arr_base, count, 0)); - if (arr_base != dest) - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, arr_base, 0)); - mach_free_reg_to(cs, save); - return dest; - } - - /* Ternary: kind="?" or "then" */ - if (strcmp(kind, "?") == 0 || strcmp(kind, "then") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - cJSON *cond = cJSON_GetObjectItemCaseSensitive(node, "expression"); - if (!cond) cond = cJSON_GetObjectItemCaseSensitive(node, "condition"); - cJSON *then_expr = cJSON_GetObjectItemCaseSensitive(node, "then"); - if (!then_expr) then_expr = cJSON_GetObjectItemCaseSensitive(node, "left"); - cJSON *else_expr = cJSON_GetObjectItemCaseSensitive(node, "else"); - if (!else_expr) else_expr = cJSON_GetObjectItemCaseSensitive(node, "right"); - - int save = cs->freereg; - int cr = mach_compile_expr(cs, cond, -1); - int jmpfalse_pc = mach_current_pc(cs); - mach_emit(cs, MACH_AsBx(MACH_JMPFALSE, cr, 0)); - mach_free_reg_to(cs, save); - - mach_compile_expr(cs, then_expr, dest); - int jmpend_pc = mach_current_pc(cs); - mach_emit(cs, MACH_sJ(MACH_JMP, 0)); - - /* Patch jmpfalse */ - int offset = mach_current_pc(cs) - (jmpfalse_pc + 1); - cs->code[jmpfalse_pc] = MACH_AsBx(MACH_JMPFALSE, cr, (int16_t)offset); - - mach_compile_expr(cs, else_expr, dest); - - /* Patch jmpend */ - offset = mach_current_pc(cs) - (jmpend_pc + 1); - cs->code[jmpend_pc] = MACH_sJ(MACH_JMP, offset); - return dest; - } - - /* Function literal */ - if (strcmp(kind, "function") == 0 || strcmp(kind, "=>") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - /* Compile nested function */ - MachCompState child = {0}; - child.parent = cs; - child.scopes = cs->scopes; - child.filename = cs->filename; - child.freereg = 1; /* slot 0 = this */ - child.pending_label = -1; - - /* Read function_nr from AST node */ - cJSON *fn_nr_node = cJSON_GetObjectItemCaseSensitive(node, "function_nr"); - child.function_nr = fn_nr_node ? (int)cJSON_GetNumberValue(fn_nr_node) : 0; - - /* Register parameters */ - cJSON *params = cJSON_GetObjectItemCaseSensitive(node, "params"); - if (!params) params = cJSON_GetObjectItemCaseSensitive(node, "parameters"); - if (!params) params = cJSON_GetObjectItemCaseSensitive(node, "list"); - int nparams = params ? cJSON_GetArraySize(params) : 0; - child.nr_args = nparams; - for (int i = 0; i < nparams; i++) { - cJSON *p = cJSON_GetArrayItem(params, i); - const char *pname = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(p, "name")); - if (!pname) pname = cJSON_GetStringValue(p); - if (pname) { - int slot = mach_reserve_reg(&child); - mach_add_var(&child, pname, slot, 1); - } - } - - /* Scan scope record for var/def declarations */ - mach_scan_scope(&child); - - /* Emit default parameter initialization */ - for (int i = 0; i < nparams; i++) { - cJSON *p = cJSON_GetArrayItem(params, i); - cJSON *default_expr = cJSON_GetObjectItemCaseSensitive(p, "expression"); - if (default_expr) { - int slot = 1 + i; /* param slots start at 1 (slot 0 = this) */ - /* If param is null, skip the JMP and fall into default code */ - mach_emit(&child, MACH_AsBx(MACH_JMPNULL, slot, 1)); - /* If param is NOT null, jump past the default code */ - int jmp_pc = mach_current_pc(&child); - mach_emit(&child, MACH_sJ(MACH_JMP, 0)); /* placeholder */ - - int save = child.freereg; - mach_compile_expr(&child, default_expr, slot); - child.freereg = save; - - /* Patch JMP offset */ - int offset = mach_current_pc(&child) - (jmp_pc + 1); - child.code[jmp_pc] = MACH_sJ(MACH_JMP, offset); - } - } - - /* Compile body */ - cJSON *body = cJSON_GetObjectItemCaseSensitive(node, "body"); - if (!body) body = node; /* statements may be directly on the function node */ - { - cJSON *stmts = cJSON_GetObjectItemCaseSensitive(body, "statements"); - if (!stmts) stmts = body; /* body might be the statements array directly */ - if (cJSON_IsArray(stmts)) { - int count = cJSON_GetArraySize(stmts); - for (int i = 0; i < count; i++) { - mach_compile_stmt(&child, cJSON_GetArrayItem(stmts, i)); - } - } - } - - /* Implicit return null */ - mach_emit(&child, MACH_ABC(MACH_RETNIL, 0, 0, 0)); - - /* Disruption clause — emitted after body, recorded as disruption_pc */ - int disruption_start = 0; - cJSON *disruption = cJSON_GetObjectItemCaseSensitive(node, "disruption"); - if (disruption && cJSON_IsArray(disruption)) { - disruption_start = mach_current_pc(&child); - int dcount = cJSON_GetArraySize(disruption); - for (int i = 0; i < dcount; i++) - mach_compile_stmt(&child, cJSON_GetArrayItem(disruption, i)); - mach_emit(&child, MACH_ABC(MACH_RETNIL, 0, 0, 0)); - } - - /* Build MachCode for the child function */ - cJSON *fn_scope = mach_find_scope_record(cs->scopes, child.function_nr); - cJSON *fn_ncs = fn_scope ? cJSON_GetObjectItemCaseSensitive(fn_scope, "nr_close_slots") : NULL; - MachCode *fn_code = sys_malloc(sizeof(MachCode)); - memset(fn_code, 0, sizeof(MachCode)); - fn_code->arity = nparams; - fn_code->nr_slots = child.maxreg; - fn_code->nr_close_slots = fn_ncs ? (int)cJSON_GetNumberValue(fn_ncs) : 0; - fn_code->entry_point = 0; - fn_code->instr_count = child.code_count; - fn_code->instructions = child.code; - fn_code->cpool_count = child.cpool_count; - fn_code->cpool = child.cpool; - fn_code->func_count = child.func_count; - fn_code->functions = child.functions; - fn_code->line_table = child.line_info; - fn_code->filename = cs->filename ? strdup(cs->filename) : NULL; - fn_code->disruption_pc = disruption_start; - - cJSON *fname = cJSON_GetObjectItemCaseSensitive(node, "name"); - if (fname && cJSON_IsString(fname)) { - const char *ns = cJSON_GetStringValue(fname); - fn_code->name = sys_malloc(strlen(ns) + 1); - strcpy(fn_code->name, ns); - } else { - fn_code->name = NULL; - } - - /* Free child var table (not code/cpool, those are owned by fn_code now) */ - for (int i = 0; i < child.var_count; i++) - sys_free(child.vars[i].name); - sys_free(child.vars); - - int fi = mach_add_function(cs, fn_code); - mach_emit(cs, MACH_ABx(MACH_CLOSURE, dest, fi)); - return dest; - } - - /* Delete operator */ - if (strcmp(kind, "delete") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - cJSON *operand = cJSON_GetObjectItemCaseSensitive(node, "expression"); - if (!operand) operand = cJSON_GetObjectItemCaseSensitive(node, "right"); - if (operand) { - const char *okind = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(operand, "kind")); - if (okind && strcmp(okind, ".") == 0) { - /* delete obj.prop */ - cJSON *obj_node = cJSON_GetObjectItemCaseSensitive(operand, "left"); - cJSON *prop_node = cJSON_GetObjectItemCaseSensitive(operand, "right"); - const char *pname = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop_node, "name")); - if (!pname) pname = cJSON_GetStringValue(prop_node); - int save = cs->freereg; - int objr = mach_compile_expr(cs, obj_node, -1); - int ki = mach_cpool_add_str(cs, pname); - mach_emit(cs, MACH_ABC(MACH_DELETE, dest, objr, ki)); - mach_free_reg_to(cs, save); - return dest; - } else if (okind && strcmp(okind, "[") == 0) { - /* delete obj[expr] */ - cJSON *obj_node = cJSON_GetObjectItemCaseSensitive(operand, "left"); - cJSON *idx_node = cJSON_GetObjectItemCaseSensitive(operand, "right"); - int save = cs->freereg; - int objr = mach_compile_expr(cs, obj_node, -1); - int ir = mach_compile_expr(cs, idx_node, -1); - mach_emit(cs, MACH_ABC(MACH_DELETEINDEX, dest, objr, ir)); - mach_free_reg_to(cs, save); - return dest; - } - } - mach_emit(cs, MACH_ABx(MACH_LOADTRUE, dest, 0)); - return dest; - } - - /* This reference — slot 0 is always 'this' */ - if (strcmp(kind, "this") == 0) { - if (dest >= 0 && dest != 0) { - mach_emit(cs, MACH_ABC(MACH_MOVE, dest, 0, 0)); - return dest; - } - return 0; - } - - /* Regex literal */ - if (strcmp(kind, "regexp") == 0) { - if (dest < 0) dest = mach_reserve_reg(cs); - const char *pattern = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(node, "pattern")); - const char *flags = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(node, "flags")); - if (!pattern) pattern = ""; - if (!flags) flags = ""; - int pi = mach_cpool_add_str(cs, pattern); - int fi = mach_cpool_add_str(cs, flags); - mach_emit(cs, MACH_ABC(MACH_REGEXP, dest, pi, fi)); - return dest; - } - - /* Fallback: unsupported expression kind — load null */ - if (dest < 0) dest = mach_reserve_reg(cs); - mach_emit(cs, MACH_ABx(MACH_LOADNULL, dest, 0)); - return dest; -} - -/* ---- Statement compiler ---- */ - -static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { - if (!stmt) return; - mach_set_pos(cs, stmt); - const char *kind = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(stmt, "kind")); - if (!kind) return; - - /* var / def declaration */ - if (strcmp(kind, "var") == 0 || strcmp(kind, "def") == 0) { - cJSON *left = cJSON_GetObjectItemCaseSensitive(stmt, "left"); - cJSON *right = cJSON_GetObjectItemCaseSensitive(stmt, "right"); - const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(left, "name")); - if (!name) return; - int slot = mach_find_var(cs, name); - if (slot < 0) { - slot = mach_reserve_reg(cs); - mach_add_var(cs, name, slot, strcmp(kind, "def") == 0); - } - /* Pop: var x = arr[] */ - cJSON *pop_node = cJSON_GetObjectItemCaseSensitive(stmt, "pop"); - if (pop_node && cJSON_IsTrue(pop_node) && right) { - cJSON *arr_expr = cJSON_GetObjectItemCaseSensitive(right, "left"); - if (!arr_expr) arr_expr = cJSON_GetObjectItemCaseSensitive(right, "expression"); - int save = cs->freereg; - int arr_r = mach_compile_expr(cs, arr_expr, -1); - mach_emit(cs, MACH_ABC(MACH_POP, slot, arr_r, 0)); - mach_free_reg_to(cs, save); - return; - } - if (right) { - int r = mach_compile_expr(cs, right, slot); - if (r != slot) - mach_emit(cs, MACH_ABC(MACH_MOVE, slot, r, 0)); - } - return; - } - - /* var_list: multiple declarations in one statement */ - if (strcmp(kind, "var_list") == 0) { - cJSON *list = cJSON_GetObjectItemCaseSensitive(stmt, "list"); - if (list && cJSON_IsArray(list)) { - int count = cJSON_GetArraySize(list); - for (int i = 0; i < count; i++) - mach_compile_stmt(cs, cJSON_GetArrayItem(list, i)); - } - return; - } - - /* Function declaration statement */ - if (strcmp(kind, "function") == 0) { - const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(stmt, "name")); - if (!name) return; - int slot = mach_find_var(cs, name); - if (slot < 0) { - slot = mach_reserve_reg(cs); - mach_add_var(cs, name, slot, 1); - } - mach_compile_expr(cs, stmt, slot); - return; - } - - /* Expression statement (call) */ - if (strcmp(kind, "call") == 0) { - cJSON *expr = cJSON_GetObjectItemCaseSensitive(stmt, "expression"); - if (expr) { - int save = cs->freereg; - mach_compile_expr(cs, expr, -1); - mach_free_reg_to(cs, save); - } - return; - } - - /* Return statement */ - if (strcmp(kind, "return") == 0) { - cJSON *expr = cJSON_GetObjectItemCaseSensitive(stmt, "expression"); - if (expr) { - int save = cs->freereg; - int r = mach_compile_expr(cs, expr, -1); - mach_emit(cs, MACH_ABC(MACH_RETURN, r, 0, 0)); - mach_free_reg_to(cs, save); - } else { - mach_emit(cs, MACH_ABC(MACH_RETNIL, 0, 0, 0)); - } - return; - } - - /* Block */ - if (strcmp(kind, "block") == 0) { - cJSON *stmts = cJSON_GetObjectItemCaseSensitive(stmt, "statements"); - if (stmts && cJSON_IsArray(stmts)) { - int count = cJSON_GetArraySize(stmts); - for (int i = 0; i < count; i++) { - mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); - } - } - return; - } - - /* If statement */ - if (strcmp(kind, "if") == 0) { - cJSON *cond = cJSON_GetObjectItemCaseSensitive(stmt, "expression"); - if (!cond) cond = cJSON_GetObjectItemCaseSensitive(stmt, "condition"); - cJSON *then_body = cJSON_GetObjectItemCaseSensitive(stmt, "then"); - if (!then_body) then_body = cJSON_GetObjectItemCaseSensitive(stmt, "block"); - cJSON *else_body = cJSON_GetObjectItemCaseSensitive(stmt, "else"); - - int save = cs->freereg; - int cr = mach_compile_expr(cs, cond, -1); - int jmpfalse_pc = mach_current_pc(cs); - mach_emit(cs, MACH_AsBx(MACH_JMPFALSE, cr, 0)); - mach_free_reg_to(cs, save); - - /* Compile then branch — "then" is a direct array of statements */ - if (then_body) { - if (cJSON_IsArray(then_body)) { - int count = cJSON_GetArraySize(then_body); - for (int i = 0; i < count; i++) - mach_compile_stmt(cs, cJSON_GetArrayItem(then_body, i)); - } else { - cJSON *stmts = cJSON_GetObjectItemCaseSensitive(then_body, "statements"); - if (stmts && cJSON_IsArray(stmts)) { - int count = cJSON_GetArraySize(stmts); - for (int i = 0; i < count; i++) - mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); - } else { - mach_compile_stmt(cs, then_body); - } - } - } - - /* Check for else-if chain ("list") or plain else */ - if (!else_body) { - cJSON *list = cJSON_GetObjectItemCaseSensitive(stmt, "list"); - if (list && cJSON_IsArray(list) && cJSON_GetArraySize(list) > 0) - else_body = cJSON_GetArrayItem(list, 0); - } - - if (else_body) { - int jmpend_pc = mach_current_pc(cs); - mach_emit(cs, MACH_sJ(MACH_JMP, 0)); - - /* Patch jmpfalse to else */ - int offset = mach_current_pc(cs) - (jmpfalse_pc + 1); - cs->code[jmpfalse_pc] = MACH_AsBx(MACH_JMPFALSE, cr, (int16_t)offset); - - /* Compile else — could be a direct array, object, or else-if stmt */ - if (cJSON_IsArray(else_body)) { - int count = cJSON_GetArraySize(else_body); - for (int i = 0; i < count; i++) - mach_compile_stmt(cs, cJSON_GetArrayItem(else_body, i)); - } else { - cJSON *stmts = cJSON_GetObjectItemCaseSensitive(else_body, "statements"); - if (stmts && cJSON_IsArray(stmts)) { - int count = cJSON_GetArraySize(stmts); - for (int i = 0; i < count; i++) - mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); - } else { - mach_compile_stmt(cs, else_body); - } - } - - /* Patch jmpend */ - offset = mach_current_pc(cs) - (jmpend_pc + 1); - cs->code[jmpend_pc] = MACH_sJ(MACH_JMP, offset); - } else { - /* No else — patch jmpfalse to after then */ - int offset = mach_current_pc(cs) - (jmpfalse_pc + 1); - cs->code[jmpfalse_pc] = MACH_AsBx(MACH_JMPFALSE, cr, (int16_t)offset); - } - return; - } - - /* While loop */ - if (strcmp(kind, "while") == 0) { - cJSON *cond = cJSON_GetObjectItemCaseSensitive(stmt, "expression"); - if (!cond) cond = cJSON_GetObjectItemCaseSensitive(stmt, "condition"); - - int my_label = cs->pending_label; - cs->pending_label = -1; - - int old_break = cs->loop_break; - int old_continue = cs->loop_continue; - cs->loop_break = -1; - cs->loop_continue = -1; - - int loop_top = mach_current_pc(cs); - - int save = cs->freereg; - int cr = mach_compile_expr(cs, cond, -1); - int jmpfalse_pc = mach_current_pc(cs); - mach_emit(cs, MACH_AsBx(MACH_JMPFALSE, cr, 0)); - mach_free_reg_to(cs, save); - - /* Compile body — "statements" on a child "block"/"body", or directly on the node */ - { - cJSON *body = cJSON_GetObjectItemCaseSensitive(stmt, "block"); - if (!body) body = cJSON_GetObjectItemCaseSensitive(stmt, "body"); - cJSON *stmts = body ? cJSON_GetObjectItemCaseSensitive(body, "statements") : NULL; - if (!stmts) stmts = cJSON_GetObjectItemCaseSensitive(stmt, "statements"); - if (stmts && cJSON_IsArray(stmts)) { - int count = cJSON_GetArraySize(stmts); - for (int i = 0; i < count; i++) - mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); - } else if (body) { - mach_compile_stmt(cs, body); - } - } - - /* Patch continue chain to loop_top */ - { - int cp = cs->loop_continue; - while (cp >= 0) { - int prev = MACH_GET_sJ(cs->code[cp]); - int off = loop_top - (cp + 1); - cs->code[cp] = MACH_sJ(MACH_JMP, off); - cp = prev; - } - /* Also patch labeled continue chain */ - if (my_label >= 0) { - cp = cs->labels[my_label].continue_chain; - while (cp >= 0) { - int prev = MACH_GET_sJ(cs->code[cp]); - int off = loop_top - (cp + 1); - cs->code[cp] = MACH_sJ(MACH_JMP, off); - cp = prev; - } - cs->labels[my_label].continue_chain = -1; - } - } - - /* Jump back to loop top */ - int offset = loop_top - (mach_current_pc(cs) + 1); - mach_emit(cs, MACH_sJ(MACH_JMP, offset)); - - /* Patch jmpfalse to after loop */ - offset = mach_current_pc(cs) - (jmpfalse_pc + 1); - cs->code[jmpfalse_pc] = MACH_AsBx(MACH_JMPFALSE, cr, (int16_t)offset); - - /* Patch break chain */ - int bp = cs->loop_break; - while (bp >= 0) { - int prev = MACH_GET_sJ(cs->code[bp]); - offset = mach_current_pc(cs) - (bp + 1); - cs->code[bp] = MACH_sJ(MACH_JMP, offset); - bp = prev; - } - cs->loop_break = old_break; - cs->loop_continue = old_continue; - return; - } - - /* Do-while loop */ - if (strcmp(kind, "do") == 0) { - cJSON *cond = cJSON_GetObjectItemCaseSensitive(stmt, "expression"); - if (!cond) cond = cJSON_GetObjectItemCaseSensitive(stmt, "condition"); - - int my_label = cs->pending_label; - cs->pending_label = -1; - - int old_break = cs->loop_break; - int old_continue = cs->loop_continue; - cs->loop_break = -1; - cs->loop_continue = -1; - - int body_top = mach_current_pc(cs); - - /* Compile body first (always executes at least once) */ - { - cJSON *body = cJSON_GetObjectItemCaseSensitive(stmt, "block"); - if (!body) body = cJSON_GetObjectItemCaseSensitive(stmt, "body"); - cJSON *stmts = body ? cJSON_GetObjectItemCaseSensitive(body, "statements") : NULL; - if (!stmts) stmts = cJSON_GetObjectItemCaseSensitive(stmt, "statements"); - if (stmts && cJSON_IsArray(stmts)) { - int count = cJSON_GetArraySize(stmts); - for (int i = 0; i < count; i++) - mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); - } else if (body) { - mach_compile_stmt(cs, body); - } - } - - /* Patch continue chain to condition check */ - int cond_pc = mach_current_pc(cs); - { - int cp = cs->loop_continue; - while (cp >= 0) { - int prev = MACH_GET_sJ(cs->code[cp]); - int off = cond_pc - (cp + 1); - cs->code[cp] = MACH_sJ(MACH_JMP, off); - cp = prev; - } - /* Also patch labeled continue chain */ - if (my_label >= 0) { - cp = cs->labels[my_label].continue_chain; - while (cp >= 0) { - int prev = MACH_GET_sJ(cs->code[cp]); - int off = cond_pc - (cp + 1); - cs->code[cp] = MACH_sJ(MACH_JMP, off); - cp = prev; - } - cs->labels[my_label].continue_chain = -1; - } - } - - /* Condition check — jump back to body if true */ - int save = cs->freereg; - int cr = mach_compile_expr(cs, cond, -1); - int jmpfalse_pc = mach_current_pc(cs); - mach_emit(cs, MACH_AsBx(MACH_JMPFALSE, cr, 0)); - mach_free_reg_to(cs, save); - - /* Jump back to body top */ - int offset = body_top - (mach_current_pc(cs) + 1); - mach_emit(cs, MACH_sJ(MACH_JMP, offset)); - - /* Patch jmpfalse to after loop */ - offset = mach_current_pc(cs) - (jmpfalse_pc + 1); - cs->code[jmpfalse_pc] = MACH_AsBx(MACH_JMPFALSE, cr, (int16_t)offset); - - /* Patch break chain */ - int bp = cs->loop_break; - while (bp >= 0) { - int prev = MACH_GET_sJ(cs->code[bp]); - offset = mach_current_pc(cs) - (bp + 1); - cs->code[bp] = MACH_sJ(MACH_JMP, offset); - bp = prev; - } - cs->loop_break = old_break; - cs->loop_continue = old_continue; - return; - } - - /* For loop */ - if (strcmp(kind, "for") == 0) { - cJSON *init = cJSON_GetObjectItemCaseSensitive(stmt, "init"); - cJSON *cond = cJSON_GetObjectItemCaseSensitive(stmt, "test"); - cJSON *update = cJSON_GetObjectItemCaseSensitive(stmt, "update"); - cJSON *body = cJSON_GetObjectItemCaseSensitive(stmt, "block"); - if (!body) body = cJSON_GetObjectItemCaseSensitive(stmt, "body"); - - int my_label = cs->pending_label; - cs->pending_label = -1; - - int old_break = cs->loop_break; - int old_continue = cs->loop_continue; - cs->loop_break = -1; - cs->loop_continue = -1; - - /* Init */ - if (init) mach_compile_stmt(cs, init); - - int loop_top = mach_current_pc(cs); - - /* Condition */ - int jmpfalse_pc = -1; - if (cond) { - int save = cs->freereg; - int cr = mach_compile_expr(cs, cond, -1); - jmpfalse_pc = mach_current_pc(cs); - mach_emit(cs, MACH_AsBx(MACH_JMPFALSE, cr, 0)); - mach_free_reg_to(cs, save); - } - - /* Body — "statements" on a child "block"/"body", or directly on the for node */ - { - cJSON *stmts = body ? cJSON_GetObjectItemCaseSensitive(body, "statements") : NULL; - if (!stmts) stmts = cJSON_GetObjectItemCaseSensitive(stmt, "statements"); - if (stmts && cJSON_IsArray(stmts)) { - int count = cJSON_GetArraySize(stmts); - for (int i = 0; i < count; i++) - mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); - } else if (body) { - mach_compile_stmt(cs, body); - } - } - - /* Patch continue chain to update (or loop_top if no update) */ - { - int continue_target = mach_current_pc(cs); - int cp = cs->loop_continue; - while (cp >= 0) { - int prev = MACH_GET_sJ(cs->code[cp]); - int off = continue_target - (cp + 1); - cs->code[cp] = MACH_sJ(MACH_JMP, off); - cp = prev; - } - /* Also patch labeled continue chain */ - if (my_label >= 0) { - cp = cs->labels[my_label].continue_chain; - while (cp >= 0) { - int prev = MACH_GET_sJ(cs->code[cp]); - int off = continue_target - (cp + 1); - cs->code[cp] = MACH_sJ(MACH_JMP, off); - cp = prev; - } - cs->labels[my_label].continue_chain = -1; - } - } - - /* Update — assignment expressions must be compiled as statements */ - if (update) { - mach_compile_stmt(cs, update); - } - - /* Jump back */ - int offset = loop_top - (mach_current_pc(cs) + 1); - mach_emit(cs, MACH_sJ(MACH_JMP, offset)); - - /* Patch condition exit */ - if (jmpfalse_pc >= 0) { - offset = mach_current_pc(cs) - (jmpfalse_pc + 1); - /* Need to recover the register used for condition - use A from the instruction */ - int cr = MACH_GET_A(cs->code[jmpfalse_pc]); - cs->code[jmpfalse_pc] = MACH_AsBx(MACH_JMPFALSE, cr, (int16_t)offset); - } - - /* Patch break chain */ - int bp = cs->loop_break; - while (bp >= 0) { - int prev = MACH_GET_sJ(cs->code[bp]); - offset = mach_current_pc(cs) - (bp + 1); - cs->code[bp] = MACH_sJ(MACH_JMP, offset); - bp = prev; - } - cs->loop_break = old_break; - cs->loop_continue = old_continue; - return; - } - - /* Label statement */ - if (strcmp(kind, "label") == 0) { - const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(stmt, "name")); - cJSON *inner = cJSON_GetObjectItemCaseSensitive(stmt, "statement"); - if (inner && cs->label_count < MACH_MAX_LABELS) { - int li = cs->label_count++; - cs->labels[li].name = name; - cs->labels[li].break_chain = -1; - cs->labels[li].continue_chain = -1; - cs->pending_label = li; - mach_compile_stmt(cs, inner); - /* Patch labeled break chain to after the statement */ - int bp = cs->labels[li].break_chain; - while (bp >= 0) { - int prev = MACH_GET_sJ(cs->code[bp]); - int offset = mach_current_pc(cs) - (bp + 1); - cs->code[bp] = MACH_sJ(MACH_JMP, offset); - bp = prev; - } - cs->label_count--; - } else if (inner) { - mach_compile_stmt(cs, inner); - } - return; - } - - /* Break */ - if (strcmp(kind, "break") == 0) { - const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(stmt, "name")); - if (name) { - /* Labeled break — find the label */ - for (int i = cs->label_count - 1; i >= 0; i--) { - if (cs->labels[i].name && strcmp(cs->labels[i].name, name) == 0) { - int pc = mach_current_pc(cs); - mach_emit(cs, MACH_sJ(MACH_JMP, cs->labels[i].break_chain)); - cs->labels[i].break_chain = pc; - return; - } - } - } - int pc = mach_current_pc(cs); - mach_emit(cs, MACH_sJ(MACH_JMP, cs->loop_break)); - cs->loop_break = pc; - return; - } - - /* Continue */ - if (strcmp(kind, "continue") == 0) { - const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(stmt, "name")); - if (name) { - /* Labeled continue — find the label */ - for (int i = cs->label_count - 1; i >= 0; i--) { - if (cs->labels[i].name && strcmp(cs->labels[i].name, name) == 0) { - int pc = mach_current_pc(cs); - mach_emit(cs, MACH_sJ(MACH_JMP, cs->labels[i].continue_chain)); - cs->labels[i].continue_chain = pc; - return; - } - } - } - int pc = mach_current_pc(cs); - mach_emit(cs, MACH_sJ(MACH_JMP, cs->loop_continue)); - cs->loop_continue = pc; - return; - } - - /* Assignment as statement */ - if (strcmp(kind, "assign") == 0 || - strcmp(kind, "+=") == 0 || strcmp(kind, "-=") == 0 || - strcmp(kind, "*=") == 0 || strcmp(kind, "/=") == 0 || - strcmp(kind, "%=") == 0 || strcmp(kind, "**=") == 0 || - strcmp(kind, "&=") == 0 || strcmp(kind, "|=") == 0 || - strcmp(kind, "^=") == 0 || strcmp(kind, "<<=") == 0 || - strcmp(kind, ">>=") == 0 || strcmp(kind, ">>>=") == 0 || - strcmp(kind, "++") == 0 || strcmp(kind, "--") == 0) { - int save = cs->freereg; - mach_compile_expr(cs, stmt, -1); - mach_free_reg_to(cs, save); - return; - } - - /* Disrupt statement */ - if (strcmp(kind, "disrupt") == 0) { - mach_emit(cs, MACH_ABC(MACH_THROW, 0, 0, 0)); - return; - } - - /* Fallback: treat as expression statement */ - { - cJSON *expr = cJSON_GetObjectItemCaseSensitive(stmt, "expression"); - if (expr) { - int save = cs->freereg; - mach_compile_expr(cs, expr, -1); - mach_free_reg_to(cs, save); - } - } -} - /* ---- Link pass: resolve GETNAME to GETINTRINSIC or GETENV ---- */ static void mach_link_code(JSContext *ctx, JSCodeRegister *code, JSValue env) { @@ -1996,96 +105,6 @@ static void mach_link_code(JSContext *ctx, JSCodeRegister *code, JSValue env) { JS_PopGCRef(ctx, &env_ref); } -/* ---- Top-level compiler ---- */ - -static MachCode *mach_compile_program(MachCompState *cs, cJSON *ast) { - cJSON *stmts = cJSON_GetObjectItemCaseSensitive(ast, "statements"); - if (!stmts || !cJSON_IsArray(stmts)) return NULL; - - /* Read scopes array from AST */ - cs->scopes = cJSON_GetObjectItemCaseSensitive(ast, "scopes"); - cs->function_nr = 0; - - /* Extract filename for debug info */ - cs->filename = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(ast, "filename")); - - /* Scan scope record for declarations */ - mach_scan_scope(cs); - - /* Hoist function declarations */ - cJSON *functions = cJSON_GetObjectItemCaseSensitive(ast, "functions"); - if (functions && cJSON_IsArray(functions)) { - int fcount = cJSON_GetArraySize(functions); - for (int i = 0; i < fcount; i++) { - cJSON *fn_node = cJSON_GetArrayItem(functions, i); - const char *fn_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(fn_node, "name")); - if (!fn_name) continue; - int slot = mach_find_var(cs, fn_name); - if (slot < 0) continue; - mach_compile_expr(cs, fn_node, slot); - } - } - - /* Compile each statement */ - int count = cJSON_GetArraySize(stmts); - for (int i = 0; i < count; i++) { - mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); - } - - /* Implicit return null */ - mach_emit(cs, MACH_ABC(MACH_RETNIL, 0, 0, 0)); - - /* nr_close_slots from scope record */ - cJSON *prog_scope = mach_find_scope_record(cs->scopes, 0); - cJSON *ncs_node = prog_scope ? cJSON_GetObjectItemCaseSensitive(prog_scope, "nr_close_slots") : NULL; - - /* Build MachCode */ - MachCode *code = sys_malloc(sizeof(MachCode)); - memset(code, 0, sizeof(MachCode)); - code->arity = 0; - code->nr_slots = cs->maxreg; - code->nr_close_slots = ncs_node ? (int)cJSON_GetNumberValue(ncs_node) : 0; - code->entry_point = 0; - code->instr_count = cs->code_count; - code->instructions = cs->code; - code->cpool_count = cs->cpool_count; - code->cpool = cs->cpool; - code->func_count = cs->func_count; - code->functions = cs->functions; - code->name = NULL; - code->line_table = cs->line_info; - code->filename = cs->filename ? strdup(cs->filename) : NULL; - - return code; -} - -/* Public API: compile AST JSON to MachCode (context-free) */ -MachCode *JS_CompileMachTree(cJSON *ast) { - if (!ast) return NULL; - - MachCompState cs = {0}; - cs.freereg = 1; /* slot 0 = this */ - cs.maxreg = 1; - cs.pending_label = -1; - - MachCode *code = mach_compile_program(&cs, ast); - - /* Free var table (code/cpool/functions are owned by MachCode now) */ - for (int i = 0; i < cs.var_count; i++) - 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; -} - /* Free a MachCode tree (compiled but not yet loaded) */ void JS_FreeMachCode(MachCode *mc) { if (!mc) return; @@ -2691,14 +710,6 @@ void __asan_on_error(void) { pc = (uint32_t)(JS_VALUE_GET_INT(frame->address) >> 16); if (code->line_table && pc < code->instr_count) line = code->line_table[pc].line; - } else if (fn->kind == JS_FUNC_KIND_MCODE && fn->u.mcode.code) { - JSMCode *code = fn->u.mcode.code; - file = code->filename; - func_name = code->name; - if (!is_first) - pc = (uint32_t)(JS_VALUE_GET_INT(frame->address) >> 16); - if (code->line_table && pc < code->instr_count) - line = code->line_table[pc].line; } fprintf(stderr, " %s (%s:%u)\n", func_name ? func_name : "", @@ -2800,6 +811,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, ctx->reg_current_frame = frame_ref.val; ctx->current_register_pc = pc > 0 ? pc - 1 : 0; int op = MACH_GET_OP(instr); + /* trace disabled */ int a = MACH_GET_A(instr); int b = MACH_GET_B(instr); int c = MACH_GET_C(instr); @@ -3059,9 +1071,22 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, int depth = b; JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function); JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.reg.outer_frame); + if (!target) { + fprintf(stderr, "GETUP: NULL outer_frame at depth 0! pc=%d a=%d depth=%d slot=%d nr_slots=%d instr=0x%08x\n", + pc-1, a, depth, c, code->nr_slots, instr); + result = JS_ThrowInternalError(ctx, "GETUP: NULL outer_frame"); + goto disrupt; + } for (int d = 1; d < depth; d++) { fn = JS_VALUE_GET_FUNCTION(target->function); - target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.reg.outer_frame); + JSFrameRegister *next = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.reg.outer_frame); + if (!next) { + fprintf(stderr, "GETUP: NULL outer_frame at depth %d! pc=%d a=%d depth=%d slot=%d nr_slots=%d instr=0x%08x\n", + d, pc-1, a, depth, c, code->nr_slots, instr); + result = JS_ThrowInternalError(ctx, "GETUP: NULL outer_frame at depth %d", d); + goto disrupt; + } + target = next; } frame->slots[a] = target->slots[c]; break; @@ -4084,6 +2109,324 @@ static int ml_int(cJSON *arr, int idx) { return (int)cJSON_GetArrayItem(arr, idx)->valuedouble; } +/* ---- Register compression ---- + The mcode compiler allocates slots monotonically, producing register numbers + that can exceed 255. Since MachInstr32 uses 8-bit fields, we must compress + the register space via live-range analysis before lowering. + + For each slot we record its first and last instruction reference, then do a + greedy linear-scan allocation to pack them into the fewest physical registers. + Slots referenced by child functions via get/put (parent_slot) are in the + PARENT frame and are not remapped here — only current-frame register + operands are touched. */ + +#define MAX_REG_ITEMS 32 + +/* Return cJSON pointers to all current-frame register operands in an + instruction. out[] must have room for MAX_REG_ITEMS entries. */ +static int mcode_reg_items(cJSON *it, cJSON **out) { + int sz = cJSON_GetArraySize(it); + if (sz < 3) return 0; + const char *op = cJSON_GetArrayItem(it, 0)->valuestring; + int c = 0; + +#define ADD(pos) do { \ + cJSON *_r = cJSON_GetArrayItem(it, (pos)); \ + if (_r && cJSON_IsNumber(_r) && c < MAX_REG_ITEMS) out[c++] = _r; \ +} while (0) + + /* get/put: only [1] is current-frame (dest/src); [2]=parent_slot, [3]=level */ + if (!strcmp(op, "get") || !strcmp(op, "put")) { ADD(1); return c; } + + /* dest-only */ + if (!strcmp(op, "access") || !strcmp(op, "int") || + !strcmp(op, "function") || !strcmp(op, "regexp") || + !strcmp(op, "true") || !strcmp(op, "false") || !strcmp(op, "null")) + { ADD(1); return c; } + + /* invoke: [1]=frame, [2]=dest (result register) */ + if (!strcmp(op, "invoke")) { ADD(1); ADD(2); return c; } + /* goinvoke: [1]=frame only (no result) */ + if (!strcmp(op, "goinvoke")) { ADD(1); return c; } + + /* set_var: [1]=name(string), [2]=val */ + if (!strcmp(op, "set_var")) { ADD(2); return c; } + + /* setarg: [1]=call, [2]=arg_idx(const), [3]=val */ + if (!strcmp(op, "setarg")) { ADD(1); ADD(3); return c; } + + /* frame/goframe: [1]=call, [2]=func, [3]=nr_args(const) */ + if (!strcmp(op, "frame") || !strcmp(op, "goframe")) { ADD(1); ADD(2); return c; } + + /* no regs */ + if (!strcmp(op, "jump") || !strcmp(op, "disrupt")) return 0; + + /* cond only */ + if (!strcmp(op, "jump_true") || !strcmp(op, "jump_false") || + !strcmp(op, "jump_not_null")) + { ADD(1); return c; } + + /* single reg */ + if (!strcmp(op, "return")) { ADD(1); return c; } + + /* delete: [1]=dest, [2]=obj, [3]=key (string or reg) */ + if (!strcmp(op, "delete")) { + ADD(1); ADD(2); + cJSON *k = cJSON_GetArrayItem(it, 3); + if (k && cJSON_IsNumber(k)) out[c++] = k; + return c; + } + + /* record: [1]=dest, [2]=0(const) — no line/col suffix */ + if (!strcmp(op, "record")) { ADD(1); return c; } + + /* array: [1]=dest, [2]=count(const), [3..]=elements (no line/col suffix) */ + if (!strcmp(op, "array")) { + ADD(1); + int cnt = (int)cJSON_GetArrayItem(it, 2)->valuedouble; + for (int j = 0; j < cnt; j++) ADD(3 + j); + return c; + } + + /* load_field: [1]=dest, [2]=obj, [3]=key (string or reg) */ + if (!strcmp(op, "load_field")) { + ADD(1); ADD(2); + cJSON *key = cJSON_GetArrayItem(it, 3); + if (key && cJSON_IsNumber(key)) out[c++] = key; + return c; + } + + /* store_field: [1]=obj, [2]=val, [3]=key (string or reg) */ + if (!strcmp(op, "store_field")) { + ADD(1); ADD(2); + cJSON *key = cJSON_GetArrayItem(it, 3); + if (key && cJSON_IsNumber(key)) out[c++] = key; + return c; + } + + /* Default: every numeric operand in [1..sz-3] is a register. + Covers move, arithmetic, comparisons, type checks, push, pop, + load_dynamic, store_dynamic, in, concat, logical, bitwise, etc. */ + for (int j = 1; j < sz - 2; j++) { + cJSON *item = cJSON_GetArrayItem(it, j); + if (item && cJSON_IsNumber(item)) out[c++] = item; + } + return c; +#undef ADD +} + +/* Compress register numbers in a single function's mcode JSON so they + fit in 8 bits. Modifies the cJSON instructions and nr_slots in place. + Returns a malloc'd remap table (caller must free), or NULL if no + compression was needed. *out_old_nr_slots is set to the original count. */ +static int *mcode_compress_regs(cJSON *fobj, int *out_old_nr_slots, + int *captured_slots, int n_captured) { + cJSON *nr_slots_j = cJSON_GetObjectItemCaseSensitive(fobj, "nr_slots"); + int nr_slots = (int)cJSON_GetNumberValue(nr_slots_j); + *out_old_nr_slots = nr_slots; + return NULL; /* TEMP: disable compression to test */ + if (nr_slots <= 255) return NULL; + + int nr_args = (int)cJSON_GetNumberValue( + cJSON_GetObjectItemCaseSensitive(fobj, "nr_args")); + cJSON *instrs = cJSON_GetObjectItemCaseSensitive(fobj, "instructions"); + int n = instrs ? cJSON_GetArraySize(instrs) : 0; + + /* Step 1: build live ranges (first_ref / last_ref per slot) */ + int *first_ref = sys_malloc(nr_slots * sizeof(int)); + int *last_ref = sys_malloc(nr_slots * sizeof(int)); + for (int i = 0; i < nr_slots; i++) { first_ref[i] = -1; last_ref[i] = -1; } + + /* Args are live for the whole function */ + for (int i = 0; i < nr_args; i++) { first_ref[i] = 0; last_ref[i] = n; } + + for (int i = 0; i < n; i++) { + cJSON *it = cJSON_GetArrayItem(instrs, i); + if (!cJSON_IsArray(it)) continue; + cJSON *regs[MAX_REG_ITEMS]; + int rc = mcode_reg_items(it, regs); + for (int j = 0; j < rc; j++) { + int s = (int)regs[j]->valuedouble; + if (s < 0 || s >= nr_slots) continue; + if (first_ref[s] < 0) first_ref[s] = i; + last_ref[s] = i; + } + } + + /* Step 1a: extend live ranges for closure-captured slots. + If a child function captures a parent slot via get/put, that slot must + remain live for the entire parent function (the closure can read it at + any time while the parent frame is on the stack). */ + for (int ci = 0; ci < n_captured; ci++) { + int s = captured_slots[ci]; + if (s >= 0 && s < nr_slots) { + if (first_ref[s] < 0) first_ref[s] = 0; + last_ref[s] = n; + } + } + + /* Step 1b: extend live ranges for loops (backward jumps). + Build label→position map, then for each backward jump [target..jump], + extend all overlapping live ranges to cover the full loop body. */ + { + /* Collect label positions */ + typedef struct { const char *name; int pos; } LabelPos; + int lbl_cap = 32, lbl_n = 0; + LabelPos *lbls = sys_malloc(lbl_cap * sizeof(LabelPos)); + for (int i = 0; i < n; i++) { + cJSON *it = cJSON_GetArrayItem(instrs, i); + if (cJSON_IsString(it)) { + if (lbl_n >= lbl_cap) { + lbl_cap *= 2; + lbls = sys_realloc(lbls, lbl_cap * sizeof(LabelPos)); + } + lbls[lbl_n++] = (LabelPos){it->valuestring, i}; + } + } + /* Find backward jumps and extend live ranges */ + int changed = 1; + while (changed) { + changed = 0; + for (int i = 0; i < n; i++) { + cJSON *it = cJSON_GetArrayItem(instrs, i); + if (!cJSON_IsArray(it)) continue; + int sz = cJSON_GetArraySize(it); + if (sz < 3) continue; + const char *op = cJSON_GetArrayItem(it, 0)->valuestring; + const char *target = NULL; + if (!strcmp(op, "jump")) { + target = cJSON_GetArrayItem(it, 1)->valuestring; + } else if (!strcmp(op, "jump_true") || !strcmp(op, "jump_false") || + !strcmp(op, "jump_not_null")) { + target = cJSON_GetArrayItem(it, 2)->valuestring; + } + if (!target) continue; + /* Find label position */ + int tpos = -1; + for (int j = 0; j < lbl_n; j++) { + if (!strcmp(lbls[j].name, target)) { tpos = lbls[j].pos; break; } + } + if (tpos < 0 || tpos >= i) continue; /* forward jump or not found */ + /* Backward jump: extend registers that are live INTO the loop + (first_ref < loop start but used inside). Temporaries born + inside the loop body don't need extension — they are per-iteration. */ + for (int s = nr_args; s < nr_slots; s++) { + if (first_ref[s] < 0) continue; + if (first_ref[s] >= tpos) continue; /* born inside loop — skip */ + if (last_ref[s] < tpos) continue; /* dead before loop — skip */ + /* Register is live into the loop body — extend to loop end */ + if (last_ref[s] < i) { last_ref[s] = i; changed = 1; } + } + } + } + sys_free(lbls); + } + + /* Step 2: linear-scan register allocation */ + typedef struct { int slot, first, last; } SlotInfo; + int cnt = 0; + SlotInfo *sorted = sys_malloc(nr_slots * sizeof(SlotInfo)); + for (int s = nr_args; s < nr_slots; s++) + if (first_ref[s] >= 0) + sorted[cnt++] = (SlotInfo){s, first_ref[s], last_ref[s]}; + + /* Sort by first_ref, tie-break by original slot (keeps named vars first) */ + for (int i = 1; i < cnt; i++) { + SlotInfo key = sorted[i]; + int j = i - 1; + while (j >= 0 && (sorted[j].first > key.first || + (sorted[j].first == key.first && sorted[j].slot > key.slot))) { + sorted[j + 1] = sorted[j]; + j--; + } + sorted[j + 1] = key; + } + + int *remap = sys_malloc(nr_slots * sizeof(int)); + for (int i = 0; i < nr_slots; i++) remap[i] = i; + + /* Free-register pool (min-heap would be ideal but a flat scan is fine) */ + int *pool = sys_malloc(nr_slots * sizeof(int)); + int pool_n = 0; + int next_phys = nr_args; + + typedef struct { int phys, last; } ActiveAlloc; + ActiveAlloc *active = sys_malloc(cnt * sizeof(ActiveAlloc)); + int active_n = 0; + + for (int i = 0; i < cnt; i++) { + int first = sorted[i].first; + /* Expire intervals whose last_ref < first */ + for (int j = 0; j < active_n; ) { + if (active[j].last < first) { + pool[pool_n++] = active[j].phys; + active[j] = active[--active_n]; + } else { + j++; + } + } + /* Pick lowest available physical register */ + int phys; + if (pool_n > 0) { + int mi = 0; + for (int j = 1; j < pool_n; j++) + if (pool[j] < pool[mi]) mi = j; + phys = pool[mi]; + pool[mi] = pool[--pool_n]; + } else { + phys = next_phys++; + } + remap[sorted[i].slot] = phys; + active[active_n++] = (ActiveAlloc){phys, sorted[i].last}; + } + + /* Compute new nr_slots */ + int new_max = nr_args; + for (int s = 0; s < nr_slots; s++) + if (first_ref[s] >= 0 && remap[s] >= new_max) + new_max = remap[s] + 1; + + fprintf(stderr, " compress_regs: %d -> %d slots\n", nr_slots, new_max); + if (new_max > 255) + fprintf(stderr, " WARNING: %d live regs still exceeds 255\n", new_max); + + /* Verify: check no two registers with overlapping live ranges share phys */ + for (int a = nr_args; a < nr_slots; a++) { + if (first_ref[a] < 0) continue; + for (int b = a + 1; b < nr_slots; b++) { + if (first_ref[b] < 0) continue; + if (remap[a] != remap[b]) continue; + /* Same phys — ranges must NOT overlap */ + if (first_ref[a] <= last_ref[b] && first_ref[b] <= last_ref[a]) { + fprintf(stderr, " OVERLAP: slot %d [%d,%d] and slot %d [%d,%d] -> phys %d\n", + a, first_ref[a], last_ref[a], b, first_ref[b], last_ref[b], remap[a]); + } + } + } + + /* Step 3: apply remap to instructions */ + for (int i = 0; i < n; i++) { + cJSON *it = cJSON_GetArrayItem(instrs, i); + if (!cJSON_IsArray(it)) continue; + cJSON *regs[MAX_REG_ITEMS]; + int rc = mcode_reg_items(it, regs); + for (int j = 0; j < rc; j++) { + int old = (int)regs[j]->valuedouble; + if (old >= 0 && old < nr_slots) { + cJSON_SetNumberValue(regs[j], remap[old]); + } + } + } + + /* Update nr_slots in the JSON */ + cJSON_SetNumberValue(nr_slots_j, new_max); + + sys_free(first_ref); sys_free(last_ref); + sys_free(sorted); + sys_free(pool); sys_free(active); + return remap; /* caller must free */ +} + /* Lower one function's mcode instructions to MachInstr32 */ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) { McodeLowerState s = {0}; @@ -4242,13 +2585,25 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) { /* Property access */ else if (strcmp(op, "load_field") == 0) { int dest = A1, obj = A2; - const char *key = cJSON_GetArrayItem(it, 3)->valuestring; - EM(MACH_ABC(MACH_LOAD_FIELD, dest, obj, ml_cpool_str(&s, key))); + cJSON *key_item = cJSON_GetArrayItem(it, 3); + if (cJSON_IsString(key_item)) { + EM(MACH_ABC(MACH_LOAD_FIELD, dest, obj, ml_cpool_str(&s, key_item->valuestring))); + } else { + /* key is a register — fall back to dynamic access */ + int key_reg = (int)key_item->valuedouble; + EM(MACH_ABC(MACH_LOAD_DYNAMIC, dest, obj, key_reg)); + } } else if (strcmp(op, "store_field") == 0) { int obj = A1, val = A2; - const char *key = cJSON_GetArrayItem(it, 3)->valuestring; - EM(MACH_ABC(MACH_STORE_FIELD, obj, ml_cpool_str(&s, key), val)); + cJSON *key_item = cJSON_GetArrayItem(it, 3); + if (cJSON_IsString(key_item)) { + EM(MACH_ABC(MACH_STORE_FIELD, obj, ml_cpool_str(&s, key_item->valuestring), val)); + } else { + /* key is a register — fall back to dynamic access */ + int key_reg = (int)key_item->valuedouble; + EM(MACH_ABC(MACH_STORE_DYNAMIC, obj, key_reg, val)); + } } else if (strcmp(op, "load_index") == 0) { ABC3(MACH_LOAD_INDEX); } else if (strcmp(op, "store_index") == 0) { @@ -4486,6 +2841,23 @@ static void mcode_assign_children(MachCode *mc, MachCode **compiled, int total) sys_free(refs); } +void verify_getup(MachCode *mc, const char *path) { + for (uint32_t i = 0; i < mc->instr_count; i++) { + MachInstr32 instr = mc->instructions[i]; + if (MACH_GET_OP(instr) == MACH_GETUP) { + int depth = MACH_GET_B(instr); + if (depth > 2) + fprintf(stderr, "VERIFY: bad GETUP at %s[%d]: depth=%d slot=%d dest=%d instr=0x%08x nr_slots=%d\n", + path, i, depth, MACH_GET_C(instr), MACH_GET_A(instr), instr, mc->nr_slots); + } + } + for (uint32_t i = 0; i < mc->func_count; i++) { + char child[256]; + snprintf(child, sizeof(child), "%s.func[%d]", path, i); + verify_getup(mc->functions[i], child); + } +} + /* Compile mcode JSON IR to MachCode binary. mcode_json has: functions[], main{}, filename, name */ MachCode *mach_compile_mcode(cJSON *mcode_json) { @@ -4494,6 +2866,139 @@ MachCode *mach_compile_mcode(cJSON *mcode_json) { cJSON *funcs_arr = cJSON_GetObjectItemCaseSensitive(mcode_json, "functions"); int func_count = funcs_arr ? cJSON_GetArraySize(funcs_arr) : 0; + cJSON *main_obj = cJSON_GetObjectItemCaseSensitive(mcode_json, "main"); + + /* Build parent_of[]: for each function, which function index is its parent. + parent_of[i] = parent index, or func_count for main, or -1 if unknown. + Scan each function (and main) for "function" instructions. */ + int *parent_of = sys_malloc(func_count * sizeof(int)); + for (int i = 0; i < func_count; i++) parent_of[i] = -1; + + /* Scan main's instructions */ + { + cJSON *main_instrs = cJSON_GetObjectItemCaseSensitive(main_obj, "instructions"); + int mn = main_instrs ? cJSON_GetArraySize(main_instrs) : 0; + for (int i = 0; i < mn; i++) { + cJSON *it = cJSON_GetArrayItem(main_instrs, i); + if (!cJSON_IsArray(it) || cJSON_GetArraySize(it) < 3) continue; + const char *op = cJSON_GetArrayItem(it, 0)->valuestring; + if (!strcmp(op, "function")) { + int child_idx = (int)cJSON_GetArrayItem(it, 2)->valuedouble; + if (child_idx >= 0 && child_idx < func_count) + parent_of[child_idx] = func_count; /* main */ + } + } + } + /* Scan each function's instructions */ + for (int fi = 0; fi < func_count; fi++) { + cJSON *fobj = cJSON_GetArrayItem(funcs_arr, fi); + cJSON *finstrs = cJSON_GetObjectItemCaseSensitive(fobj, "instructions"); + int fn = finstrs ? cJSON_GetArraySize(finstrs) : 0; + for (int i = 0; i < fn; i++) { + cJSON *it = cJSON_GetArrayItem(finstrs, i); + if (!cJSON_IsArray(it) || cJSON_GetArraySize(it) < 3) continue; + const char *op = cJSON_GetArrayItem(it, 0)->valuestring; + if (!strcmp(op, "function")) { + int child_idx = (int)cJSON_GetArrayItem(it, 2)->valuedouble; + if (child_idx >= 0 && child_idx < func_count) + parent_of[child_idx] = fi; + } + } + } + + /* Build per-function capture sets: for each function F, which of its slots + are captured by descendant functions via get/put. Captured slots must + have extended live ranges during register compression. */ + int **cap_slots = sys_malloc((func_count + 1) * sizeof(int *)); + int *cap_counts = sys_malloc((func_count + 1) * sizeof(int)); + memset(cap_slots, 0, (func_count + 1) * sizeof(int *)); + memset(cap_counts, 0, (func_count + 1) * sizeof(int)); + + for (int fi = 0; fi < func_count; fi++) { + cJSON *fobj = cJSON_GetArrayItem(funcs_arr, fi); + cJSON *finstrs = cJSON_GetObjectItemCaseSensitive(fobj, "instructions"); + int fn = finstrs ? cJSON_GetArraySize(finstrs) : 0; + for (int i = 0; i < fn; i++) { + cJSON *it = cJSON_GetArrayItem(finstrs, i); + if (!cJSON_IsArray(it) || cJSON_GetArraySize(it) < 4) continue; + const char *op = cJSON_GetArrayItem(it, 0)->valuestring; + if (strcmp(op, "get") && strcmp(op, "put")) continue; + int slot = (int)cJSON_GetArrayItem(it, 2)->valuedouble; + int level = (int)cJSON_GetArrayItem(it, 3)->valuedouble; + /* Walk up parent chain to find the ancestor whose slot is referenced */ + int ancestor = fi; + for (int l = 0; l < level && ancestor >= 0; l++) + ancestor = parent_of[ancestor]; + if (ancestor < 0) continue; + /* Add slot to ancestor's capture list (deduplicate) */ + int found = 0; + for (int k = 0; k < cap_counts[ancestor]; k++) + if (cap_slots[ancestor][k] == slot) { found = 1; break; } + if (!found) { + cap_slots[ancestor] = sys_realloc(cap_slots[ancestor], + (cap_counts[ancestor] + 1) * sizeof(int)); + cap_slots[ancestor][cap_counts[ancestor]++] = slot; + } + } + } + + /* Compress registers for functions that exceed 8-bit slot limits. + Save remap tables so we can fix get/put parent_slot references. */ + int **remaps = sys_malloc((func_count + 1) * sizeof(int *)); + int *remap_sizes = sys_malloc((func_count + 1) * sizeof(int)); + memset(remaps, 0, (func_count + 1) * sizeof(int *)); + + for (int i = 0; i < func_count; i++) + remaps[i] = mcode_compress_regs(cJSON_GetArrayItem(funcs_arr, i), + &remap_sizes[i], cap_slots[i], cap_counts[i]); + /* main is stored at index func_count in our arrays */ + remaps[func_count] = mcode_compress_regs(main_obj, + &remap_sizes[func_count], cap_slots[func_count], cap_counts[func_count]); + + /* Free capture lists */ + for (int i = 0; i <= func_count; i++) + if (cap_slots[i]) sys_free(cap_slots[i]); + sys_free(cap_slots); + sys_free(cap_counts); + + /* Fix up get/put parent_slot references using ancestor remap tables */ + for (int fi = 0; fi < func_count; fi++) { + cJSON *fobj = cJSON_GetArrayItem(funcs_arr, fi); + cJSON *finstrs = cJSON_GetObjectItemCaseSensitive(fobj, "instructions"); + int fn = finstrs ? cJSON_GetArraySize(finstrs) : 0; + for (int i = 0; i < fn; i++) { + cJSON *it = cJSON_GetArrayItem(finstrs, i); + if (!cJSON_IsArray(it) || cJSON_GetArraySize(it) < 4) continue; + const char *op = cJSON_GetArrayItem(it, 0)->valuestring; + if (strcmp(op, "get") && strcmp(op, "put")) continue; + int level = (int)cJSON_GetArrayItem(it, 3)->valuedouble; + /* Walk up parent chain 'level' times to find ancestor */ + int ancestor = fi; + for (int l = 0; l < level && ancestor >= 0; l++) { + ancestor = parent_of[ancestor]; + } + if (ancestor < 0) continue; /* unknown parent — leave as is */ + int *anc_remap = remaps[ancestor]; + if (!anc_remap) continue; /* ancestor wasn't compressed */ + cJSON *slot_item = cJSON_GetArrayItem(it, 2); + int old_slot = (int)slot_item->valuedouble; + if (old_slot >= 0 && old_slot < remap_sizes[ancestor]) { + int new_slot = anc_remap[old_slot]; + if (old_slot != new_slot) + fprintf(stderr, " FIXUP: func[%d] %s parent_slot %d -> %d (ancestor=%d level=%d)\n", + fi, op, old_slot, new_slot, ancestor, level); + cJSON_SetNumberValue(slot_item, new_slot); + } + } + } + + /* Free remap tables */ + for (int i = 0; i <= func_count; i++) + if (remaps[i]) sys_free(remaps[i]); + sys_free(remaps); + sys_free(remap_sizes); + sys_free(parent_of); + /* Compile all flat functions */ MachCode **compiled = NULL; if (func_count > 0) { @@ -4504,14 +3009,28 @@ MachCode *mach_compile_mcode(cJSON *mcode_json) { } /* Compile main */ - cJSON *main_obj = cJSON_GetObjectItemCaseSensitive(mcode_json, "main"); MachCode *main_code = mcode_lower_func(main_obj, filename); + /* Verify each flat function before tree assembly */ + fprintf(stderr, "COMPILE %s: %d flat functions\n", filename ? filename : "?", func_count); + for (int i = 0; i < func_count; i++) { + char name_buf[128]; + snprintf(name_buf, sizeof(name_buf), "%s:FLAT[%d]", filename ? filename : "?", i); + verify_getup(compiled[i], name_buf); + } + /* Assign nested functions to each compiled unit */ for (int i = 0; i < func_count; i++) mcode_assign_children(compiled[i], compiled, func_count); mcode_assign_children(main_code, compiled, func_count); + /* Verify after tree assembly */ + { + char post_name[128]; + snprintf(post_name, sizeof(post_name), "%s:POST", filename ? filename : "?"); + verify_getup(main_code, post_name); + } + sys_free(compiled); return main_code; } @@ -4785,70 +3304,33 @@ static void dump_register_code(JSContext *ctx, JSCodeRegister *code, int indent) } } -/* 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; - } - - JSCodeRegister *code = JS_LoadMachCode(ctx, mc, env); - printf("=== MACH Bytecode ===\n"); - dump_register_code(ctx, code, 0); - printf("=== End MACH Bytecode ===\n"); -} - -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"); - } - - /* Protect env from GC — JS_LoadMachCode allocates on GC heap */ - JSGCRef env_ref; - JS_PushGCRef(ctx, &env_ref); - env_ref.val = env; - - JSCodeRegister *code = JS_LoadMachCode(ctx, mc, env_ref.val); - JSValue result = JS_CallRegisterVM(ctx, code, ctx->global_obj, 0, NULL, env_ref.val, JS_NULL); - JS_PopGCRef(ctx, &env_ref); - return result; -} - JSValue JS_RunMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue env) { MachCode *mc = JS_DeserializeMachCode(data, size); if (!mc) return JS_ThrowSyntaxError(ctx, "failed to deserialize MACH bytecode"); + fprintf(stderr, "DEBUG RunMachBin: mc=%p instr_count=%d cpool_count=%d func_count=%d nr_slots=%d\n", + (void*)mc, mc->instr_count, mc->cpool_count, mc->func_count, mc->nr_slots); + verify_getup(mc, "DESER_BOOT"); + if (mc->instr_count > 0) { + fprintf(stderr, "DEBUG first 5 instrs:"); + for (uint32_t i = 0; i < 5 && i < mc->instr_count; i++) + fprintf(stderr, " 0x%08x", mc->instructions[i]); + fprintf(stderr, "\n"); + } + JSGCRef env_ref; JS_PushGCRef(ctx, &env_ref); env_ref.val = env; JSCodeRegister *code = JS_LoadMachCode(ctx, mc, env_ref.val); JS_FreeMachCode(mc); + + fprintf(stderr, "DEBUG LoadMachCode: code=%p instr_count=%d cpool_count=%d func_count=%d\n", + (void*)code, code->instr_count, code->cpool_count, code->func_count); + JSValue result = JS_CallRegisterVM(ctx, code, ctx->global_obj, 0, NULL, env_ref.val, JS_NULL); JS_PopGCRef(ctx, &env_ref); 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/mcode.c b/source/mcode.c deleted file mode 100644 index 96aabbb5..00000000 --- a/source/mcode.c +++ /dev/null @@ -1,2038 +0,0 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2025 Fabrice Bellard - * Copyright (c) 2017-2025 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "quickjs-internal.h" - - -/* ============================================================ - MCODE JSON Interpreter - ============================================================ */ - -/* Parse a single MCODE function from cJSON into a JSMCode struct */ -/* Parse a single function (no recursive function parsing) */ -JSMCode *jsmcode_parse_one(cJSON *func_def) { - JSMCode *code = js_mallocz_rt(sizeof(JSMCode)); - if (!code) return NULL; - - code->nr_args = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItemCaseSensitive(func_def, "nr_args")); - code->nr_slots = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItemCaseSensitive(func_def, "nr_slots")); - - /* Build instruction array from cJSON linked list */ - cJSON *instrs_arr = cJSON_GetObjectItemCaseSensitive(func_def, "instructions"); - int raw_count = cJSON_GetArraySize(instrs_arr); - code->instrs = js_mallocz_rt(raw_count * sizeof(cJSON *)); - code->instr_count = 0; - - /* First pass: count labels and build instruction array */ - uint32_t label_cap = 32; - code->labels = js_mallocz_rt(label_cap * sizeof(*code->labels)); - code->label_count = 0; - - cJSON *item; - uint32_t idx = 0; - cJSON_ArrayForEach(item, instrs_arr) { - if (cJSON_IsString(item)) { - /* Label marker — record position, don't add to instruction array */ - if (code->label_count >= label_cap) { - label_cap *= 2; - code->labels = js_realloc_rt(code->labels, label_cap * sizeof(*code->labels)); - } - code->labels[code->label_count].name = item->valuestring; - code->labels[code->label_count].index = idx; - code->label_count++; - } else { - /* Instruction (array) */ - code->instrs[idx++] = item; - } - } - code->instr_count = idx; - - /* Extract line table from trailing numbers in each instruction array */ - if (idx > 0) { - code->line_table = js_mallocz_rt(idx * sizeof(MachLineEntry)); - for (uint32_t i = 0; i < idx; i++) { - cJSON *instr = code->instrs[i]; - int n = cJSON_GetArraySize(instr); - if (n >= 2) { - cJSON *line_item = cJSON_GetArrayItem(instr, n - 2); - cJSON *col_item = cJSON_GetArrayItem(instr, n - 1); - if (cJSON_IsNumber(line_item) && cJSON_IsNumber(col_item)) { - code->line_table[i].line = (uint16_t)line_item->valuedouble; - code->line_table[i].col = (uint16_t)col_item->valuedouble; - } - } - } - } - - /* Extract name and filename from function definition */ - code->name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(func_def, "name")); - code->filename = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(func_def, "filename")); - - /* Extract disruption_pc */ - cJSON *dpc = cJSON_GetObjectItemCaseSensitive(func_def, "disruption_pc"); - code->disruption_pc = dpc ? (uint16_t)cJSON_GetNumberValue(dpc) : 0; - - return code; -} - -/* Parse MCODE: main + all functions from the global functions array. - All JSMCode structs share the same functions[] pointer. */ -JSMCode *jsmcode_parse(cJSON *func_def, cJSON *all_functions) { - /* Parse the global functions array first (flat, non-recursive) */ - uint32_t func_count = all_functions ? cJSON_GetArraySize(all_functions) : 0; - JSMCode **parsed_funcs = NULL; - if (func_count > 0) { - parsed_funcs = js_mallocz_rt(func_count * sizeof(JSMCode *)); - for (uint32_t i = 0; i < func_count; i++) { - cJSON *fn = cJSON_GetArrayItem(all_functions, i); - parsed_funcs[i] = jsmcode_parse_one(fn); - /* Each function shares the same functions array */ - parsed_funcs[i]->func_count = func_count; - parsed_funcs[i]->functions = parsed_funcs; - } - } - - /* Parse the main function */ - JSMCode *code = jsmcode_parse_one(func_def); - code->func_count = func_count; - code->functions = parsed_funcs; - - return code; -} - -/* Free a top-level JSMCode and all its shared functions. - Only call this on the main code returned by jsmcode_parse. */ -void jsmcode_free(JSMCode *code) { - if (!code) return; - /* Free all parsed functions (they share the same functions array) */ - if (code->functions) { - for (uint32_t i = 0; i < code->func_count; i++) { - if (code->functions[i]) { - /* Don't free functions[i]->functions — it's the shared pointer */ - if (code->functions[i]->instrs) js_free_rt(code->functions[i]->instrs); - if (code->functions[i]->labels) js_free_rt(code->functions[i]->labels); - if (code->functions[i]->line_table) js_free_rt(code->functions[i]->line_table); - js_free_rt(code->functions[i]); - } - } - js_free_rt(code->functions); - } - if (code->instrs) js_free_rt(code->instrs); - if (code->labels) js_free_rt(code->labels); - if (code->line_table) js_free_rt(code->line_table); - if (code->json_root) cJSON_Delete(code->json_root); - js_free_rt(code); -} - -/* Resolve label name → instruction index */ -static uint32_t mcode_resolve_label(JSMCode *code, const char *name) { - for (uint32_t i = 0; i < code->label_count; i++) { - if (strcmp(code->labels[i].name, name) == 0) - return code->labels[i].index; - } - return code->instr_count; /* past end = implicit return */ -} - -/* Create a MCODE function object. - outer_frame must be set by the caller AFTER refreshing from GC root, - since js_mallocz can trigger GC which invalidates stale JSValues. */ -JSValue js_new_mcode_function(JSContext *ctx, JSMCode *code) { - JSFunction *fn = js_mallocz(ctx, sizeof(JSFunction)); - if (!fn) return JS_EXCEPTION; - - fn->header = objhdr_make(0, OBJ_FUNCTION, 0, 0, 0, 0); - fn->kind = JS_FUNC_KIND_MCODE; - fn->length = code->nr_args; - fn->name = JS_NULL; - fn->u.mcode.code = code; - fn->u.mcode.outer_frame = JS_NULL; - fn->u.mcode.env_record = JS_NULL; - - return JS_MKPTR(fn); -} - -/* Main MCODE interpreter — executes pre-parsed JSMCode */ -JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, - int argc, JSValue *argv, JSValue outer_frame) { - /* Protect argv, this_obj, outer_frame from GC by pushing onto value_stack. - alloc_frame_register and js_new_mcode_function can trigger GC. */ - int vs_save = ctx->value_stack_top; - int nargs_copy = (argc < code->nr_args) ? argc : code->nr_args; - ctx->value_stack[vs_save] = this_obj; - ctx->value_stack[vs_save + 1] = outer_frame; - for (int i = 0; i < nargs_copy; i++) - ctx->value_stack[vs_save + 2 + i] = argv[i]; - ctx->value_stack_top = vs_save + 2 + nargs_copy; - - JSFrameRegister *frame = alloc_frame_register(ctx, code->nr_slots); - if (!frame) { ctx->value_stack_top = vs_save; return JS_EXCEPTION; } - - /* Protect frame from GC */ - JSGCRef frame_ref; - JS_AddGCRef(ctx, &frame_ref); - frame_ref.val = JS_MKPTR(frame); - - /* Create a function object for the main frame so return can find the code */ - JSValue main_func = js_new_mcode_function(ctx, code); - if (JS_IsException(main_func)) { - ctx->value_stack_top = vs_save; - JS_DeleteGCRef(ctx, &frame_ref); - return JS_ThrowInternalError(ctx, "failed to allocate main function for MCODE"); - } - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - /* Set outer_frame AFTER allocation so it uses the post-GC value */ - JSFunction *main_fn = JS_VALUE_GET_FUNCTION(main_func); - main_fn->u.mcode.outer_frame = ctx->value_stack[vs_save + 1]; - frame->function = main_func; - - /* Setup initial frame from GC-safe value_stack */ - frame->slots[0] = ctx->value_stack[vs_save]; /* slot 0 is this */ - for (int i = 0; i < nargs_copy; i++) { - frame->slots[1 + i] = ctx->value_stack[vs_save + 2 + i]; - } - ctx->value_stack_top = vs_save; - - uint32_t pc = 0; - JSValue result = JS_NULL; - - for (;;) { - /* Check for interrupt */ - if (reg_vm_check_interrupt(ctx)) { - result = JS_ThrowInternalError(ctx, "interrupted"); - goto done; - } - - if (pc >= code->instr_count) { - /* Implicit return null */ - result = JS_NULL; - if (JS_IsNull(frame->caller)) goto done; - - /* Pop frame — read return info from CALLER */ - JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller); - int ret_info = JS_VALUE_GET_INT(caller->address); - frame->caller = JS_NULL; - frame = caller; - frame_ref.val = JS_MKPTR(frame); - - JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function); - code = fn->u.mcode.code; - pc = ret_info >> 16; - frame->slots[ret_info & 0xFFFF] = result; - continue; - } - - cJSON *instr = code->instrs[pc++]; - cJSON *op_item = cJSON_GetArrayItem(instr, 0); - const char *op = op_item->valuestring; - - /* Operand extraction helpers — items 1,2,3 */ - cJSON *a1 = cJSON_GetArrayItem(instr, 1); - cJSON *a2 = cJSON_GetArrayItem(instr, 2); - cJSON *a3 = cJSON_GetArrayItem(instr, 3); - - /* ---- Constants ---- */ - if (strcmp(op, "access") == 0) { - int dest = (int)a1->valuedouble; - if (cJSON_IsObject(a2)) { - /* Intrinsic: {"kind":"name","name":"...","make":"intrinsic"} */ - const char *iname = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(a2, "name")); - if (iname) { - JSValue key = JS_NewString(ctx, iname); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - /* Try env (outer_frame) first, then global */ - JSFunction *cur_fn = JS_VALUE_GET_FUNCTION(frame->function); - JSValue env = (cur_fn && cur_fn->kind == JS_FUNC_KIND_MCODE) - ? cur_fn->u.mcode.outer_frame : JS_NULL; - JSValue val = JS_NULL; - if (!JS_IsNull(env)) { - val = JS_GetProperty(ctx, env, key); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - } - if (JS_IsNull(val)) { - key = JS_NewString(ctx, iname); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - val = JS_GetProperty(ctx, ctx->global_obj, key); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - } - if (JS_IsNull(val)) { - key = JS_NewString(ctx, iname); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - int has = JS_HasProperty(ctx, ctx->global_obj, key); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (has <= 0) { - JS_ThrowReferenceError(ctx, "'%s' is not defined", iname); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - goto disrupt; - } - } - frame->slots[dest] = val; - } else { - frame->slots[dest] = JS_NULL; - } - } else if (cJSON_IsString(a2)) { - JSValue str = JS_NewString(ctx, a2->valuestring); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = str; - } else { - frame->slots[dest] = JS_NewFloat64(ctx, a2->valuedouble); - } - } - else if (strcmp(op, "int") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewFloat64(ctx, a2->valuedouble); - } - else if (strcmp(op, "null") == 0) { - frame->slots[(int)a1->valuedouble] = JS_NULL; - } - else if (strcmp(op, "true") == 0) { - frame->slots[(int)a1->valuedouble] = JS_TRUE; - } - else if (strcmp(op, "false") == 0) { - frame->slots[(int)a1->valuedouble] = JS_FALSE; - } - - /* ---- Movement ---- */ - else if (strcmp(op, "move") == 0) { - frame->slots[(int)a1->valuedouble] = frame->slots[(int)a2->valuedouble]; - } - - /* ---- Arithmetic (inline) ---- */ - else if (strcmp(op, "add") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - int64_t r = (int64_t)JS_VALUE_GET_INT(left) + (int64_t)JS_VALUE_GET_INT(right); - frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) - ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - frame->slots[dest] = JS_NewFloat64(ctx, a + b); - } else if (JS_IsText(left) && JS_IsText(right)) { - JSValue res = JS_ConcatString(ctx, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; - } else { goto disrupt; } - } - else if (strcmp(op, "subtract") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - int64_t r = (int64_t)JS_VALUE_GET_INT(left) - (int64_t)JS_VALUE_GET_INT(right); - frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) - ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - frame->slots[dest] = JS_NewFloat64(ctx, a - b); - } else { goto disrupt; } - } - else if (strcmp(op, "multiply") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - int64_t r = (int64_t)JS_VALUE_GET_INT(left) * (int64_t)JS_VALUE_GET_INT(right); - frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) - ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - frame->slots[dest] = JS_NewFloat64(ctx, a * b); - } else { goto disrupt; } - } - else if (strcmp(op, "divide") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - int32_t ia = JS_VALUE_GET_INT(left), ib = JS_VALUE_GET_INT(right); - if (ib == 0) { frame->slots[dest] = JS_NULL; } - else if (ia % ib == 0) frame->slots[dest] = JS_NewInt32(ctx, ia / ib); - else frame->slots[dest] = JS_NewFloat64(ctx, (double)ia / (double)ib); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - if (b == 0.0) { frame->slots[dest] = JS_NULL; } - else frame->slots[dest] = JS_NewFloat64(ctx, a / b); - } else { goto disrupt; } - } - else if (strcmp(op, "integer_divide") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - int32_t ib = JS_VALUE_GET_INT(right); - if (ib == 0) { frame->slots[dest] = JS_NULL; } - else frame->slots[dest] = JS_NewInt32(ctx, JS_VALUE_GET_INT(left) / ib); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - if (b == 0.0) { frame->slots[dest] = JS_NULL; } - else frame->slots[dest] = JS_NewInt32(ctx, (int32_t)(a / b)); - } else { goto disrupt; } - } - else if (strcmp(op, "modulo") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - int32_t ib = JS_VALUE_GET_INT(right); - if (ib == 0) { frame->slots[dest] = JS_NULL; } - else frame->slots[dest] = JS_NewInt32(ctx, JS_VALUE_GET_INT(left) % ib); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - if (b == 0.0) { frame->slots[dest] = JS_NULL; } - else frame->slots[dest] = JS_NewFloat64(ctx, fmod(a, b)); - } else { goto disrupt; } - } - else if (strcmp(op, "remainder") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - int32_t ib = JS_VALUE_GET_INT(right); - if (ib == 0) { frame->slots[dest] = JS_NULL; } - else frame->slots[dest] = JS_NewInt32(ctx, JS_VALUE_GET_INT(left) % ib); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - if (b == 0.0) { frame->slots[dest] = JS_NULL; } - else frame->slots[dest] = JS_NewFloat64(ctx, remainder(a, b)); - } else { goto disrupt; } - } - else if (strcmp(op, "pow") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - frame->slots[dest] = JS_NewFloat64(ctx, pow(a, b)); - } else { goto disrupt; } - } - else if (strcmp(op, "max") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - int32_t ia = JS_VALUE_GET_INT(left), ib = JS_VALUE_GET_INT(right); - frame->slots[dest] = JS_NewInt32(ctx, ia > ib ? ia : ib); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - frame->slots[dest] = JS_NewFloat64(ctx, a > b ? a : b); - } else { goto disrupt; } - } - else if (strcmp(op, "min") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - int32_t ia = JS_VALUE_GET_INT(left), ib = JS_VALUE_GET_INT(right); - frame->slots[dest] = JS_NewInt32(ctx, ia < ib ? ia : ib); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - frame->slots[dest] = JS_NewFloat64(ctx, a < b ? a : b); - } else { goto disrupt; } - } - else if (strcmp(op, "neg") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (!JS_IsNumber(v)) { goto disrupt; } - if (JS_IsInt(v)) { - int32_t i = JS_VALUE_GET_INT(v); - if (i == INT32_MIN) frame->slots[dest] = JS_NewFloat64(ctx, -(double)i); - else frame->slots[dest] = JS_NewInt32(ctx, -i); - } else { - double d; - JS_ToFloat64(ctx, &d, v); - frame->slots[dest] = JS_NewFloat64(ctx, -d); - } - } - /* ---- Compiler-internal type guards ---- */ - else if (strcmp(op, "is_int") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_IsInt(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "is_num") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_IsNumber(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "is_text") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_IsText(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "is_null") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_IsNull(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "is_bool") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = (JS_VALUE_GET_TAG(frame->slots[(int)a2->valuedouble]) == JS_TAG_BOOL) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "is_identical") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = (frame->slots[(int)a2->valuedouble] == frame->slots[(int)a3->valuedouble]) ? JS_TRUE : JS_FALSE; - } - - /* ---- Specialized arithmetic (int) ---- */ - else if (strcmp(op, "add_int") == 0) { - int dest = (int)a1->valuedouble; - int64_t r = (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) + (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); - frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); - } - else if (strcmp(op, "sub_int") == 0) { - int dest = (int)a1->valuedouble; - int64_t r = (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) - (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); - frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); - } - else if (strcmp(op, "mul_int") == 0) { - int dest = (int)a1->valuedouble; - int64_t r = (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) * (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); - frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); - } - else if (strcmp(op, "div_int") == 0) { - int dest = (int)a1->valuedouble; - int32_t ia = JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]); - int32_t ib = JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); - if (ib == 0) frame->slots[dest] = JS_NULL; - else if (ia % ib == 0) frame->slots[dest] = JS_NewInt32(ctx, ia / ib); - else frame->slots[dest] = JS_NewFloat64(ctx, (double)ia / (double)ib); - } - else if (strcmp(op, "mod_int") == 0) { - int dest = (int)a1->valuedouble; - int32_t ib = JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); - if (ib == 0) frame->slots[dest] = JS_NULL; - else frame->slots[dest] = JS_NewInt32(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) % ib); - } - else if (strcmp(op, "neg_int") == 0) { - int dest = (int)a1->valuedouble; - int32_t i = JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]); - if (i == INT32_MIN) frame->slots[dest] = JS_NewFloat64(ctx, -(double)i); - else frame->slots[dest] = JS_NewInt32(ctx, -i); - } - - /* ---- Specialized arithmetic (float) ---- */ - else if (strcmp(op, "add_float") == 0) { - int dest = (int)a1->valuedouble; - double a, b; - JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); - JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); - frame->slots[dest] = JS_NewFloat64(ctx, a + b); - } - else if (strcmp(op, "sub_float") == 0) { - int dest = (int)a1->valuedouble; - double a, b; - JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); - JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); - frame->slots[dest] = JS_NewFloat64(ctx, a - b); - } - else if (strcmp(op, "mul_float") == 0) { - int dest = (int)a1->valuedouble; - double a, b; - JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); - JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); - frame->slots[dest] = JS_NewFloat64(ctx, a * b); - } - else if (strcmp(op, "div_float") == 0) { - int dest = (int)a1->valuedouble; - double a, b; - JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); - JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); - if (b == 0.0) frame->slots[dest] = JS_NULL; - else frame->slots[dest] = JS_NewFloat64(ctx, a / b); - } - else if (strcmp(op, "mod_float") == 0) { - int dest = (int)a1->valuedouble; - double a, b; - JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); - JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); - if (b == 0.0) frame->slots[dest] = JS_NULL; - else frame->slots[dest] = JS_NewFloat64(ctx, fmod(a, b)); - } - else if (strcmp(op, "neg_float") == 0) { - int dest = (int)a1->valuedouble; - double d; - JS_ToFloat64(ctx, &d, frame->slots[(int)a2->valuedouble]); - frame->slots[dest] = JS_NewFloat64(ctx, -d); - } - - /* ---- Specialized comparisons (int) ---- */ - else if (strcmp(op, "eq_int") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) == JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); - } - else if (strcmp(op, "ne_int") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) != JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); - } - else if (strcmp(op, "lt_int") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) < JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); - } - else if (strcmp(op, "le_int") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) <= JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); - } - else if (strcmp(op, "gt_int") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) > JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); - } - else if (strcmp(op, "ge_int") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) >= JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); - } - - /* ---- Specialized comparisons (float) ---- */ - else if (strcmp(op, "eq_float") == 0) { - int dest = (int)a1->valuedouble; - double a, b; - JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); - JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); - frame->slots[dest] = JS_NewBool(ctx, a == b); - } - else if (strcmp(op, "ne_float") == 0) { - int dest = (int)a1->valuedouble; - double a, b; - JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); - JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); - frame->slots[dest] = JS_NewBool(ctx, a != b); - } - else if (strcmp(op, "lt_float") == 0) { - int dest = (int)a1->valuedouble; - double a, b; - JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); - JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); - frame->slots[dest] = JS_NewBool(ctx, a < b); - } - else if (strcmp(op, "le_float") == 0) { - int dest = (int)a1->valuedouble; - double a, b; - JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); - JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); - frame->slots[dest] = JS_NewBool(ctx, a <= b); - } - else if (strcmp(op, "gt_float") == 0) { - int dest = (int)a1->valuedouble; - double a, b; - JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); - JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); - frame->slots[dest] = JS_NewBool(ctx, a > b); - } - else if (strcmp(op, "ge_float") == 0) { - int dest = (int)a1->valuedouble; - double a, b; - JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); - JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); - frame->slots[dest] = JS_NewBool(ctx, a >= b); - } - - /* ---- Specialized comparisons (text) ---- */ - else if (strcmp(op, "eq_text") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], TRUE) == 0); - } - else if (strcmp(op, "ne_text") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], TRUE) != 0); - } - else if (strcmp(op, "lt_text") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], FALSE) < 0); - } - else if (strcmp(op, "le_text") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], FALSE) <= 0); - } - else if (strcmp(op, "gt_text") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], FALSE) > 0); - } - else if (strcmp(op, "ge_text") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], FALSE) >= 0); - } - - /* ---- Specialized comparisons (bool) ---- */ - else if (strcmp(op, "eq_bool") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = (frame->slots[(int)a2->valuedouble] == frame->slots[(int)a3->valuedouble]) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "ne_bool") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = (frame->slots[(int)a2->valuedouble] != frame->slots[(int)a3->valuedouble]) ? JS_TRUE : JS_FALSE; - } - - else if (strcmp(op, "abs") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (!JS_IsNumber(v)) { goto disrupt; } - if (JS_IsInt(v)) { - int32_t i = JS_VALUE_GET_INT(v); - if (i == INT32_MIN) frame->slots[dest] = JS_NewFloat64(ctx, (double)INT32_MAX + 1.0); - else frame->slots[dest] = JS_NewInt32(ctx, i < 0 ? -i : i); - } else { - double d; - JS_ToFloat64(ctx, &d, v); - frame->slots[dest] = JS_NewFloat64(ctx, fabs(d)); - } - } - else if (strcmp(op, "sign") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (!JS_IsNumber(v)) { goto disrupt; } - if (JS_IsInt(v)) { - int32_t i = JS_VALUE_GET_INT(v); - frame->slots[dest] = JS_NewInt32(ctx, (i > 0) - (i < 0)); - } else { - double d; - JS_ToFloat64(ctx, &d, v); - frame->slots[dest] = JS_NewInt32(ctx, (d > 0) - (d < 0)); - } - } - else if (strcmp(op, "fraction") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (!JS_IsNumber(v)) { goto disrupt; } - if (JS_IsInt(v)) { - frame->slots[dest] = JS_NewInt32(ctx, 0); - } else { - double d; - JS_ToFloat64(ctx, &d, v); - frame->slots[dest] = JS_NewFloat64(ctx, d - trunc(d)); - } - } - else if (strcmp(op, "integer") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (!JS_IsNumber(v)) { goto disrupt; } - if (JS_IsInt(v)) { - frame->slots[dest] = v; - } else { - double d; - JS_ToFloat64(ctx, &d, v); - frame->slots[dest] = JS_NewFloat64(ctx, trunc(d)); - } - } - else if (strcmp(op, "ceiling") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - JSValue p = frame->slots[(int)a3->valuedouble]; - if (!JS_IsNumber(v) || !JS_IsNumber(p)) { goto disrupt; } - double d, place; - JS_ToFloat64(ctx, &d, v); - JS_ToFloat64(ctx, &place, p); - frame->slots[dest] = JS_NewFloat64(ctx, ceil(d * place) / place); - } - else if (strcmp(op, "floor") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - JSValue p = frame->slots[(int)a3->valuedouble]; - if (!JS_IsNumber(v) || !JS_IsNumber(p)) { goto disrupt; } - double d, place; - JS_ToFloat64(ctx, &d, v); - JS_ToFloat64(ctx, &place, p); - frame->slots[dest] = JS_NewFloat64(ctx, floor(d * place) / place); - } - else if (strcmp(op, "round") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - JSValue p = frame->slots[(int)a3->valuedouble]; - if (!JS_IsNumber(v) || !JS_IsNumber(p)) { goto disrupt; } - double d, place; - JS_ToFloat64(ctx, &d, v); - JS_ToFloat64(ctx, &place, p); - frame->slots[dest] = JS_NewFloat64(ctx, round(d * place) / place); - } - else if (strcmp(op, "trunc") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - JSValue p = frame->slots[(int)a3->valuedouble]; - if (!JS_IsNumber(v) || !JS_IsNumber(p)) { goto disrupt; } - double d, place; - JS_ToFloat64(ctx, &d, v); - JS_ToFloat64(ctx, &place, p); - frame->slots[dest] = JS_NewFloat64(ctx, trunc(d * place) / place); - } - - /* ---- Text ---- */ - else if (strcmp(op, "concat") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (!JS_IsText(left) || !JS_IsText(right)) { goto disrupt; } - JSValue res = JS_ConcatString(ctx, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; - } - else if (strcmp(op, "concat_space") == 0) { - int dest = (int)a1->valuedouble; - int left_slot = (int)a2->valuedouble; - int right_slot = (int)a3->valuedouble; - JSValue left = frame->slots[left_slot]; - JSValue right = frame->slots[right_slot]; - if (!JS_IsText(left) || !JS_IsText(right)) { goto disrupt; } - JSValue space_str = JS_NewString(ctx, " "); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - JSValue space = JS_ConcatString(ctx, frame->slots[left_slot], space_str); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - JSValue res = JS_ConcatString(ctx, space, frame->slots[right_slot]); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; - } - else if (strcmp(op, "length") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (!JS_IsText(v)) { goto disrupt; } - frame->slots[dest] = JS_NewInt32(ctx, js_string_value_len(v)); - } - else if (strcmp(op, "lower") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (!JS_IsText(v)) { goto disrupt; } - int vs_base = ctx->value_stack_top; - ctx->value_stack[vs_base] = v; - ctx->value_stack_top = vs_base + 1; - JSValue res = js_cell_text_lower(ctx, JS_NULL, 1, &ctx->value_stack[vs_base]); - ctx->value_stack_top = vs_base; - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; - } - else if (strcmp(op, "upper") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (!JS_IsText(v)) { goto disrupt; } - int vs_base = ctx->value_stack_top; - ctx->value_stack[vs_base] = v; - ctx->value_stack_top = vs_base + 1; - JSValue res = js_cell_text_upper(ctx, JS_NULL, 1, &ctx->value_stack[vs_base]); - ctx->value_stack_top = vs_base; - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; - } - else if (strcmp(op, "character") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (!JS_IsNumber(v)) { goto disrupt; } - int vs_base = ctx->value_stack_top; - ctx->value_stack[vs_base] = v; - ctx->value_stack_top = vs_base + 1; - JSValue res = js_cell_character(ctx, JS_NULL, 1, &ctx->value_stack[vs_base]); - ctx->value_stack_top = vs_base; - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; - } - else if (strcmp(op, "codepoint") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (!JS_IsText(v)) { goto disrupt; } - int vs_base = ctx->value_stack_top; - ctx->value_stack[vs_base] = v; - ctx->value_stack_top = vs_base + 1; - JSValue res = js_cell_text_codepoint(ctx, JS_NULL, 1, &ctx->value_stack[vs_base]); - ctx->value_stack_top = vs_base; - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; - } - - /* ---- Comparison (inline) ---- */ - else if (strcmp(op, "eq") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (left == right) { - frame->slots[dest] = JS_TRUE; - } else if (JS_VALUE_IS_BOTH_INT(left, right)) { - frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) == JS_VALUE_GET_INT(right)); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - frame->slots[dest] = JS_NewBool(ctx, a == b); - } else if (JS_IsText(left) && JS_IsText(right)) { - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, TRUE) == 0); - } else if (JS_IsNull(left) && JS_IsNull(right)) { - frame->slots[dest] = JS_TRUE; - } else if (JS_VALUE_GET_TAG(left) == JS_TAG_BOOL && JS_VALUE_GET_TAG(right) == JS_TAG_BOOL) { - frame->slots[dest] = JS_NewBool(ctx, left == right); - } else { - frame->slots[dest] = JS_FALSE; - } - } - else if (strcmp(op, "ne") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (left == right) { - frame->slots[dest] = JS_FALSE; - } else if (JS_VALUE_IS_BOTH_INT(left, right)) { - frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) != JS_VALUE_GET_INT(right)); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - frame->slots[dest] = JS_NewBool(ctx, a != b); - } else if (JS_IsText(left) && JS_IsText(right)) { - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, TRUE) != 0); - } else if (JS_IsNull(left) && JS_IsNull(right)) { - frame->slots[dest] = JS_FALSE; - } else if (JS_VALUE_GET_TAG(left) == JS_TAG_BOOL && JS_VALUE_GET_TAG(right) == JS_TAG_BOOL) { - frame->slots[dest] = JS_NewBool(ctx, left != right); - } else { - frame->slots[dest] = JS_TRUE; - } - } - else if (strcmp(op, "eq_tol") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - cJSON *a4 = cJSON_GetArrayItem(instr, 4); - JSValue tol = frame->slots[(int)a4->valuedouble]; - if (JS_IsNumber(left) && JS_IsNumber(right) && JS_IsNumber(tol)) { - double a, b, t; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - JS_ToFloat64(ctx, &t, tol); - frame->slots[dest] = JS_NewBool(ctx, fabs(a - b) <= t); - } else if (JS_IsText(left) && JS_IsText(right) && JS_VALUE_GET_TAG(tol) == JS_TAG_BOOL && JS_VALUE_GET_BOOL(tol)) { - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value_nocase(ctx, left, right) == 0); - } else { - /* Fall through to standard eq */ - if (left == right) frame->slots[dest] = JS_TRUE; - else if (JS_IsText(left) && JS_IsText(right)) - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, TRUE) == 0); - else frame->slots[dest] = JS_FALSE; - } - } - else if (strcmp(op, "ne_tol") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - cJSON *a4 = cJSON_GetArrayItem(instr, 4); - JSValue tol = frame->slots[(int)a4->valuedouble]; - if (JS_IsNumber(left) && JS_IsNumber(right) && JS_IsNumber(tol)) { - double a, b, t; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - JS_ToFloat64(ctx, &t, tol); - frame->slots[dest] = JS_NewBool(ctx, fabs(a - b) > t); - } else if (JS_IsText(left) && JS_IsText(right) && JS_VALUE_GET_TAG(tol) == JS_TAG_BOOL && JS_VALUE_GET_BOOL(tol)) { - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value_nocase(ctx, left, right) != 0); - } else { - if (left == right) frame->slots[dest] = JS_FALSE; - else if (JS_IsText(left) && JS_IsText(right)) - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, TRUE) != 0); - else frame->slots[dest] = JS_TRUE; - } - } - else if (strcmp(op, "and") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - frame->slots[dest] = JS_ToBool(ctx, left) ? right : left; - } - else if (strcmp(op, "or") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - frame->slots[dest] = JS_ToBool(ctx, left) ? left : right; - } - else if (strcmp(op, "lt") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) < JS_VALUE_GET_INT(right)); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - frame->slots[dest] = JS_NewBool(ctx, a < b); - } else if (JS_IsText(left) && JS_IsText(right)) { - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, FALSE) < 0); - } else { goto disrupt; } - } - else if (strcmp(op, "le") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) <= JS_VALUE_GET_INT(right)); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - frame->slots[dest] = JS_NewBool(ctx, a <= b); - } else if (JS_IsText(left) && JS_IsText(right)) { - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, FALSE) <= 0); - } else { goto disrupt; } - } - else if (strcmp(op, "gt") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) > JS_VALUE_GET_INT(right)); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - frame->slots[dest] = JS_NewBool(ctx, a > b); - } else if (JS_IsText(left) && JS_IsText(right)) { - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, FALSE) > 0); - } else { goto disrupt; } - } - else if (strcmp(op, "ge") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (JS_VALUE_IS_BOTH_INT(left, right)) { - frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) >= JS_VALUE_GET_INT(right)); - } else if (JS_IsNumber(left) && JS_IsNumber(right)) { - double a, b; - JS_ToFloat64(ctx, &a, left); - JS_ToFloat64(ctx, &b, right); - frame->slots[dest] = JS_NewBool(ctx, a >= b); - } else if (JS_IsText(left) && JS_IsText(right)) { - frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, FALSE) >= 0); - } else { goto disrupt; } - } - - /* ---- in operator ---- */ - else if (strcmp(op, "in") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - int ret = JS_HasPropertyKey(ctx, right, left); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (ret < 0) { goto disrupt; } - frame->slots[dest] = JS_NewBool(ctx, ret); - } - - /* ---- Sensory (type checks) ---- */ - else if (strcmp(op, "text?") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_VALUE_IS_TEXT(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "function?") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_IsFunction(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "null?") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_IsNull(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "integer?") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_IsInt(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "array?") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_IsArray(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "record?") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_IsRecord(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "logical?") == 0) { - int dest = (int)a1->valuedouble; - int tag = JS_VALUE_GET_TAG(frame->slots[(int)a2->valuedouble]); - frame->slots[dest] = (tag == JS_TAG_BOOL) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "true?") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = (frame->slots[(int)a2->valuedouble] == JS_TRUE) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "false?") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = (frame->slots[(int)a2->valuedouble] == JS_FALSE) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "blob?") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_IsBlob(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "character?") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - frame->slots[dest] = (JS_IsText(v) && js_string_value_len(v) == 1) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "data?") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - frame->slots[dest] = (JS_IsRecord(v) || JS_IsArray(v)) ? JS_TRUE : JS_FALSE; - } - else if (strcmp(op, "digit?") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (JS_IsText(v) && js_string_value_len(v) == 1) { - uint32_t c = js_string_value_get(v, 0); - frame->slots[dest] = (c >= '0' && c <= '9') ? JS_TRUE : JS_FALSE; - } else { - frame->slots[dest] = JS_FALSE; - } - } - else if (strcmp(op, "fit?") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (JS_IsInt(v)) { - frame->slots[dest] = JS_TRUE; - } else if (JS_IsNumber(v)) { - double d; - JS_ToFloat64(ctx, &d, v); - frame->slots[dest] = (d == (double)(int32_t)d) ? JS_TRUE : JS_FALSE; - } else { - frame->slots[dest] = JS_FALSE; - } - } - else if (strcmp(op, "letter?") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (JS_IsText(v) && js_string_value_len(v) == 1) { - uint32_t c = js_string_value_get(v, 0); - frame->slots[dest] = ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) ? JS_TRUE : JS_FALSE; - } else { - frame->slots[dest] = JS_FALSE; - } - } - else if (strcmp(op, "pattern?") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_FALSE; /* TODO: pattern type check */ - } - else if (strcmp(op, "stone?") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (JS_IsPtr(v)) { - objhdr_t hdr = *(objhdr_t *)JS_VALUE_GET_PTR(v); - frame->slots[dest] = objhdr_s(hdr) ? JS_TRUE : JS_FALSE; - } else { - /* Primitives are immutable */ - frame->slots[dest] = JS_TRUE; - } - } - else if (strcmp(op, "upper?") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (JS_IsText(v) && js_string_value_len(v) == 1) { - uint32_t c = js_string_value_get(v, 0); - frame->slots[dest] = (c >= 'A' && c <= 'Z') ? JS_TRUE : JS_FALSE; - } else { - frame->slots[dest] = JS_FALSE; - } - } - else if (strcmp(op, "whitespace?") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - if (JS_IsText(v) && js_string_value_len(v) == 1) { - uint32_t c = js_string_value_get(v, 0); - frame->slots[dest] = (c == ' ' || c == '\t' || c == '\n' || c == '\r') ? JS_TRUE : JS_FALSE; - } else { - frame->slots[dest] = JS_FALSE; - } - } - - /* ---- Logical / Bitwise ---- */ - else if (strcmp(op, "not") == 0) { - int dest = (int)a1->valuedouble; - int b = JS_ToBool(ctx, frame->slots[(int)a2->valuedouble]); - frame->slots[dest] = JS_NewBool(ctx, !b); - } - else if (strcmp(op, "bitnot") == 0) { - int dest = (int)a1->valuedouble; - int32_t i; JS_ToInt32(ctx, &i, frame->slots[(int)a2->valuedouble]); - frame->slots[dest] = JS_NewInt32(ctx, ~i); - } - else if (strcmp(op, "bitand") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (!JS_IsNumber(left) || !JS_IsNumber(right)) { goto disrupt; } - int32_t ia, ib; - JS_ToInt32(ctx, &ia, left); - JS_ToInt32(ctx, &ib, right); - frame->slots[dest] = JS_NewInt32(ctx, ia & ib); - } - else if (strcmp(op, "bitor") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (!JS_IsNumber(left) || !JS_IsNumber(right)) { goto disrupt; } - int32_t ia, ib; - JS_ToInt32(ctx, &ia, left); - JS_ToInt32(ctx, &ib, right); - frame->slots[dest] = JS_NewInt32(ctx, ia | ib); - } - else if (strcmp(op, "bitxor") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (!JS_IsNumber(left) || !JS_IsNumber(right)) { goto disrupt; } - int32_t ia, ib; - JS_ToInt32(ctx, &ia, left); - JS_ToInt32(ctx, &ib, right); - frame->slots[dest] = JS_NewInt32(ctx, ia ^ ib); - } - else if (strcmp(op, "shl") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (!JS_IsNumber(left) || !JS_IsNumber(right)) { goto disrupt; } - int32_t ia, ib; - JS_ToInt32(ctx, &ia, left); - JS_ToInt32(ctx, &ib, right); - frame->slots[dest] = JS_NewInt32(ctx, ia << (ib & 31)); - } - else if (strcmp(op, "shr") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (!JS_IsNumber(left) || !JS_IsNumber(right)) { goto disrupt; } - int32_t ia, ib; - JS_ToInt32(ctx, &ia, left); - JS_ToInt32(ctx, &ib, right); - frame->slots[dest] = JS_NewInt32(ctx, ia >> (ib & 31)); - } - else if (strcmp(op, "ushr") == 0) { - int dest = (int)a1->valuedouble; - JSValue left = frame->slots[(int)a2->valuedouble]; - JSValue right = frame->slots[(int)a3->valuedouble]; - if (!JS_IsNumber(left) || !JS_IsNumber(right)) { goto disrupt; } - int32_t ia, ib; - JS_ToInt32(ctx, &ia, left); - JS_ToInt32(ctx, &ib, right); - frame->slots[dest] = JS_NewInt32(ctx, (uint32_t)ia >> (ib & 31)); - } - - /* ---- Control flow ---- */ - else if (strcmp(op, "jump") == 0) { - const char *label = cJSON_IsString(a1) ? a1->valuestring : NULL; - if (label) pc = mcode_resolve_label(code, label); - } - else if (strcmp(op, "jump_true") == 0) { - int slot = (int)a1->valuedouble; - const char *label = cJSON_IsString(a2) ? a2->valuestring : NULL; - if (label && JS_ToBool(ctx, frame->slots[slot])) - pc = mcode_resolve_label(code, label); - } - else if (strcmp(op, "jump_false") == 0) { - int slot = (int)a1->valuedouble; - const char *label = cJSON_IsString(a2) ? a2->valuestring : NULL; - if (label && !JS_ToBool(ctx, frame->slots[slot])) - pc = mcode_resolve_label(code, label); - } - else if (strcmp(op, "jump_null") == 0) { - int slot = (int)a1->valuedouble; - const char *label = cJSON_IsString(a2) ? a2->valuestring : NULL; - if (label && JS_IsNull(frame->slots[slot])) - pc = mcode_resolve_label(code, label); - } - else if (strcmp(op, "jump_not_null") == 0) { - int slot = (int)a1->valuedouble; - const char *label = cJSON_IsString(a2) ? a2->valuestring : NULL; - if (label && !JS_IsNull(frame->slots[slot])) - pc = mcode_resolve_label(code, label); - } - else if (strcmp(op, "jump_empty") == 0) { - int slot = (int)a1->valuedouble; - const char *label = cJSON_IsString(a2) ? a2->valuestring : NULL; - if (label && JS_IsNull(frame->slots[slot])) - pc = mcode_resolve_label(code, label); - } - else if (strcmp(op, "wary_true") == 0) { - int slot = (int)a1->valuedouble; - const char *label = cJSON_IsString(a2) ? a2->valuestring : NULL; - JSValue v = frame->slots[slot]; - if (v == JS_TRUE) { - if (label) pc = mcode_resolve_label(code, label); - } else if (v != JS_FALSE) { - goto disrupt; - } - } - else if (strcmp(op, "wary_false") == 0) { - int slot = (int)a1->valuedouble; - const char *label = cJSON_IsString(a2) ? a2->valuestring : NULL; - JSValue v = frame->slots[slot]; - if (v == JS_FALSE) { - if (label) pc = mcode_resolve_label(code, label); - } else if (v != JS_TRUE) { - goto disrupt; - } - } - - /* ---- Property/element access (unified) ---- */ - else if (strcmp(op, "load") == 0) { - int dest = (int)a1->valuedouble; - int obj_reg = (int)a2->valuedouble; - JSValue obj = frame->slots[obj_reg]; - if (JS_IsFunction(obj)) { - JSFunction *fn_chk = JS_VALUE_GET_FUNCTION(obj); - if (fn_chk->length != 2) { - JS_ThrowTypeError(ctx, "cannot read property of non-proxy function"); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - goto disrupt; - } - } - JSValue val; - if (cJSON_IsString(a3)) { - JSValue key = JS_NewString(ctx, a3->valuestring); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - obj = frame->slots[obj_reg]; - val = JS_GetProperty(ctx, obj, key); - } else { - JSValue idx = frame->slots[(int)a3->valuedouble]; - if (JS_IsInt(idx)) - val = JS_GetPropertyUint32(ctx, obj, JS_VALUE_GET_INT(idx)); - else - val = JS_GetProperty(ctx, obj, idx); - } - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(val)) goto disrupt; - frame->slots[dest] = val; - } - else if (strcmp(op, "store") == 0) { - int obj_reg = (int)a1->valuedouble; - int val_reg = (int)a2->valuedouble; - JSValue obj = frame->slots[obj_reg]; - JSValue val = frame->slots[val_reg]; - if (JS_IsFunction(obj)) { - JS_ThrowTypeError(ctx, "cannot set property of function"); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - goto disrupt; - } - if (cJSON_IsString(a3)) { - JSValue key = JS_NewString(ctx, a3->valuestring); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - obj = frame->slots[obj_reg]; - val = frame->slots[val_reg]; - int ret = JS_SetProperty(ctx, obj, key, val); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (ret < 0) goto disrupt; - } else { - JSValue idx = frame->slots[(int)a3->valuedouble]; - int ret; - if (JS_IsInt(idx)) { - ret = JS_SetPropertyUint32(ctx, obj, JS_VALUE_GET_INT(idx), val); - } else if (JS_IsArray(obj)) { - JS_ThrowTypeError(ctx, "array index must be a number"); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - goto disrupt; - } else if (JS_IsRecord(obj) && !JS_IsText(idx) && !JS_IsRecord(idx)) { - JS_ThrowTypeError(ctx, "object key must be a string or object"); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - goto disrupt; - } else { - ret = JS_SetProperty(ctx, obj, idx, val); - } - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (ret < 0) goto disrupt; - } - } - else if (strcmp(op, "delete") == 0) { - int dest = (int)a1->valuedouble; - int obj_reg = (int)a2->valuedouble; - JSValue obj = frame->slots[obj_reg]; - JSValue key; - if (cJSON_IsString(a3)) { - key = JS_NewString(ctx, a3->valuestring); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - obj = frame->slots[obj_reg]; - } else { - key = frame->slots[(int)a3->valuedouble]; - } - int ret = JS_DeleteProperty(ctx, obj, key); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (ret < 0) goto disrupt; - frame->slots[dest] = JS_NewBool(ctx, ret >= 0); - } - - /* ---- Closure access ---- */ - else if (strcmp(op, "get") == 0) { - int dest = (int)a1->valuedouble; - int slot = (int)a2->valuedouble; - int depth = (int)a3->valuedouble; - /* Walk outer_frame chain from the current function's outer_frame */ - JSFunction *cur_fn = JS_VALUE_GET_FUNCTION(frame->function); - JSValue of = (cur_fn && cur_fn->kind == JS_FUNC_KIND_MCODE) ? cur_fn->u.mcode.outer_frame : JS_NULL; - for (int d = 1; d < depth && !JS_IsNull(of); d++) { - JSFrameRegister *outer = (JSFrameRegister *)JS_VALUE_GET_PTR(of); - JSFunction *outer_fn = JS_VALUE_GET_FUNCTION(outer->function); - of = (outer_fn && outer_fn->kind == JS_FUNC_KIND_MCODE) ? outer_fn->u.mcode.outer_frame : JS_NULL; - } - if (!JS_IsNull(of)) { - JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(of); - frame->slots[dest] = target->slots[slot]; - } else { - frame->slots[dest] = JS_NULL; - } - } - else if (strcmp(op, "put") == 0) { - int src = (int)a1->valuedouble; - int slot = (int)a2->valuedouble; - int depth = (int)a3->valuedouble; - JSFunction *cur_fn = JS_VALUE_GET_FUNCTION(frame->function); - JSValue of = (cur_fn && cur_fn->kind == JS_FUNC_KIND_MCODE) ? cur_fn->u.mcode.outer_frame : JS_NULL; - for (int d = 1; d < depth && !JS_IsNull(of); d++) { - JSFrameRegister *outer = (JSFrameRegister *)JS_VALUE_GET_PTR(of); - JSFunction *outer_fn = JS_VALUE_GET_FUNCTION(outer->function); - of = (outer_fn && outer_fn->kind == JS_FUNC_KIND_MCODE) ? outer_fn->u.mcode.outer_frame : JS_NULL; - } - if (!JS_IsNull(of)) { - JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(of); - target->slots[slot] = frame->slots[src]; - } - } - - - - /* ---- Function calls ---- */ - else if (strcmp(op, "frame") == 0) { - int frame_reg = (int)a1->valuedouble; - JSValue func_val = frame->slots[(int)a2->valuedouble]; - int call_argc = a3 ? (int)a3->valuedouble : 0; - - if (!JS_IsFunction(func_val)) { - goto disrupt; - } - - JSFunction *fn = JS_VALUE_GET_FUNCTION(func_val); - int nr_slots; - if (fn->kind == JS_FUNC_KIND_MCODE) { - nr_slots = fn->u.mcode.code->nr_slots; - } else { - nr_slots = call_argc + 2; - } - JSFrameRegister *new_frame = alloc_frame_register(ctx, nr_slots); - if (!new_frame) { goto disrupt; } - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - func_val = frame->slots[(int)a2->valuedouble]; - new_frame->function = func_val; - frame->slots[frame_reg] = JS_MKPTR(new_frame); - } - else if (strcmp(op, "setarg") == 0) { - int frame_reg = (int)a1->valuedouble; - int arg_idx = (int)a2->valuedouble; - int val_reg = (int)a3->valuedouble; - JSValue target = frame->slots[frame_reg]; - if (!JS_IsFunction(target)) { - JSFrameRegister *call_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(target); - call_frame->slots[arg_idx] = frame->slots[val_reg]; - } - } - else if (strcmp(op, "invoke") == 0) { - int frame_reg = (int)a1->valuedouble; - int ret_reg = (int)a2->valuedouble; - JSValue target = frame->slots[frame_reg]; - - JSFrameRegister *new_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(target); - JSFunction *fn = JS_VALUE_GET_FUNCTION(new_frame->function); - - if (fn->kind == JS_FUNC_KIND_MCODE) { - /* Store return address: pc << 16 | ret_slot */ - frame->address = JS_NewInt32(ctx, (pc << 16) | ret_reg); - new_frame->caller = JS_MKPTR(frame); - /* Switch to new frame */ - frame = new_frame; - frame_ref.val = JS_MKPTR(frame); - code = fn->u.mcode.code; - pc = 0; - } else { - /* C or bytecode function — collect args on value stack (GC-safe) */ - int nr_slots = (int)objhdr_cap56(new_frame->hdr); - int c_argc = (nr_slots >= 2) ? nr_slots - 2 : 0; - int vs_base = ctx->value_stack_top; - for (int i = 0; i < c_argc; i++) { - ctx->value_stack[vs_base + i] = new_frame->slots[i + 1]; - } - ctx->value_stack_top = vs_base + c_argc; - ctx->reg_current_frame = frame_ref.val; - ctx->current_register_pc = pc > 0 ? pc - 1 : 0; - JSValue c_result = JS_Call(ctx, new_frame->function, new_frame->slots[0], c_argc, &ctx->value_stack[vs_base]); - ctx->value_stack_top = vs_base; - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - ctx->reg_current_frame = JS_NULL; - if (JS_IsException(c_result)) { goto disrupt; } - frame->slots[ret_reg] = c_result; - } - } - - /* ---- Method call (handles function proxies) ---- */ - else if (strcmp(op, "callmethod") == 0) { - /* ["callmethod", dest, obj_reg, "method_name", arg0_reg, arg1_reg, ...] */ - int dest = (int)a1->valuedouble; - int obj_reg = (int)a2->valuedouble; - JSValue obj = frame->slots[obj_reg]; - const char *method_name = a3->valuestring; - /* Count arg registers (items after a3, minus trailing line/col) */ - int nargs = 0; - for (cJSON *p = a3->next; p; p = p->next) - nargs++; - nargs -= 2; /* subtract line and col metadata */ - if (nargs < 0) nargs = 0; - - if (JS_IsFunction(obj)) { - /* Proxy call: obj(name, [args...]) */ - /* Store key on value stack immediately to protect from GC */ - int vs_base = ctx->value_stack_top; - ctx->value_stack[vs_base] = JS_NewString(ctx, method_name); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - ctx->value_stack_top = vs_base + 1; /* protect key from GC */ - JSValue arr = JS_NewArray(ctx); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(arr)) goto disrupt; - frame->slots[dest] = arr; /* protect from GC */ - cJSON *p = a3->next; - for (int i = 0; i < nargs; i++, p = p->next) { - if (cJSON_IsString(p)) break; /* hit line/col */ - int areg = (int)p->valuedouble; - JS_SetPropertyUint32(ctx, frame->slots[dest], i, frame->slots[areg]); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - } - ctx->value_stack[vs_base + 1] = frame->slots[dest]; - ctx->value_stack_top = vs_base + 2; - ctx->reg_current_frame = frame_ref.val; - ctx->current_register_pc = pc > 0 ? pc - 1 : 0; - JSValue ret = JS_CallInternal(ctx, frame->slots[obj_reg], JS_NULL, 2, &ctx->value_stack[vs_base], 0); - ctx->value_stack_top = vs_base; - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - ctx->reg_current_frame = JS_NULL; - if (JS_IsException(ret)) goto disrupt; - frame->slots[dest] = ret; - } else { - /* Record method call: get property, call with this=obj */ - JSValue key = JS_NewString(ctx, method_name); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - JSValue method = JS_GetProperty(ctx, frame->slots[obj_reg], key); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(method)) goto disrupt; - if (!JS_IsFunction(method)) { - frame->slots[dest] = JS_NULL; - } else { - JSFunction *fn = JS_VALUE_GET_FUNCTION(method); - if (fn->kind == JS_FUNC_KIND_MCODE) { - /* mcode function — set up frame and jump */ - frame->slots[dest] = method; /* protect from GC */ - JSFrameRegister *new_frame = alloc_frame_register(ctx, fn->u.mcode.code->nr_slots); - if (!new_frame) { - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - goto disrupt; - } - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - method = frame->slots[dest]; /* re-read after GC */ - fn = JS_VALUE_GET_FUNCTION(method); - new_frame->function = method; - new_frame->slots[0] = frame->slots[obj_reg]; /* this */ - cJSON *p = a3->next; - for (int i = 0; i < nargs && i < fn->u.mcode.code->nr_slots - 1; i++, p = p->next) { - if (cJSON_IsString(p)) break; - new_frame->slots[1 + i] = frame->slots[(int)p->valuedouble]; - } - frame->address = JS_NewInt32(ctx, (pc << 16) | dest); - new_frame->caller = JS_MKPTR(frame); - frame = new_frame; - frame_ref.val = JS_MKPTR(frame); - code = fn->u.mcode.code; - pc = 0; - } else { - /* C or bytecode function */ - int vs_base = ctx->value_stack_top; - cJSON *p = a3->next; - for (int i = 0; i < nargs; i++, p = p->next) { - if (cJSON_IsString(p)) break; - ctx->value_stack[vs_base + i] = frame->slots[(int)p->valuedouble]; - } - ctx->value_stack_top = vs_base + nargs; - ctx->reg_current_frame = frame_ref.val; - ctx->current_register_pc = pc > 0 ? pc - 1 : 0; - JSValue ret = JS_Call(ctx, method, frame->slots[obj_reg], nargs, &ctx->value_stack[vs_base]); - ctx->value_stack_top = vs_base; - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - ctx->reg_current_frame = JS_NULL; - if (JS_IsException(ret)) goto disrupt; - frame->slots[dest] = ret; - } - } - } - } - - else if (strcmp(op, "callmethod_dyn") == 0) { - /* ["callmethod_dyn", dest, obj_reg, key_reg, arg0_reg, ...] */ - int dest = (int)a1->valuedouble; - int obj_reg = (int)a2->valuedouble; - int key_reg = (int)a3->valuedouble; - JSValue obj = frame->slots[obj_reg]; - JSValue key = frame->slots[key_reg]; - /* Count arg registers (items after a3, minus trailing line/col) */ - int nargs = 0; - for (cJSON *p = a3->next; p; p = p->next) - nargs++; - nargs -= 2; - if (nargs < 0) nargs = 0; - - if (JS_IsFunction(obj) && JS_VALUE_IS_TEXT(key) && - JS_VALUE_GET_FUNCTION(obj)->length == 2) { - /* Proxy call (arity-2 functions only): obj(key, [args...]) */ - int vs_base = ctx->value_stack_top; - ctx->value_stack[vs_base] = key; /* protect key on value stack */ - ctx->value_stack_top = vs_base + 1; /* protect key from GC */ - JSValue arr = JS_NewArray(ctx); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(arr)) goto disrupt; - frame->slots[dest] = arr; /* protect from GC */ - cJSON *p = a3->next; - for (int i = 0; i < nargs; i++, p = p->next) { - int areg = (int)p->valuedouble; - JS_SetPropertyUint32(ctx, frame->slots[dest], i, frame->slots[areg]); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - } - ctx->value_stack[vs_base + 1] = frame->slots[dest]; - ctx->value_stack_top = vs_base + 2; - ctx->reg_current_frame = frame_ref.val; - ctx->current_register_pc = pc > 0 ? pc - 1 : 0; - JSValue ret = JS_CallInternal(ctx, frame->slots[obj_reg], JS_NULL, 2, &ctx->value_stack[vs_base], 0); - ctx->value_stack_top = vs_base; - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - ctx->reg_current_frame = JS_NULL; - if (JS_IsException(ret)) goto disrupt; - frame->slots[dest] = ret; - } else if (JS_IsFunction(obj)) { - /* Non-proxy function: bracket access not allowed */ - JS_ThrowTypeError(ctx, "cannot use bracket notation on non-proxy function"); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - goto disrupt; - } else { - /* Record method call: get property, call with this=obj */ - JSValue method = JS_GetProperty(ctx, frame->slots[obj_reg], key); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(method)) goto disrupt; - if (!JS_IsFunction(method)) { - frame->slots[dest] = JS_NULL; - } else { - JSFunction *fn = JS_VALUE_GET_FUNCTION(method); - if (fn->kind == JS_FUNC_KIND_MCODE) { - frame->slots[dest] = method; /* protect method from GC */ - JSFrameRegister *new_frame = alloc_frame_register(ctx, fn->u.mcode.code->nr_slots); - if (!new_frame) { - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - goto disrupt; - } - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - method = frame->slots[dest]; /* re-read after GC */ - fn = JS_VALUE_GET_FUNCTION(method); - new_frame->function = method; - new_frame->slots[0] = frame->slots[obj_reg]; /* this */ - cJSON *p = a3->next; - for (int i = 0; i < nargs && i < fn->u.mcode.code->nr_slots - 1; i++, p = p->next) { - new_frame->slots[1 + i] = frame->slots[(int)p->valuedouble]; - } - frame->address = JS_NewInt32(ctx, (pc << 16) | dest); - new_frame->caller = JS_MKPTR(frame); - frame = new_frame; - frame_ref.val = JS_MKPTR(frame); - code = fn->u.mcode.code; - pc = 0; - } else { - int vs_base = ctx->value_stack_top; - cJSON *p = a3->next; - for (int i = 0; i < nargs; i++, p = p->next) { - ctx->value_stack[vs_base + i] = frame->slots[(int)p->valuedouble]; - } - ctx->value_stack_top = vs_base + nargs; - ctx->reg_current_frame = frame_ref.val; - ctx->current_register_pc = pc > 0 ? pc - 1 : 0; - JSValue ret = JS_Call(ctx, method, frame->slots[obj_reg], nargs, &ctx->value_stack[vs_base]); - ctx->value_stack_top = vs_base; - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - ctx->reg_current_frame = JS_NULL; - if (JS_IsException(ret)) goto disrupt; - frame->slots[dest] = ret; - } - } - } - } - - /* ---- Tail calls ---- */ - else if (strcmp(op, "goframe") == 0) { - int frame_reg = (int)a1->valuedouble; - int func_reg = (int)a2->valuedouble; - int call_argc = a3 ? (int)a3->valuedouble : 0; - JSValue func_val = frame->slots[func_reg]; - - if (!JS_IsFunction(func_val)) { - goto disrupt; - } - JSFunction *fn = JS_VALUE_GET_FUNCTION(func_val); - int nr_slots; - if (fn->kind == JS_FUNC_KIND_MCODE) { - nr_slots = fn->u.mcode.code->nr_slots; - } else { - nr_slots = call_argc + 2; - } - JSFrameRegister *new_frame = alloc_frame_register(ctx, nr_slots); - if (!new_frame) { goto disrupt; } - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - func_val = frame->slots[func_reg]; - new_frame->function = func_val; - frame->slots[frame_reg] = JS_MKPTR(new_frame); - } - else if (strcmp(op, "goinvoke") == 0) { - int frame_reg = (int)a1->valuedouble; - JSValue target = frame->slots[frame_reg]; - - if (JS_IsFunction(target)) { - result = JS_ThrowInternalError(ctx, "C function tail call not supported in MCODE"); - goto disrupt; - } - - JSFrameRegister *new_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(target); - JSFunction *fn = JS_VALUE_GET_FUNCTION(new_frame->function); - - if (fn->kind != JS_FUNC_KIND_MCODE) { - goto disrupt; - } - - /* Tail call — bypass current frame */ - new_frame->caller = frame->caller; - new_frame->address = frame->address; - frame = new_frame; - frame_ref.val = JS_MKPTR(frame); - code = fn->u.mcode.code; - pc = 0; - } - - /* ---- Return ---- */ - else if (strcmp(op, "return") == 0) { - result = frame->slots[(int)a1->valuedouble]; - - if (JS_IsNull(frame->caller)) goto done; - - JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller); - int ret_info = JS_VALUE_GET_INT(caller->address); - frame->caller = JS_NULL; - - frame = caller; - frame_ref.val = JS_MKPTR(frame); - - JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function); - code = fn->u.mcode.code; - pc = ret_info >> 16; - frame->slots[ret_info & 0xFFFF] = result; - } - else if (strcmp(op, "return_value") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = result; - } - - /* ---- Apply ---- */ - else if (strcmp(op, "apply") == 0) { - int func_slot = (int)a1->valuedouble; - int arr_slot = (int)a2->valuedouble; - if (!JS_IsFunction(frame->slots[func_slot]) || !JS_IsArray(frame->slots[arr_slot])) { goto disrupt; } - JSValue len_val = JS_GetProperty(ctx, frame->slots[arr_slot], JS_KEY_length); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - int len = JS_IsNumber(len_val) ? (int)JS_VALUE_GET_INT(len_val) : 0; - if (len > 256) len = 256; - int vs_base = ctx->value_stack_top; - for (int i = 0; i < len; i++) { - ctx->value_stack[vs_base + i] = JS_GetPropertyUint32(ctx, frame->slots[arr_slot], i); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - } - ctx->value_stack_top = vs_base + len; - ctx->reg_current_frame = frame_ref.val; - ctx->current_register_pc = pc > 0 ? pc - 1 : 0; - result = JS_Call(ctx, frame->slots[func_slot], JS_NULL, len, &ctx->value_stack[vs_base]); - ctx->value_stack_top = vs_base; - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(result)) { goto disrupt; } - } - - /* ---- Object/Array creation ---- */ - else if (strcmp(op, "record") == 0) { - int dest = (int)a1->valuedouble; - JSValue rec = JS_NewObject(ctx); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(rec)) { goto disrupt; } - frame->slots[dest] = rec; - } - else if (strcmp(op, "array") == 0) { - int dest = (int)a1->valuedouble; - int nr_elems = a2 ? (int)a2->valuedouble : 0; - JSValue arr = JS_NewArray(ctx); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(arr)) { goto disrupt; } - frame->slots[dest] = arr; - for (int i = 0; i < nr_elems; i++) { - cJSON *elem = cJSON_GetArrayItem(instr, 3 + i); - if (elem) { - int elem_slot = (int)elem->valuedouble; - JS_SetPropertyUint32(ctx, frame->slots[dest], i, frame->slots[elem_slot]); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - } - } - } - else if (strcmp(op, "function") == 0) { - int dest = (int)a1->valuedouble; - int func_id = (int)a2->valuedouble; - if ((uint32_t)func_id < code->func_count && code->functions[func_id]) { - JSValue fn_val = js_new_mcode_function(ctx, code->functions[func_id]); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val); - fn->u.mcode.outer_frame = frame_ref.val; - frame->slots[dest] = fn_val; - } else { - frame->slots[dest] = JS_NULL; - } - } - - /* ---- Blob ---- */ - else if (strcmp(op, "blob") == 0) { - int dest = (int)a1->valuedouble; - int nr_bits = a2 ? (int)a2->valuedouble : 0; - blob *bd = blob_new((size_t)(nr_bits < 0 ? 0 : nr_bits)); - if (!bd) { goto disrupt; } - JSValue bv = js_new_blob(ctx, bd); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(bv)) { goto disrupt; } - frame->slots[dest] = bv; - } - - /* ---- Pretext ---- */ - else if (strcmp(op, "pretext") == 0) { - int dest = (int)a1->valuedouble; - int nr_chars = a2 ? (int)a2->valuedouble : 16; - JSText *s = pretext_init(ctx, nr_chars); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (!s) { goto disrupt; } - frame->slots[dest] = JS_MKPTR(s); - } - - /* ---- Append (to pretext) ---- */ - else if (strcmp(op, "append") == 0) { - int pt_slot = (int)a1->valuedouble; - int right_slot = (int)a2->valuedouble; - if (!JS_IsText(frame->slots[pt_slot]) || !JS_IsText(frame->slots[right_slot])) { goto disrupt; } - int vs_base = ctx->value_stack_top; - ctx->value_stack[vs_base] = frame->slots[pt_slot]; - ctx->value_stack[vs_base + 1] = frame->slots[right_slot]; - ctx->value_stack_top = vs_base + 2; - JSText *s = JS_VALUE_GET_PTR(ctx->value_stack[vs_base]); - s = pretext_concat_value(ctx, s, ctx->value_stack[vs_base + 1]); - ctx->value_stack_top = vs_base; - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (!s) { goto disrupt; } - frame->slots[pt_slot] = JS_MKPTR(s); - } - - /* ---- Stone ---- */ - else if (strcmp(op, "stone") == 0) { - int dest = (int)a1->valuedouble; - JSValue v = frame->slots[(int)a2->valuedouble]; - JSValue stoned = JS_Stone(ctx, v); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = stoned; - } - - /* ---- Regexp literal ---- */ - else if (strcmp(op, "regexp") == 0) { - int dest = (int)a1->valuedouble; - const char *pattern = a2 ? a2->valuestring : ""; - cJSON *a3 = cJSON_GetArrayItem(instr, 3); - const char *flags_str = a3 ? a3->valuestring : ""; - if (!pattern) pattern = ""; - if (!flags_str) flags_str = ""; - int vs_base = ctx->value_stack_top; - ctx->value_stack[vs_base] = JS_NewString(ctx, pattern); /* pat_val */ - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - ctx->value_stack_top = vs_base + 1; /* protect pattern from GC */ - ctx->value_stack[vs_base + 1] = *flags_str ? JS_NewString(ctx, flags_str) : JS_NULL; /* flags_val */ - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - ctx->value_stack_top = vs_base + 2; - JSValue bc = js_compile_regexp(ctx, ctx->value_stack[vs_base], ctx->value_stack[vs_base + 1]); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(bc)) { ctx->value_stack_top = vs_base; goto disrupt; } - JSValue re_obj = js_regexp_constructor_internal(ctx, ctx->value_stack[vs_base], bc); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - ctx->value_stack_top = vs_base; - if (JS_IsException(re_obj)) { goto disrupt; } - frame->slots[dest] = re_obj; - } - - /* ---- Push (append to array) ---- */ - else if (strcmp(op, "push") == 0) { - int arr_slot = (int)a1->valuedouble; - int val_slot = (int)a2->valuedouble; - if (!JS_IsArray(frame->slots[arr_slot])) { goto disrupt; } - JSGCRef arr_gc; - JS_PushGCRef(ctx, &arr_gc); - arr_gc.val = frame->slots[arr_slot]; - JSGCRef val_gc; - JS_PushGCRef(ctx, &val_gc); - val_gc.val = frame->slots[val_slot]; - int rc = JS_ArrayPush(ctx, &arr_gc.val, val_gc.val); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - JS_PopGCRef(ctx, &val_gc); - JS_PopGCRef(ctx, &arr_gc); - if (rc < 0) goto disrupt; - frame->slots[arr_slot] = arr_gc.val; - } - - /* ---- Pop (remove last from array) ---- */ - else if (strcmp(op, "pop") == 0) { - int dest = (int)a1->valuedouble; - JSValue arr = frame->slots[(int)a2->valuedouble]; - if (!JS_IsArray(arr)) { goto disrupt; } - JSValue popped = JS_ArrayPop(ctx, arr); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = popped; - } - - /* ---- Disruption ---- */ - else if (strcmp(op, "disrupt") == 0) { - goto disrupt; - } - - /* ---- Unknown opcode ---- */ - else { - result = JS_ThrowInternalError(ctx, "unknown MCODE opcode: %s", op); - goto done; - } - continue; - - disrupt: - /* Search frame chain for a disruption handler. - Use frame_pc to track each frame's execution point: - - For the faulting frame, it's the current pc. - - For unwound caller frames, read from frame->address. */ - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); /* re-chase after GC */ - { - uint32_t frame_pc = pc; - for (;;) { - JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function); - JSMCode *fn_code = fn->u.mcode.code; - /* Only enter handler if we're not already inside it */ - if (fn_code->disruption_pc > 0 && frame_pc < fn_code->disruption_pc) { - code = fn_code; - pc = fn_code->disruption_pc; - break; - } - if (JS_IsNull(frame->caller)) { - fprintf(stderr, "unhandled disruption\n"); - result = JS_Throw(ctx, JS_NULL); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - goto done; - } - /* Unwind one frame — read caller's saved pc from its address field */ - JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller); - frame->caller = JS_NULL; - frame = caller; - frame_ref.val = JS_MKPTR(frame); - frame_pc = (uint32_t)(JS_VALUE_GET_INT(frame->address) >> 16); - } - } - } - -done: - if (JS_IsException(result)) { - ctx->reg_current_frame = frame_ref.val; - ctx->current_register_pc = pc > 0 ? pc - 1 : 0; - } - JS_DeleteGCRef(ctx, &frame_ref); - return result; -} - -JSValue JS_CallMcodeTree(JSContext *ctx, cJSON *root) { - 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 */ - JSValue result = mcode_exec(ctx, code, ctx->global_obj, 0, NULL, JS_NULL); - - /* 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_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"); - return JS_CallMcodeTree(ctx, root); -} diff --git a/source/quickjs-internal.h b/source/quickjs-internal.h index 4cc3dd98..88a960a4 100644 --- a/source/quickjs-internal.h +++ b/source/quickjs-internal.h @@ -812,28 +812,6 @@ typedef struct JSCodeRegister { uint16_t disruption_pc; /* start of disruption handler (0 = none) */ } JSCodeRegister; -/* Pre-parsed MCODE for a single function (off-heap, never GC'd). - Created by jsmcode_parse from cJSON MCODE output. - Instructions remain as cJSON pointers for string-based dispatch. */ -typedef struct JSMCode { - uint16_t nr_args; - uint16_t nr_slots; - /* Pre-flattened instruction array (cJSON array items → C array for O(1) access) */ - cJSON **instrs; - uint32_t instr_count; - /* Label map: label string → instruction index */ - struct { const char *name; uint32_t index; } *labels; - uint32_t label_count; - /* Nested function definitions (indexes into top-level functions array) */ - struct JSMCode **functions; - uint32_t func_count; - /* Keep root cJSON alive (owns all the cJSON nodes instrs[] point into) */ - cJSON *json_root; - MachLineEntry *line_table; /* [instr_count], parallel to instrs[] */ - const char *name; /* function name (points into cJSON tree) */ - const char *filename; /* source filename (points into cJSON tree) */ - uint16_t disruption_pc; /* start of disruption handler (0 = none) */ -} JSMCode; /* Frame for closures - used by link-time relocation model where closures reference outer frames via (depth, slot) addressing. @@ -1306,7 +1284,6 @@ typedef enum { JS_FUNC_KIND_BYTECODE, JS_FUNC_KIND_C_DATA, JS_FUNC_KIND_REGISTER, /* register-based VM function */ - JS_FUNC_KIND_MCODE, /* MCODE JSON interpreter */ } JSFunctionKind; typedef struct JSFunction { @@ -1330,11 +1307,6 @@ typedef struct JSFunction { JSValue env_record; /* stone record, module environment */ JSValue outer_frame; /* JSFrame JSValue, for closures */ } reg; - struct { - JSMCode *code; /* pre-parsed MCODE (off-heap) */ - JSValue outer_frame; /* lexical parent frame for closures */ - JSValue env_record; /* module env or JS_NULL */ - } mcode; } u; } JSFunction; @@ -1527,8 +1499,6 @@ JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue this_obj, JSValue js_call_bound_function (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv); JSValue JS_CallInternal (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv, int flags); JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JSValue this_obj, int argc, JSValue *argv, JSValue env, JSValue outer_frame); -JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, - int argc, JSValue *argv, JSValue outer_frame); int JS_DeleteProperty (JSContext *ctx, JSValue obj, JSValue prop); JSValue __attribute__ ((format (printf, 2, 3))) JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...); @@ -1860,13 +1830,6 @@ typedef struct { } GetLineColCache; -/* === MachVarInfo (shared by mach.c and mcode.c) === */ -typedef struct MachVarInfo { - char *name; - int slot; - int is_const; /* 1 for def, function args; 0 for var */ - int is_closure; /* 1 if captured by a nested function */ -} MachVarInfo; /* === PPretext (parser pretext, system-malloc, used by cell_js.c parser) === */ typedef struct PPretext { @@ -2057,8 +2020,5 @@ JSFrameRegister *alloc_frame_register(JSContext *ctx, int slot_count); cJSON *mach_find_scope_record(cJSON *scopes, int function_nr); int reg_vm_check_interrupt(JSContext *ctx); -/* mcode.c exports */ -JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, int argc, JSValue *argv, JSValue outer_frame); - #endif /* QUICKJS_INTERNAL_H */ diff --git a/source/quickjs.h b/source/quickjs.h index 8c773150..b03993b0 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -1091,34 +1091,12 @@ MachCode *JS_DeserializeMachCode(const uint8_t *data, size_t size); /* Load compiled MachCode into a JSContext, materializing JSValues. */ struct JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env); -/* 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 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); - /* Deserialize and execute pre-compiled MACH binary bytecode. */ JSValue JS_RunMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue env); /* Compile mcode JSON IR to MachCode binary. */ MachCode *mach_compile_mcode(struct cJSON *mcode_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); - /* Get stack trace as cJSON array of frame objects. Returns NULL if no register VM frame is active. Caller must call cJSON_Delete() on the result. */ diff --git a/source/runtime.c b/source/runtime.c index 0e026a06..637b436d 100644 --- a/source/runtime.c +++ b/source/runtime.c @@ -1139,10 +1139,6 @@ void gc_scan_object (JSContext *ctx, void *ptr, uint8_t *from_base, uint8_t *fro nested->name = gc_copy_value (ctx, nested->name, from_base, from_end, to_base, to_free, to_end); } } - } else if (fn->kind == JS_FUNC_KIND_MCODE) { - /* MCODE function - scan outer_frame and env_record */ - fn->u.mcode.outer_frame = gc_copy_value (ctx, fn->u.mcode.outer_frame, from_base, from_end, to_base, to_free, to_end); - fn->u.mcode.env_record = gc_copy_value (ctx, fn->u.mcode.env_record, from_base, from_end, to_base, to_free, to_end); } break; } @@ -4630,8 +4626,6 @@ JSValue JS_CallInternal (JSContext *ctx, JSValue func_obj, JSValue this_obj, case JS_FUNC_KIND_REGISTER: return JS_CallRegisterVM (ctx, f->u.reg.code, this_obj, argc, argv, f->u.reg.env_record, f->u.reg.outer_frame); - case JS_FUNC_KIND_MCODE: - return mcode_exec (ctx, f->u.mcode.code, this_obj, argc, argv, f->u.mcode.outer_frame); default: return JS_ThrowTypeError (ctx, "not a function"); } @@ -4653,8 +4647,6 @@ JSValue JS_Call (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, J case JS_FUNC_KIND_REGISTER: return JS_CallRegisterVM (ctx, f->u.reg.code, this_obj, argc, argv, f->u.reg.env_record, f->u.reg.outer_frame); - case JS_FUNC_KIND_MCODE: - return mcode_exec (ctx, f->u.mcode.code, this_obj, argc, argv, f->u.mcode.outer_frame); default: return JS_ThrowTypeError (ctx, "not a function"); } @@ -10117,83 +10109,6 @@ int js_is_blob (JSContext *js, JSValue v) { * ============================================================================ */ -/* mach_eval_ast(name, ast_json, env?) - compile pre-parsed AST and run */ -static JSValue js_mach_eval_ast (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1])) - return JS_ThrowTypeError (ctx, "mach_eval_ast requires (name, ast_json) text arguments"); - - const char *name = JS_ToCString (ctx, argv[0]); - if (!name) return JS_EXCEPTION; - - const char *json_str = JS_ToCString (ctx, argv[1]); - if (!json_str) { - JS_FreeCString (ctx, name); - return JS_EXCEPTION; - } - - cJSON *ast = cJSON_Parse (json_str); - JS_FreeCString (ctx, json_str); - - if (!ast) { - JS_FreeCString (ctx, name); - return JS_ThrowSyntaxError (ctx, "mach_eval_ast: failed to parse AST JSON"); - } - - /* Set the filename on the AST root */ - cJSON_DeleteItemFromObjectCaseSensitive (ast, "filename"); - cJSON_AddStringToObject (ast, "filename", name); - - 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; -} - -/* mach_compile_ast(name, ast_json) - compile pre-parsed AST to binary blob */ -static JSValue js_mach_compile_ast (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1])) - return JS_ThrowTypeError (ctx, "mach_compile_ast requires (name, ast_json) text arguments"); - - const char *name = JS_ToCString (ctx, argv[0]); - if (!name) return JS_EXCEPTION; - - const char *json_str = JS_ToCString (ctx, argv[1]); - if (!json_str) { - JS_FreeCString (ctx, name); - return JS_EXCEPTION; - } - - cJSON *ast = cJSON_Parse (json_str); - JS_FreeCString (ctx, json_str); - - if (!ast) { - JS_FreeCString (ctx, name); - return JS_ThrowSyntaxError (ctx, "mach_compile_ast: failed to parse AST JSON"); - } - - cJSON_DeleteItemFromObjectCaseSensitive (ast, "filename"); - cJSON_AddStringToObject (ast, "filename", name); - - MachCode *mc = JS_CompileMachTree (ast); - cJSON_Delete (ast); - JS_FreeCString (ctx, name); - - if (!mc) - return JS_ThrowSyntaxError (ctx, "mach_compile_ast: failed to compile AST"); - - size_t blob_size; - uint8_t *buf = JS_SerializeMachCode (mc, &blob_size); - JS_FreeMachCode (mc); - - if (!buf) - return JS_ThrowInternalError (ctx, "mach_compile_ast: serialization failed"); - - JSValue result = js_new_blob_stoned_copy (ctx, buf, blob_size); - sys_free (buf); - return result; -} - /* mach_load(blob, env?) - deserialize and execute binary blob */ static JSValue js_mach_load (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { if (argc < 1) @@ -10207,6 +10122,11 @@ static JSValue js_mach_load (JSContext *ctx, JSValue this_val, int argc, JSValue if (!mc) return JS_ThrowSyntaxError (ctx, "mach_load: failed to deserialize bytecode"); + { + extern void verify_getup(MachCode *mc, const char *path); + verify_getup(mc, "MACH_LOAD"); + } + JSValue env = (argc >= 2 && JS_IsObject (argv[1])) ? argv[1] : JS_NULL; JSGCRef env_ref; @@ -10220,35 +10140,6 @@ static JSValue js_mach_load (JSContext *ctx, JSValue this_val, int argc, JSValue return result; } -/* mcode_run(name, mcode_json, env?) - run pre-compiled mcode JSON */ -static JSValue js_mcode_run (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1])) - return JS_ThrowTypeError (ctx, "mcode_run requires (name, mcode_json) text arguments"); - - const char *name = JS_ToCString (ctx, argv[0]); - if (!name) return JS_EXCEPTION; - - const char *json_str = JS_ToCString (ctx, argv[1]); - if (!json_str) { - JS_FreeCString (ctx, name); - return JS_EXCEPTION; - } - - cJSON *mcode = cJSON_Parse (json_str); - JS_FreeCString (ctx, json_str); - - if (!mcode) { - JS_FreeCString (ctx, name); - return JS_ThrowSyntaxError (ctx, "mcode_run: failed to parse mcode JSON"); - } - - JSValue env = (argc >= 3 && JS_IsObject (argv[2])) ? argv[2] : JS_NULL; - JSValue result = JS_CallMcodeTreeEnv (ctx, mcode, env); - /* mcode tree ownership transferred to JS_CallMcodeTreeEnv — do not free */ - JS_FreeCString (ctx, name); - return result; -} - /* mach_eval_mcode(name, mcode_json, env?) - compile mcode IR and run via register VM */ static JSValue js_mach_eval_mcode (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1])) @@ -10298,6 +10189,61 @@ static JSValue js_mach_eval_mcode (JSContext *ctx, JSValue this_val, int argc, J return result; } +/* mach_compile_mcode_bin(name, mcode_json) - compile mcode IR to serialized binary blob */ +static JSValue js_mach_compile_mcode_bin (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1])) + return JS_ThrowTypeError (ctx, "mach_compile_mcode_bin requires (name, mcode_json) text arguments"); + + const char *name = JS_ToCString (ctx, argv[0]); + if (!name) return JS_EXCEPTION; + + const char *json_str = JS_ToCString (ctx, argv[1]); + if (!json_str) { + JS_FreeCString (ctx, name); + return JS_EXCEPTION; + } + + cJSON *mcode = cJSON_Parse (json_str); + JS_FreeCString (ctx, json_str); + + if (!mcode) { + JS_FreeCString (ctx, name); + return JS_ThrowSyntaxError (ctx, "mach_compile_mcode_bin: failed to parse mcode JSON"); + } + + if (!cJSON_GetObjectItemCaseSensitive (mcode, "filename")) + cJSON_AddStringToObject (mcode, "filename", name); + + MachCode *mc = mach_compile_mcode (mcode); + cJSON_Delete (mcode); + JS_FreeCString (ctx, name); + + if (!mc) + return JS_ThrowInternalError (ctx, "mach_compile_mcode_bin: compilation failed"); + + size_t size = 0; + uint8_t *data = JS_SerializeMachCode (mc, &size); + + /* DEBUG: verify round-trip */ + { + MachCode *mc2 = JS_DeserializeMachCode (data, size); + if (mc2) { + extern void verify_getup(MachCode *mc, const char *path); + verify_getup(mc2, "ROUNDTRIP"); + JS_FreeMachCode (mc2); + } + } + + JS_FreeMachCode (mc); + + if (!data) + return JS_ThrowInternalError (ctx, "mach_compile_mcode_bin: serialization failed"); + + JSValue blob = js_new_blob_stoned_copy (ctx, data, size); + sys_free (data); + return blob; +} + /* ============================================================================ * stone() function - deep freeze with blob support * ============================================================================ @@ -11398,11 +11344,9 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) { } /* Core functions - using GC-safe helper */ - js_set_global_cfunc(ctx, "mach_eval_ast", js_mach_eval_ast, 3); - js_set_global_cfunc(ctx, "mcode_run", js_mcode_run, 3); - js_set_global_cfunc(ctx, "mach_compile_ast", js_mach_compile_ast, 2); js_set_global_cfunc(ctx, "mach_load", js_mach_load, 2); js_set_global_cfunc(ctx, "mach_eval_mcode", js_mach_eval_mcode, 3); + js_set_global_cfunc(ctx, "mach_compile_mcode_bin", js_mach_compile_mcode_bin, 2); js_set_global_cfunc(ctx, "stone", js_cell_stone, 1); js_set_global_cfunc(ctx, "length", js_cell_length, 1); js_set_global_cfunc(ctx, "call", js_cell_call, 3); @@ -13150,17 +13094,6 @@ cJSON *JS_GetStack(JSContext *ctx) { line = code->line_table[pc].line; col = code->line_table[pc].col; } - } else if (fn->kind == JS_FUNC_KIND_MCODE && fn->u.mcode.code) { - JSMCode *code = fn->u.mcode.code; - file = code->filename; - func_name = code->name; - if (!is_first) { - pc = (uint32_t)(JS_VALUE_GET_INT(frame->address) >> 16); - } - if (code->line_table && pc < code->instr_count) { - line = code->line_table[pc].line; - col = code->line_table[pc].col; - } } cJSON *entry = cJSON_CreateObject(); diff --git a/streamline.mach b/streamline.mach index 3ccf6a76..699b76cf 100644 Binary files a/streamline.mach and b/streamline.mach differ diff --git a/tokenize.mach b/tokenize.mach index a8d0e522..d6cca1b7 100644 Binary files a/tokenize.mach and b/tokenize.mach differ