diff --git a/source/quickjs.c b/source/quickjs.c index 52e62a29..741b091c 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -387,7 +387,6 @@ struct JSRuntime { /* User data */ void *user_opaque; - /* Interrupt handler for checking execution limits */ JSInterruptHandler *interrupt_handler; void *interrupt_opaque; @@ -474,7 +473,6 @@ typedef struct { int is_tail_call; } VMCallInfo; - static inline objhdr_t objhdr_set_s (objhdr_t h, bool s) { return s ? (h | OBJHDR_S_MASK) : (h & ~OBJHDR_S_MASK); } @@ -1564,7 +1562,6 @@ typedef struct JSFunction { JSValue name; /* function name as JSValue text */ int16_t length; /* arity: max allowed arguments (-1 = variadic) */ uint8_t kind; - uint8_t free_mark : 1; union { struct { JSContext *realm; @@ -28873,31 +28870,265 @@ char *JS_AST (JSContext *ctx, const char *source, size_t len, const char *filena Register-Based Machine Code Generator ============================================================ */ +/* Variable kinds */ +#define MACH_VAR_ARG 0 +#define MACH_VAR_LOCAL 1 +#define MACH_VAR_CLOSED 2 + +/* Variable resolution result */ +typedef enum MachVarResolution { + MACH_VAR_LOCAL_SLOT, /* variable is in current scope */ + MACH_VAR_CLOSURE, /* variable is in parent scope */ + MACH_VAR_UNBOUND /* variable not found in any scope */ +} MachVarResolution; + +typedef struct MachVarInfo { + char *name; + int slot; + int kind; +} MachVarInfo; + +typedef struct MachClosureRef { + char *name; + int parent_slot; + int depth; + int local_slot; +} MachClosureRef; + +typedef struct MachResolveResult { + MachVarResolution type; + int slot; + int depth; +} MachResolveResult; + typedef struct MachGenState { JSContext *ctx; cJSON *instructions; cJSON *data; cJSON *functions; - int next_slot; - int max_slot; + + int this_slot; /* always 0 */ int nr_args; + int nr_close_slots; /* captured from parents */ + int nr_local_slots; + int next_temp_slot; + int max_slot; + + MachVarInfo *vars; + int var_count; + int var_capacity; + + MachClosureRef *closure_refs; + int closure_ref_count; + int closure_ref_capacity; + int label_counter; int func_counter; + struct MachGenState *parent; - /* Loop labels for break/continue */ const char *loop_break; const char *loop_continue; + + int is_arrow; } MachGenState; static int mach_gen_expr (MachGenState *s, cJSON *expr); static void mach_gen_statement (MachGenState *s, cJSON *stmt); +/* Allocate a temporary slot */ static int mach_alloc_slot (MachGenState *s) { - int slot = s->next_slot++; + int slot = s->next_temp_slot++; if (slot > s->max_slot) s->max_slot = slot; return slot; } +/* Add a variable to the tracking table */ +static void mach_add_var (MachGenState *s, const char *name, int slot, int kind) { + if (s->var_count >= s->var_capacity) { + int new_cap = s->var_capacity ? s->var_capacity * 2 : 16; + s->vars = sys_realloc (s->vars, new_cap * sizeof(MachVarInfo)); + s->var_capacity = new_cap; + } + MachVarInfo *v = &s->vars[s->var_count++]; + v->name = sys_malloc (strlen (name) + 1); + strcpy (v->name, name); + v->slot = slot; + v->kind = kind; +} + +/* Find a variable in the current scope only */ +static int mach_find_var_local (MachGenState *s, const char *name) { + for (int i = 0; i < s->var_count; i++) { + if (strcmp (s->vars[i].name, name) == 0) { + return s->vars[i].slot; + } + } + return -1; +} + +/* Scan a node for var/def declarations and register them (hoisting) */ +static void mach_scan_vars (MachGenState *s, cJSON *node) { + if (!node) return; + + const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (node, "kind")); + if (!kind) return; + + /* var/def declaration - register the variable */ + if (strcmp (kind, "var") == 0 || strcmp (kind, "def") == 0) { + cJSON *left = cJSON_GetObjectItem (node, "left"); + const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name")); + if (name && mach_find_var_local (s, name) < 0) { + int slot = 1 + s->nr_args + s->nr_local_slots++; + mach_add_var (s, name, slot, MACH_VAR_LOCAL); + } + return; + } + + /* Block - scan children */ + if (strcmp (kind, "block") == 0) { + cJSON *stmts = cJSON_GetObjectItem (node, "statements"); + cJSON *child; + cJSON_ArrayForEach (child, stmts) { + mach_scan_vars (s, child); + } + return; + } + + /* If - scan then/else branches */ + if (strcmp (kind, "if") == 0) { + cJSON *then_stmts = cJSON_GetObjectItem (node, "then"); + cJSON *else_stmts = cJSON_GetObjectItem (node, "else"); + cJSON *child; + cJSON_ArrayForEach (child, then_stmts) { + mach_scan_vars (s, child); + } + if (else_stmts) { + cJSON_ArrayForEach (child, else_stmts) { + mach_scan_vars (s, child); + } + } + return; + } + + /* While - scan body */ + if (strcmp (kind, "while") == 0) { + cJSON *stmts = cJSON_GetObjectItem (node, "statements"); + cJSON *child; + cJSON_ArrayForEach (child, stmts) { + mach_scan_vars (s, child); + } + return; + } + + /* Do-while - scan body */ + if (strcmp (kind, "do") == 0) { + cJSON *stmts = cJSON_GetObjectItem (node, "statements"); + cJSON *child; + cJSON_ArrayForEach (child, stmts) { + mach_scan_vars (s, child); + } + return; + } + + /* For - scan init and body */ + if (strcmp (kind, "for") == 0) { + cJSON *init = cJSON_GetObjectItem (node, "init"); + cJSON *stmts = cJSON_GetObjectItem (node, "statements"); + if (init) mach_scan_vars (s, init); + cJSON *child; + cJSON_ArrayForEach (child, stmts) { + mach_scan_vars (s, child); + } + return; + } + + /* Switch - scan case bodies */ + if (strcmp (kind, "switch") == 0) { + cJSON *cases = cJSON_GetObjectItem (node, "cases"); + cJSON *case_node; + cJSON_ArrayForEach (case_node, cases) { + cJSON *case_stmts = cJSON_GetObjectItem (case_node, "statements"); + cJSON *child; + cJSON_ArrayForEach (child, case_stmts) { + mach_scan_vars (s, child); + } + } + return; + } + + /* Try-catch-finally - scan all blocks */ + if (strcmp (kind, "try") == 0) { + cJSON *try_stmts = cJSON_GetObjectItem (node, "statements"); + cJSON *catch_node = cJSON_GetObjectItem (node, "catch"); + cJSON *finally_node = cJSON_GetObjectItem (node, "finally"); + cJSON *child; + cJSON_ArrayForEach (child, try_stmts) { + mach_scan_vars (s, child); + } + if (catch_node) { + cJSON *catch_stmts = cJSON_GetObjectItem (catch_node, "statements"); + cJSON_ArrayForEach (child, catch_stmts) { + mach_scan_vars (s, child); + } + } + if (finally_node) { + cJSON *finally_stmts = cJSON_GetObjectItem (finally_node, "statements"); + cJSON_ArrayForEach (child, finally_stmts) { + mach_scan_vars (s, child); + } + } + return; + } + + /* NOTE: Do NOT recurse into nested function nodes - they have their own scope */ +} + +/* Resolve a variable - returns resolution type and slot/depth info */ +static MachResolveResult mach_resolve_var (MachGenState *s, const char *name) { + MachResolveResult result = {MACH_VAR_UNBOUND, -1, 0}; + + /* Check current scope */ + int local_slot = mach_find_var_local (s, name); + if (local_slot >= 0) { + result.type = MACH_VAR_LOCAL_SLOT; + result.slot = local_slot; + return result; + } + + /* Check parent scopes */ + MachGenState *parent = s->parent; + int depth = 1; + while (parent) { + int parent_slot = mach_find_var_local (parent, name); + if (parent_slot >= 0) { + /* Found in parent scope - add closure ref */ + if (s->closure_ref_count >= s->closure_ref_capacity) { + int new_cap = s->closure_ref_capacity ? s->closure_ref_capacity * 2 : 8; + s->closure_refs = sys_realloc (s->closure_refs, new_cap * sizeof(MachClosureRef)); + s->closure_ref_capacity = new_cap; + } + int close_slot = 1 + s->nr_args + s->nr_close_slots; + MachClosureRef *ref = &s->closure_refs[s->closure_ref_count++]; + ref->name = sys_malloc (strlen (name) + 1); + strcpy (ref->name, name); + ref->parent_slot = parent_slot; + ref->depth = depth; + ref->local_slot = close_slot; + s->nr_close_slots++; + + result.type = MACH_VAR_CLOSURE; + result.slot = parent_slot; + result.depth = depth; + return result; + } + parent = parent->parent; + depth++; + } + + /* Not found - unbound */ + return result; +} + static char *mach_gen_label (MachGenState *s, const char *prefix) { char *label = sys_malloc (64); snprintf (label, 64, "%s_%d", prefix, s->label_counter++); @@ -28909,6 +29140,7 @@ static void mach_emit_label (MachGenState *s, const char *label) { cJSON_AddItemToArray (s->instructions, item); } +/* Emit instruction with string opcode */ static void mach_emit_0 (MachGenState *s, const char *op) { cJSON *instr = cJSON_CreateArray (); cJSON_AddItemToArray (instr, cJSON_CreateString (op)); @@ -28939,38 +29171,45 @@ static void mach_emit_3 (MachGenState *s, const char *op, int a, int b, int c) { cJSON_AddItemToArray (s->instructions, instr); } +static void mach_emit_4 (MachGenState *s, const char *op, int a, int b, int c, int d) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString (op)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (a)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (b)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (c)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (d)); + cJSON_AddItemToArray (s->instructions, instr); +} + +/* Emit number constant - inline literal */ static void mach_emit_const_num (MachGenState *s, int dest, double val) { cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("const")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("access")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (val)); cJSON_AddItemToArray (s->instructions, instr); } +/* Emit string constant - inline literal */ static void mach_emit_const_str (MachGenState *s, int dest, const char *val) { cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("const")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("access")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); - cJSON_AddItemToArray (instr, cJSON_CreateString (val)); + cJSON_AddItemToArray (instr, cJSON_CreateString (val ? val : "")); cJSON_AddItemToArray (s->instructions, instr); } +/* Emit boolean constant */ static void mach_emit_const_bool (MachGenState *s, int dest, int val) { - cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("const")); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); - cJSON_AddItemToArray (instr, val ? cJSON_CreateTrue () : cJSON_CreateFalse ()); - cJSON_AddItemToArray (s->instructions, instr); + mach_emit_1 (s, val ? "true" : "false", dest); } +/* Emit null constant */ static void mach_emit_const_null (MachGenState *s, int dest) { - cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("const")); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); - cJSON_AddItemToArray (instr, cJSON_CreateNull ()); - cJSON_AddItemToArray (s->instructions, instr); + mach_emit_1 (s, "null", dest); } +/* Emit jump with label */ static void mach_emit_jump (MachGenState *s, const char *label) { cJSON *instr = cJSON_CreateArray (); cJSON_AddItemToArray (instr, cJSON_CreateString ("jump")); @@ -28978,6 +29217,7 @@ static void mach_emit_jump (MachGenState *s, const char *label) { cJSON_AddItemToArray (s->instructions, instr); } +/* Emit conditional jump */ static void mach_emit_jump_cond (MachGenState *s, const char *op, int slot, const char *label) { cJSON *instr = cJSON_CreateArray (); cJSON_AddItemToArray (instr, cJSON_CreateString (op)); @@ -28986,81 +29226,150 @@ static void mach_emit_jump_cond (MachGenState *s, const char *op, int slot, cons cJSON_AddItemToArray (s->instructions, instr); } +/* Emit get variable - resolves variable and emits appropriate opcode */ static void mach_emit_get_var (MachGenState *s, int dest, const char *name) { - cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("get_var")); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); - cJSON_AddItemToArray (instr, cJSON_CreateString (name)); - cJSON_AddItemToArray (s->instructions, instr); + MachResolveResult res = mach_resolve_var (s, name); + if (res.type == MACH_VAR_LOCAL_SLOT) { + /* Local variable - emit move */ + mach_emit_2 (s, "move", dest, res.slot); + } else if (res.type == MACH_VAR_CLOSURE) { + /* Closure variable - emit get */ + mach_emit_3 (s, "get", dest, res.slot, res.depth); + } else { + /* Unbound variable - emit cap_name for linker to resolve */ + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("cap_name")); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); + cJSON_AddItemToArray (instr, cJSON_CreateString (name)); + cJSON_AddItemToArray (s->instructions, instr); + } } +/* Emit set variable - resolves variable and emits appropriate opcode */ static void mach_emit_set_var (MachGenState *s, const char *name, int slot) { - cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("set_var")); - cJSON_AddItemToArray (instr, cJSON_CreateString (name)); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (slot)); - cJSON_AddItemToArray (s->instructions, instr); + int local_slot = mach_find_var_local (s, name); + if (local_slot >= 0) { + /* Local variable - emit move */ + mach_emit_2 (s, "move", local_slot, slot); + } else { + /* Try to resolve in parent scopes for closure write */ + MachResolveResult res = mach_resolve_var (s, name); + if (res.type == MACH_VAR_CLOSURE) { + /* Closure variable - emit put */ + mach_emit_3 (s, "put", slot, res.slot, res.depth); + } else { + /* Unbound - emit set_var with name for linker to resolve */ + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString ("set_var")); + cJSON_AddItemToArray (instr, cJSON_CreateString (name)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (slot)); + cJSON_AddItemToArray (s->instructions, instr); + } + } } +/* Emit property access with inline key */ static void mach_emit_get_prop (MachGenState *s, int dest, int obj, const char *prop) { cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("get_prop")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("load_prop")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); cJSON_AddItemToArray (s->instructions, instr); } +/* Emit property store with inline key */ static void mach_emit_set_prop (MachGenState *s, int obj, const char *prop, int val) { cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("set_prop")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("store_prop")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); - cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (val)); + cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); cJSON_AddItemToArray (s->instructions, instr); } +/* Emit element access */ static void mach_emit_get_elem (MachGenState *s, int dest, int obj, int idx) { - cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("get_elem")); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (idx)); - cJSON_AddItemToArray (s->instructions, instr); + mach_emit_3 (s, "load_idx", dest, obj, idx); } +/* Emit element store */ static void mach_emit_set_elem (MachGenState *s, int obj, int idx, int val) { - cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("set_elem")); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (idx)); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (val)); - cJSON_AddItemToArray (s->instructions, instr); + mach_emit_3 (s, "store_idx", obj, idx, val); } +/* Emit function call using frame/arg/invoke sequence */ static void mach_emit_call (MachGenState *s, int dest, int func_slot, cJSON *args) { - cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("call")); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (func_slot)); + int argc = cJSON_GetArraySize (args); + int frame_slot = mach_alloc_slot (s); + + /* frame frame_slot, func_slot, argc */ + mach_emit_3 (s, "frame", frame_slot, func_slot, argc); + + /* set_this frame_slot, null (or global for non-method calls) */ + int null_slot = mach_alloc_slot (s); + mach_emit_1 (s, "null", null_slot); + mach_emit_2 (s, "set_this", frame_slot, null_slot); + + /* arg frame_slot, arg_index, val_slot for each arg */ + int arg_idx = 1; cJSON *arg; cJSON_ArrayForEach (arg, args) { - cJSON_AddItemToArray (instr, cJSON_CreateNumber (arg->valueint)); + mach_emit_3 (s, "arg", frame_slot, arg_idx++, arg->valueint); } - cJSON_AddItemToArray (s->instructions, instr); + + /* invoke frame_slot, ret_slot */ + mach_emit_2 (s, "invoke", frame_slot, dest); } +/* Emit method call using frame/arg/invoke sequence */ static void mach_emit_call_method (MachGenState *s, int dest, int obj, const char *prop, cJSON *args) { - cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("call_method")); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); - cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); + /* First get the method */ + int func_slot = mach_alloc_slot (s); + mach_emit_get_prop (s, func_slot, obj, prop); + + int argc = cJSON_GetArraySize (args); + int frame_slot = mach_alloc_slot (s); + + /* frame frame_slot, func_slot, argc */ + mach_emit_3 (s, "frame", frame_slot, func_slot, argc); + + /* set_this frame_slot, obj */ + mach_emit_2 (s, "set_this", frame_slot, obj); + + /* arg frame_slot, arg_index, val_slot for each arg */ + int arg_idx = 1; cJSON *arg; cJSON_ArrayForEach (arg, args) { - cJSON_AddItemToArray (instr, cJSON_CreateNumber (arg->valueint)); + mach_emit_3 (s, "arg", frame_slot, arg_idx++, arg->valueint); } - cJSON_AddItemToArray (s->instructions, instr); + + /* invoke frame_slot, ret_slot */ + mach_emit_2 (s, "invoke", frame_slot, dest); +} + +/* Map JS operator to opcode string */ +static const char *mach_binop_to_string (const char *kind) { + if (strcmp (kind, "+") == 0) return "add"; + if (strcmp (kind, "-") == 0) return "sub"; + if (strcmp (kind, "*") == 0) return "mul"; + if (strcmp (kind, "/") == 0) return "div"; + if (strcmp (kind, "%") == 0) return "mod"; + if (strcmp (kind, "**") == 0) return "pow"; + if (strcmp (kind, "&") == 0) return "bitand"; + if (strcmp (kind, "|") == 0) return "bitor"; + if (strcmp (kind, "^") == 0) return "bitxor"; + if (strcmp (kind, "<<") == 0) return "shl"; + if (strcmp (kind, ">>") == 0) return "shr"; + if (strcmp (kind, ">>>") == 0) return "ushr"; + if (strcmp (kind, "==") == 0 || strcmp (kind, "===") == 0) return "eq"; + if (strcmp (kind, "!=") == 0 || strcmp (kind, "!==") == 0) return "ne"; + if (strcmp (kind, "<") == 0) return "lt"; + if (strcmp (kind, "<=") == 0) return "le"; + if (strcmp (kind, ">") == 0) return "gt"; + if (strcmp (kind, ">=") == 0) return "ge"; + if (strcmp (kind, "in") == 0) return "in"; + return "add"; } /* Generate code for binary operators */ @@ -29114,28 +29423,7 @@ static int mach_gen_binary (MachGenState *s, cJSON *node) { int right_slot = mach_gen_expr (s, right); int dest = mach_alloc_slot (s); - const char *op = NULL; - if (strcmp (kind, "+") == 0) op = "add"; - else if (strcmp (kind, "-") == 0) op = "sub"; - else if (strcmp (kind, "*") == 0) op = "mul"; - else if (strcmp (kind, "/") == 0) op = "div"; - else if (strcmp (kind, "%") == 0) op = "mod"; - else if (strcmp (kind, "**") == 0) op = "pow"; - else if (strcmp (kind, "&") == 0) op = "bitand"; - else if (strcmp (kind, "|") == 0) op = "bitor"; - else if (strcmp (kind, "^") == 0) op = "bitxor"; - else if (strcmp (kind, "<<") == 0) op = "shl"; - else if (strcmp (kind, ">>") == 0) op = "shr"; - else if (strcmp (kind, ">>>") == 0) op = "ushr"; - else if (strcmp (kind, "==") == 0 || strcmp (kind, "===") == 0) op = "eq"; - else if (strcmp (kind, "!=") == 0 || strcmp (kind, "!==") == 0) op = "ne"; - else if (strcmp (kind, "<") == 0) op = "lt"; - else if (strcmp (kind, "<=") == 0) op = "le"; - else if (strcmp (kind, ">") == 0) op = "gt"; - else if (strcmp (kind, ">=") == 0) op = "ge"; - else if (strcmp (kind, "in") == 0) op = "in"; - else op = kind; - + const char *op = mach_binop_to_string (kind); mach_emit_3 (s, op, dest, left_slot, right_slot); return dest; } @@ -29272,9 +29560,8 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr) { } if (strcmp (kind, "this") == 0) { - int slot = mach_alloc_slot (s); - mach_emit_1 (s, "this", slot); - return slot; + /* this is always slot 0 */ + return s->this_slot; } /* Variable reference */ @@ -29432,12 +29719,7 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr) { cJSON *idx = cJSON_GetObjectItem (operand, "right"); int obj_slot = mach_gen_expr (s, obj); int idx_slot = mach_gen_expr (s, idx); - cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("delete_elem")); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (slot)); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj_slot)); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (idx_slot)); - cJSON_AddItemToArray (s->instructions, instr); + mach_emit_3 (s, "delete_idx", slot, obj_slot, idx_slot); } else { mach_emit_const_bool (s, slot, 1); } @@ -29496,7 +29778,7 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr) { int dest = mach_alloc_slot (s); cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("array")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("mkarray")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (count)); cJSON *el; @@ -29515,7 +29797,7 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr) { int dest = mach_alloc_slot (s); cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("record")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("mkrecord")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (count)); @@ -29532,9 +29814,9 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr) { const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (key, "value")); cJSON_AddItemToArray (instr, cJSON_CreateString (name ? name : "")); } else { - /* Computed property - emit the key slot */ + /* Computed property - emit the key slot (negative to distinguish) */ int key_slot = mach_gen_expr (s, key); - cJSON_AddItemToArray (instr, cJSON_CreateNumber (key_slot)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (-1 - key_slot)); } int val_slot = mach_gen_expr (s, val); @@ -29550,8 +29832,14 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr) { cJSON *func = mach_gen_function (s, expr); cJSON_AddItemToArray (s->functions, func); + /* Check if it's an arrow function */ + cJSON *is_arrow = cJSON_GetObjectItem (expr, "arrow"); int dest = mach_alloc_slot (s); - mach_emit_2 (s, "function", dest, func_id); + if (is_arrow && cJSON_IsTrue (is_arrow)) { + mach_emit_2 (s, "mkfunc_arrow", dest, func_id); + } else { + mach_emit_2 (s, "mkfunc", dest, func_id); + } return dest; } @@ -29563,11 +29851,13 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr) { int func_slot = mach_gen_expr (s, callee); cJSON *arg_slots = cJSON_CreateArray (); + int argc = 0; if (args_list) { cJSON *arg; cJSON_ArrayForEach (arg, args_list) { int slot = mach_gen_expr (s, arg); cJSON_AddItemToArray (arg_slots, cJSON_CreateNumber (slot)); + argc++; } } @@ -29576,6 +29866,7 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr) { cJSON_AddItemToArray (instr, cJSON_CreateString ("new")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (func_slot)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (argc)); cJSON *el; cJSON_ArrayForEach (el, arg_slots) { cJSON_AddItemToArray (instr, cJSON_CreateNumber (el->valueint)); @@ -29607,13 +29898,20 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) { cJSON *right = cJSON_GetObjectItem (stmt, "right"); const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name")); + /* Variable should already be registered from scan phase */ + int local_slot = mach_find_var_local (s, name); + if (right) { int val_slot = mach_gen_expr (s, right); - mach_emit_set_var (s, name, val_slot); - } else { - int slot = mach_alloc_slot (s); - mach_emit_const_null (s, slot); - mach_emit_set_var (s, name, slot); + if (local_slot >= 0) { + mach_emit_2 (s, "move", local_slot, val_slot); + } else { + /* Fallback for unregistered (shouldn't happen) */ + mach_emit_set_var (s, name, val_slot); + } + } else if (local_slot >= 0) { + /* Initialize to null */ + mach_emit_const_null (s, local_slot); } return; } @@ -29833,7 +30131,6 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) { /* First pass: generate case tests and jumps */ cJSON *case_node; - int case_num = 0; cJSON_ArrayForEach (case_node, cases) { const char *case_kind = cJSON_GetStringValue (cJSON_GetObjectItem (case_node, "kind")); if (strcmp (case_kind, "default") == 0) { @@ -29849,7 +30146,6 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) { cJSON_AddStringToObject (case_node, "_label", case_label); sys_free (case_label); } - case_num++; } /* Jump to default or end */ @@ -29955,7 +30251,7 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) { cJSON_AddItemToArray (s->functions, func); int dest = mach_alloc_slot (s); - mach_emit_2 (s, "function", dest, func_id); + mach_emit_2 (s, "mkfunc", dest, func_id); mach_emit_set_var (s, name, dest); } return; @@ -29993,16 +30289,36 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) { cJSON_AddStringToObject (result, "name", ""); } - /* Parameters - allocate slots 0..nr_args-1 */ + /* Check if arrow function */ + cJSON *is_arrow = cJSON_GetObjectItem (func_node, "arrow"); + s.is_arrow = is_arrow && cJSON_IsTrue (is_arrow); + + /* Parameters - slot 0 is this, slots 1..nr_args are args */ cJSON *params = cJSON_GetObjectItem (func_node, "list"); s.nr_args = cJSON_GetArraySize (params); - s.next_slot = s.nr_args; - s.max_slot = s.nr_args; + s.this_slot = 0; + s.nr_close_slots = 0; + s.nr_local_slots = 0; + + /* Register parameters as variables (slots 1..nr_args) */ + int param_slot = 1; + cJSON *param; + cJSON_ArrayForEach (param, params) { + const char *param_name = cJSON_GetStringValue (cJSON_GetObjectItem (param, "name")); + if (param_name) { + mach_add_var (&s, param_name, param_slot, MACH_VAR_ARG); + param_slot++; + } + } + + /* Temps start after this (0), args (1..nr_args), closed vars, and locals */ + /* For now start at 1 + nr_args, will be adjusted after scanning for vars */ + s.next_temp_slot = 1 + s.nr_args; + s.max_slot = 1 + s.nr_args; cJSON_AddNumberToObject (result, "nr_args", s.nr_args); cJSON *vars = cJSON_AddArrayToObject (result, "vars"); - cJSON *param; cJSON_ArrayForEach (param, params) { const char *param_name = cJSON_GetStringValue (cJSON_GetObjectItem (param, "name")); if (param_name) { @@ -30010,16 +30326,26 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) { } } - /* Generate body */ + /* Scan for var/def declarations and register them (hoisting) */ cJSON *stmts = cJSON_GetObjectItem (func_node, "statements"); cJSON *stmt; + cJSON_ArrayForEach (stmt, stmts) { + mach_scan_vars (&s, stmt); + } + + /* Adjust temp slot start after all locals are allocated */ + s.next_temp_slot = 1 + s.nr_args + s.nr_local_slots; + if (s.next_temp_slot > s.max_slot) s.max_slot = s.next_temp_slot; + + /* Generate body */ cJSON_ArrayForEach (stmt, stmts) { mach_gen_statement (&s, stmt); } - /* Add implicit return null */ + /* Add implicit return */ mach_emit_0 (&s, "return_undef"); + cJSON_AddNumberToObject (result, "nr_close_slots", s.nr_close_slots); cJSON_AddNumberToObject (result, "nr_slots", s.max_slot + 1); cJSON_AddItemToObject (result, "labels", cJSON_CreateObject ()); cJSON_AddItemToObject (result, "instructions", s.instructions); @@ -30028,6 +30354,16 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) { parent->label_counter = s.label_counter; parent->func_counter = s.func_counter; + /* Free variable tracking arrays */ + for (int i = 0; i < s.var_count; i++) { + sys_free (s.vars[i].name); + } + if (s.vars) sys_free (s.vars); + for (int i = 0; i < s.closure_ref_count; i++) { + sys_free (s.closure_refs[i].name); + } + if (s.closure_refs) sys_free (s.closure_refs); + return result; } @@ -30045,7 +30381,27 @@ static cJSON *mach_gen_program (MachGenState *s, cJSON *ast) { /* Functions array */ s->functions = cJSON_AddArrayToObject (result, "functions"); - /* Process top-level function declarations first */ + /* Main scope setup */ + s->this_slot = 0; + s->nr_args = 0; + s->nr_close_slots = 0; + s->nr_local_slots = 0; + s->next_temp_slot = 1; /* slot 0 is this */ + s->max_slot = 1; + + /* Scan main scope for var/def declarations FIRST (hoisting) */ + /* This must happen before compiling functions so closure resolution works */ + cJSON *statements = cJSON_GetObjectItem (ast, "statements"); + cJSON *stmt; + cJSON_ArrayForEach (stmt, statements) { + mach_scan_vars (s, stmt); + } + + /* Adjust temp slot start after all locals are allocated */ + s->next_temp_slot = 1 + s->nr_local_slots; + if (s->next_temp_slot > s->max_slot) s->max_slot = s->next_temp_slot; + + /* Process top-level function declarations */ cJSON *functions = cJSON_GetObjectItem (ast, "functions"); cJSON *func_node; cJSON_ArrayForEach (func_node, functions) { @@ -30057,20 +30413,20 @@ static cJSON *mach_gen_program (MachGenState *s, cJSON *ast) { cJSON_AddItemToArray (s->functions, func); int dest = mach_alloc_slot (s); - mach_emit_2 (s, "function", dest, func_id); + mach_emit_2 (s, "mkfunc", dest, func_id); mach_emit_set_var (s, name, dest); } } /* Generate main code */ - cJSON *statements = cJSON_GetObjectItem (ast, "statements"); - cJSON *stmt; cJSON_ArrayForEach (stmt, statements) { mach_gen_statement (s, stmt); } /* Main section */ cJSON *main_obj = cJSON_CreateObject (); + cJSON_AddNumberToObject (main_obj, "nr_args", 0); + cJSON_AddNumberToObject (main_obj, "nr_close_slots", 0); cJSON_AddNumberToObject (main_obj, "nr_slots", s->max_slot + 1); cJSON_AddItemToObject (main_obj, "vars", cJSON_CreateArray ()); cJSON_AddItemToObject (main_obj, "labels", cJSON_CreateObject ()); @@ -30091,6 +30447,16 @@ char *JS_Mach (JSContext *ctx, const char *ast_json) { cJSON *mach = mach_gen_program (&s, ast); cJSON_Delete (ast); + /* Free any allocated variable tracking */ + for (int i = 0; i < s.var_count; i++) { + sys_free (s.vars[i].name); + } + if (s.vars) sys_free (s.vars); + for (int i = 0; i < s.closure_ref_count; i++) { + sys_free (s.closure_refs[i].name); + } + if (s.closure_refs) sys_free (s.closure_refs); + if (!mach) return NULL; char *json = cJSON_Print (mach);