fix closures
This commit is contained in:
590
source/quickjs.c
590
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", "<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);
|
||||
|
||||
Reference in New Issue
Block a user