fix closures

This commit is contained in:
2026-02-05 02:07:18 -06:00
parent c3dc27eac6
commit 08559234c4

View File

@@ -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", "<anonymous>");
}
/* 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);