stack traces

This commit is contained in:
2026-02-06 20:44:38 -06:00
parent e2bc5948c1
commit 5b65c64fe5
3 changed files with 256 additions and 14 deletions

View File

@@ -574,7 +574,21 @@ int cell_init(int argc, char **argv)
if (JS_IsException(result)) { if (JS_IsException(result)) {
JSValue exc = JS_GetException(ctx); JSValue exc = JS_GetException(ctx);
const char *str = JS_ToCString(ctx, exc); const char *str = JS_ToCString(ctx, exc);
if (str) { printf("Exception: %s\n", str); JS_FreeCString(ctx, str); } if (str) { printf("Error: %s\n", str); JS_FreeCString(ctx, str); }
cJSON *stack = JS_GetStack(ctx);
if (stack) {
int n = cJSON_GetArraySize(stack);
for (int i = 0; i < n; i++) {
cJSON *fr = cJSON_GetArrayItem(stack, i);
const char *fn = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "function"));
const char *file = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "file"));
int line = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "line"));
int col = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "column"));
printf(" at %s (%s:%d:%d)\n", fn ? fn : "<anonymous>", file ? file : "<unknown>", line, col);
}
cJSON_Delete(stack);
}
JS_FreeValue(ctx, exc);
} else if (!JS_IsNull(result)) { } else if (!JS_IsNull(result)) {
const char *str = JS_ToCString(ctx, result); const char *str = JS_ToCString(ctx, result);
if (str) { printf("%s\n", str); JS_FreeCString(ctx, str); } if (str) { printf("%s\n", str); JS_FreeCString(ctx, str); }
@@ -716,6 +730,20 @@ int cell_init(int argc, char **argv)
printf("Error: %s\n", err_str); printf("Error: %s\n", err_str);
JS_FreeCString(ctx, err_str); JS_FreeCString(ctx, err_str);
} }
cJSON *stack = JS_GetStack(ctx);
if (stack) {
int n = cJSON_GetArraySize(stack);
for (int i = 0; i < n; i++) {
cJSON *fr = cJSON_GetArrayItem(stack, i);
const char *fn = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "function"));
const char *file = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "file"));
int line = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "line"));
int col = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "column"));
printf(" at %s (%s:%d:%d)\n", fn ? fn : "<anonymous>", file ? file : "<unknown>", line, col);
}
cJSON_Delete(stack);
}
JS_FreeValue(ctx, exc);
exit_code = 1; exit_code = 1;
} else if (!JS_IsNull(result)) { } else if (!JS_IsNull(result)) {
const char *str = JS_ToCString(ctx, result); const char *str = JS_ToCString(ctx, result);

View File

@@ -390,6 +390,10 @@ struct JSRuntime {
/* Interrupt handler for checking execution limits */ /* Interrupt handler for checking execution limits */
JSInterruptHandler *interrupt_handler; JSInterruptHandler *interrupt_handler;
void *interrupt_opaque; void *interrupt_opaque;
/* Register VM frame tracking for stack traces */
void *current_register_frame; /* JSFrameRegister* at exception time */
uint32_t current_register_pc; /* PC at exception time */
}; };
struct JSClass { struct JSClass {
@@ -462,6 +466,8 @@ typedef struct JSFrameRegister {
typedef uint32_t MachInstr32; typedef uint32_t MachInstr32;
typedef struct { uint16_t line; uint16_t col; } MachLineEntry;
/* Encoding macros */ /* Encoding macros */
#define MACH_ABC(op, a, b, c) ((uint32_t)(op) | ((uint32_t)(a)<<8) | ((uint32_t)(b)<<16) | ((uint32_t)(c)<<24)) #define MACH_ABC(op, a, b, c) ((uint32_t)(op) | ((uint32_t)(a)<<8) | ((uint32_t)(b)<<16) | ((uint32_t)(c)<<24))
#define MACH_ABx(op, a, bx) ((uint32_t)(op) | ((uint32_t)(a)<<8) | ((uint32_t)(bx)<<16)) #define MACH_ABx(op, a, bx) ((uint32_t)(op) | ((uint32_t)(a)<<8) | ((uint32_t)(bx)<<16))
@@ -633,6 +639,9 @@ typedef struct JSCodeRegister {
/* Debug info */ /* Debug info */
JSValue name; /* function name (stone text) */ JSValue name; /* function name (stone text) */
MachLineEntry *line_table; /* [instr_count], parallel to instructions[] */
char *filename_cstr; /* plain C string for stack trace (js_malloc_rt) */
char *name_cstr; /* plain C string for stack trace (js_malloc_rt) */
} JSCodeRegister; } JSCodeRegister;
/* Pre-parsed MCODE for a single function (off-heap, never GC'd). /* Pre-parsed MCODE for a single function (off-heap, never GC'd).
@@ -652,6 +661,9 @@ typedef struct JSMCode {
uint32_t func_count; uint32_t func_count;
/* Keep root cJSON alive (owns all the cJSON nodes instrs[] point into) */ /* Keep root cJSON alive (owns all the cJSON nodes instrs[] point into) */
cJSON *json_root; cJSON *json_root;
MachLineEntry *line_table; /* [instr_count], parallel to instrs[] */
const char *name; /* function name (points into cJSON tree) */
const char *filename; /* source filename (points into cJSON tree) */
} JSMCode; } JSMCode;
/* Frame for closures - used by link-time relocation model where closures /* Frame for closures - used by link-time relocation model where closures
@@ -2067,6 +2079,7 @@ static JSValue js_cell_number_remainder (JSContext *ctx, JSValue this_val, int a
static JSValue js_cell_object (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_object (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_print (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_print (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
cJSON *JS_GetStack(JSContext *ctx);
JSValue JS_ThrowOutOfMemory (JSContext *ctx); JSValue JS_ThrowOutOfMemory (JSContext *ctx);
static JSValue JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx); static JSValue JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx);
@@ -22898,6 +22911,23 @@ static JSValue js_print (JSContext *ctx, JSValue this_val, int argc, JSValue *ar
return JS_NULL; return JS_NULL;
} }
static JSValue js_stacktrace (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
cJSON *stack = JS_GetStack(ctx);
if (stack) {
int n = cJSON_GetArraySize(stack);
for (int i = 0; i < n; i++) {
cJSON *fr = cJSON_GetArrayItem(stack, i);
const char *fn = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "function"));
const char *file = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "file"));
int line = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "line"));
int col = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "column"));
printf(" at %s (%s:%d:%d)\n", fn ? fn : "<anonymous>", file ? file : "<unknown>", line, col);
}
cJSON_Delete(stack);
}
return JS_NULL;
}
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
* Bytecode dump function (always available, for debugging) * Bytecode dump function (always available, for debugging)
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
@@ -26041,6 +26071,7 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
/* I/O functions */ /* I/O functions */
js_set_global_cfunc(ctx, "print", js_print, -1); /* variadic: length < 0 means no arg limit */ js_set_global_cfunc(ctx, "print", js_print, -1); /* variadic: length < 0 means no arg limit */
js_set_global_cfunc(ctx, "stacktrace", js_stacktrace, 0);
} }
} }
@@ -30769,6 +30800,8 @@ typedef struct MachCode {
struct MachCode **functions; struct MachCode **functions;
char *name; /* owned C string, or NULL */ char *name; /* owned C string, or NULL */
MachLineEntry *line_table; /* [instr_count], parallel to instructions[] */
char *filename; /* source filename (sys_malloc'd) */
} MachCode; } MachCode;
/* ---- Compiler state ---- */ /* ---- Compiler state ---- */
@@ -30815,6 +30848,12 @@ typedef struct MachCompState {
/* Error tracking */ /* Error tracking */
int has_error; int has_error;
/* Line tracking for debug info */
int cur_line, cur_col;
MachLineEntry *line_info; /* growable, parallel to code[] */
int line_capacity;
const char *filename; /* pointer into AST cJSON (not owned) */
} MachCompState; } MachCompState;
/* Forward declarations */ /* Forward declarations */
@@ -30823,12 +30862,25 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt);
/* ---- Compiler helpers ---- */ /* ---- Compiler helpers ---- */
static void mach_set_pos(MachCompState *cs, cJSON *node) {
cJSON *r = cJSON_GetObjectItem(node, "from_row");
cJSON *c = cJSON_GetObjectItem(node, "from_column");
if (r) cs->cur_line = (int)r->valuedouble + 1;
if (c) cs->cur_col = (int)c->valuedouble + 1;
}
static void mach_emit(MachCompState *cs, MachInstr32 instr) { static void mach_emit(MachCompState *cs, MachInstr32 instr) {
if (cs->code_count >= cs->code_capacity) { if (cs->code_count >= cs->code_capacity) {
int new_cap = cs->code_capacity ? cs->code_capacity * 2 : 64; int new_cap = cs->code_capacity ? cs->code_capacity * 2 : 64;
cs->code = sys_realloc(cs->code, new_cap * sizeof(MachInstr32)); cs->code = sys_realloc(cs->code, new_cap * sizeof(MachInstr32));
cs->code_capacity = new_cap; cs->code_capacity = new_cap;
} }
if (cs->code_count >= cs->line_capacity) {
int new_cap = cs->line_capacity ? cs->line_capacity * 2 : 64;
cs->line_info = sys_realloc(cs->line_info, new_cap * sizeof(MachLineEntry));
cs->line_capacity = new_cap;
}
cs->line_info[cs->code_count] = (MachLineEntry){cs->cur_line, cs->cur_col};
cs->code[cs->code_count++] = instr; cs->code[cs->code_count++] = instr;
} }
@@ -30997,6 +31049,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
return dest; return dest;
} }
mach_set_pos(cs, node);
const char *kind = cJSON_GetStringValue(cJSON_GetObjectItem(node, "kind")); const char *kind = cJSON_GetStringValue(cJSON_GetObjectItem(node, "kind"));
if (!kind) { if (!kind) {
if (dest < 0) dest = mach_reserve_reg(cs); if (dest < 0) dest = mach_reserve_reg(cs);
@@ -31458,6 +31511,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
MachCompState child = {0}; MachCompState child = {0};
child.parent = cs; child.parent = cs;
child.scopes = cs->scopes; child.scopes = cs->scopes;
child.filename = cs->filename;
child.freereg = 1; /* slot 0 = this */ child.freereg = 1; /* slot 0 = this */
/* Read function_nr from AST node */ /* Read function_nr from AST node */
@@ -31515,6 +31569,8 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
fn_code->cpool = child.cpool; fn_code->cpool = child.cpool;
fn_code->func_count = child.func_count; fn_code->func_count = child.func_count;
fn_code->functions = child.functions; fn_code->functions = child.functions;
fn_code->line_table = child.line_info;
fn_code->filename = cs->filename ? strdup(cs->filename) : NULL;
cJSON *fname = cJSON_GetObjectItem(node, "name"); cJSON *fname = cJSON_GetObjectItem(node, "name");
if (fname && cJSON_IsString(fname)) { if (fname && cJSON_IsString(fname)) {
@@ -31545,6 +31601,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
if (!stmt) return; if (!stmt) return;
mach_set_pos(cs, stmt);
const char *kind = cJSON_GetStringValue(cJSON_GetObjectItem(stmt, "kind")); const char *kind = cJSON_GetStringValue(cJSON_GetObjectItem(stmt, "kind"));
if (!kind) return; if (!kind) return;
@@ -31816,6 +31873,9 @@ static MachCode *mach_compile_program(MachCompState *cs, cJSON *ast) {
cs->scopes = cJSON_GetObjectItem(ast, "scopes"); cs->scopes = cJSON_GetObjectItem(ast, "scopes");
cs->function_nr = 0; cs->function_nr = 0;
/* Extract filename for debug info */
cs->filename = cJSON_GetStringValue(cJSON_GetObjectItem(ast, "filename"));
/* Scan scope record for declarations */ /* Scan scope record for declarations */
mach_scan_scope(cs); mach_scan_scope(cs);
@@ -31860,6 +31920,8 @@ static MachCode *mach_compile_program(MachCompState *cs, cJSON *ast) {
code->func_count = cs->func_count; code->func_count = cs->func_count;
code->functions = cs->functions; code->functions = cs->functions;
code->name = NULL; code->name = NULL;
code->line_table = cs->line_info;
code->filename = cs->filename ? strdup(cs->filename) : NULL;
return code; return code;
} }
@@ -31897,6 +31959,8 @@ void JS_FreeMachCode(MachCode *mc) {
JS_FreeMachCode(mc->functions[i]); JS_FreeMachCode(mc->functions[i]);
sys_free(mc->functions); sys_free(mc->functions);
sys_free(mc->name); sys_free(mc->name);
sys_free(mc->line_table);
sys_free(mc->filename);
sys_free(mc); sys_free(mc);
} }
@@ -31927,6 +31991,12 @@ JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env) {
/* Intern function name */ /* Intern function name */
code->name = mc->name ? js_key_new(ctx, mc->name) : JS_NULL; code->name = mc->name ? js_key_new(ctx, mc->name) : JS_NULL;
/* Transfer debug info */
code->line_table = mc->line_table;
mc->line_table = NULL;
code->filename_cstr = mc->filename ? js_strdup_rt(mc->filename) : NULL;
code->name_cstr = mc->name ? js_strdup_rt(mc->name) : NULL;
/* Link: resolve GETNAME to GETENV/GETINTRINSIC */ /* Link: resolve GETNAME to GETENV/GETINTRINSIC */
mach_link_code(ctx, code, env); mach_link_code(ctx, code, env);
@@ -31942,6 +32012,9 @@ static void js_free_code_register(JSCodeRegister *code) {
js_free_code_register(code->functions[i]); js_free_code_register(code->functions[i]);
} }
js_free_rt(code->functions); js_free_rt(code->functions);
js_free_rt(code->line_table);
js_free_rt(code->filename_cstr);
js_free_rt(code->name_cstr);
js_free_rt(code); js_free_rt(code);
} }
@@ -31972,17 +32045,31 @@ static JSFrameRegister *alloc_frame_register(JSContext *ctx, int slot_count) {
/* Create a register-based function from JSCodeRegister */ /* Create a register-based function from JSCodeRegister */
static JSValue js_new_register_function(JSContext *ctx, JSCodeRegister *code, JSValue env, JSValue outer_frame) { static JSValue js_new_register_function(JSContext *ctx, JSCodeRegister *code, JSValue env, JSValue outer_frame) {
/* Protect env and outer_frame from GC — js_mallocz can trigger
collection which moves heap objects, invalidating stack-local copies */
JSGCRef env_ref, frame_ref;
JS_PushGCRef(ctx, &env_ref);
env_ref.val = env;
JS_PushGCRef(ctx, &frame_ref);
frame_ref.val = outer_frame;
JSFunction *fn = js_mallocz(ctx, sizeof(JSFunction)); JSFunction *fn = js_mallocz(ctx, sizeof(JSFunction));
if (!fn) return JS_EXCEPTION; if (!fn) {
JS_PopGCRef(ctx, &frame_ref);
JS_PopGCRef(ctx, &env_ref);
return JS_EXCEPTION;
}
fn->header = objhdr_make(0, OBJ_FUNCTION, 0, 0, 0, 0); fn->header = objhdr_make(0, OBJ_FUNCTION, 0, 0, 0, 0);
fn->kind = JS_FUNC_KIND_REGISTER; fn->kind = JS_FUNC_KIND_REGISTER;
fn->length = code->arity; fn->length = code->arity;
fn->name = code->name; fn->name = code->name;
fn->u.reg.code = code; fn->u.reg.code = code;
fn->u.reg.env_record = env; fn->u.reg.env_record = env_ref.val;
fn->u.reg.outer_frame = outer_frame; fn->u.reg.outer_frame = frame_ref.val;
JS_PopGCRef(ctx, &frame_ref);
JS_PopGCRef(ctx, &env_ref);
return JS_MKPTR(fn); return JS_MKPTR(fn);
} }
@@ -32452,8 +32539,11 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
JSFunction *fn = JS_VALUE_GET_FUNCTION(func_val); JSFunction *fn = JS_VALUE_GET_FUNCTION(func_val);
if (fn->kind == JS_FUNC_KIND_C) { if (fn->kind == JS_FUNC_KIND_C) {
/* C function: call directly with args from consecutive slots */ /* C function: call directly with args from consecutive slots */
ctx->reg_current_frame = frame_ref.val;
ctx->rt->current_register_pc = pc > 0 ? pc - 1 : 0;
JSValue ret = js_call_c_function(ctx, func_val, JS_NULL, nargs, &frame->slots[base + 1]); JSValue ret = js_call_c_function(ctx, func_val, JS_NULL, nargs, &frame->slots[base + 1]);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
ctx->reg_current_frame = JS_NULL;
if (JS_IsException(ret)) { result = ret; goto done; } if (JS_IsException(ret)) { result = ret; goto done; }
if (nresults > 0) frame->slots[base] = ret; if (nresults > 0) frame->slots[base] = ret;
} else if (fn->kind == JS_FUNC_KIND_REGISTER) { } else if (fn->kind == JS_FUNC_KIND_REGISTER) {
@@ -32571,6 +32661,10 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
} }
done: done:
if (JS_IsException(result)) {
ctx->reg_current_frame = frame_ref.val;
ctx->rt->current_register_pc = pc > 0 ? pc - 1 : 0;
}
JS_DeleteGCRef(ctx, &frame_ref); JS_DeleteGCRef(ctx, &frame_ref);
return result; return result;
} }
@@ -32615,6 +32709,10 @@ typedef struct MachGenState {
/* Error tracking */ /* Error tracking */
cJSON *errors; cJSON *errors;
int has_error; int has_error;
/* Line tracking for debug info */
int cur_line, cur_col;
const char *filename;
} MachGenState; } MachGenState;
static int mach_gen_expr (MachGenState *s, cJSON *expr, int target); static int mach_gen_expr (MachGenState *s, cJSON *expr, int target);
@@ -32739,17 +32837,30 @@ static void mach_gen_emit_label (MachGenState *s, const char *label) {
cJSON_AddItemToArray (s->instructions, item); cJSON_AddItemToArray (s->instructions, item);
} }
static void mach_gen_set_pos (MachGenState *s, cJSON *node) {
cJSON *r = cJSON_GetObjectItem (node, "from_row");
cJSON *c = cJSON_GetObjectItem (node, "from_column");
if (r) s->cur_line = (int)r->valuedouble + 1;
if (c) s->cur_col = (int)c->valuedouble + 1;
}
static void mach_gen_add_instr (MachGenState *s, cJSON *instr) {
cJSON_AddItemToArray (instr, cJSON_CreateNumber (s->cur_line));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (s->cur_col));
cJSON_AddItemToArray (s->instructions, instr);
}
static void mach_gen_emit_0 (MachGenState *s, const char *op) { static void mach_gen_emit_0 (MachGenState *s, const char *op) {
cJSON *instr = cJSON_CreateArray (); cJSON *instr = cJSON_CreateArray ();
cJSON_AddItemToArray (instr, cJSON_CreateString (op)); cJSON_AddItemToArray (instr, cJSON_CreateString (op));
cJSON_AddItemToArray (s->instructions, instr); mach_gen_add_instr (s, instr);
} }
static void mach_gen_emit_1 (MachGenState *s, const char *op, int a) { static void mach_gen_emit_1 (MachGenState *s, const char *op, int a) {
cJSON *instr = cJSON_CreateArray (); cJSON *instr = cJSON_CreateArray ();
cJSON_AddItemToArray (instr, cJSON_CreateString (op)); cJSON_AddItemToArray (instr, cJSON_CreateString (op));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (a)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (a));
cJSON_AddItemToArray (s->instructions, instr); mach_gen_add_instr (s, instr);
} }
static void mach_gen_emit_2 (MachGenState *s, const char *op, int a, int b) { static void mach_gen_emit_2 (MachGenState *s, const char *op, int a, int b) {
@@ -32757,7 +32868,7 @@ static void mach_gen_emit_2 (MachGenState *s, const char *op, int a, int b) {
cJSON_AddItemToArray (instr, cJSON_CreateString (op)); cJSON_AddItemToArray (instr, cJSON_CreateString (op));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (a)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (a));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (b)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (b));
cJSON_AddItemToArray (s->instructions, instr); mach_gen_add_instr (s, instr);
} }
static void mach_gen_emit_3 (MachGenState *s, const char *op, int a, int b, int c) { static void mach_gen_emit_3 (MachGenState *s, const char *op, int a, int b, int c) {
@@ -32766,7 +32877,7 @@ static void mach_gen_emit_3 (MachGenState *s, const char *op, int a, int b, int
cJSON_AddItemToArray (instr, cJSON_CreateNumber (a)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (a));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (b)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (b));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (c)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (c));
cJSON_AddItemToArray (s->instructions, instr); mach_gen_add_instr (s, instr);
} }
static void mach_gen_emit_const_num (MachGenState *s, int dest, double val) { static void mach_gen_emit_const_num (MachGenState *s, int dest, double val) {
@@ -32774,7 +32885,7 @@ static void mach_gen_emit_const_num (MachGenState *s, int dest, double val) {
cJSON_AddItemToArray (instr, cJSON_CreateString ("access")); cJSON_AddItemToArray (instr, cJSON_CreateString ("access"));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (val)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (val));
cJSON_AddItemToArray (s->instructions, instr); mach_gen_add_instr (s, instr);
} }
static void mach_gen_emit_const_str (MachGenState *s, int dest, const char *val) { static void mach_gen_emit_const_str (MachGenState *s, int dest, const char *val) {
@@ -32782,7 +32893,7 @@ static void mach_gen_emit_const_str (MachGenState *s, int dest, const char *val)
cJSON_AddItemToArray (instr, cJSON_CreateString ("access")); cJSON_AddItemToArray (instr, cJSON_CreateString ("access"));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest));
cJSON_AddItemToArray (instr, cJSON_CreateString (val ? val : "")); cJSON_AddItemToArray (instr, cJSON_CreateString (val ? val : ""));
cJSON_AddItemToArray (s->instructions, instr); mach_gen_add_instr (s, instr);
} }
static void mach_gen_emit_const_bool (MachGenState *s, int dest, int val) { static void mach_gen_emit_const_bool (MachGenState *s, int dest, int val) {
@@ -32797,7 +32908,7 @@ static void mach_gen_emit_jump (MachGenState *s, const char *label) {
cJSON *instr = cJSON_CreateArray (); cJSON *instr = cJSON_CreateArray ();
cJSON_AddItemToArray (instr, cJSON_CreateString ("jump")); cJSON_AddItemToArray (instr, cJSON_CreateString ("jump"));
cJSON_AddItemToArray (instr, cJSON_CreateString (label)); cJSON_AddItemToArray (instr, cJSON_CreateString (label));
cJSON_AddItemToArray (s->instructions, instr); mach_gen_add_instr (s, instr);
} }
static void mach_gen_emit_jump_cond (MachGenState *s, const char *op, int slot, const char *label) { static void mach_gen_emit_jump_cond (MachGenState *s, const char *op, int slot, const char *label) {
@@ -32805,7 +32916,7 @@ static void mach_gen_emit_jump_cond (MachGenState *s, const char *op, int slot,
cJSON_AddItemToArray (instr, cJSON_CreateString (op)); cJSON_AddItemToArray (instr, cJSON_CreateString (op));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (slot)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (slot));
cJSON_AddItemToArray (instr, cJSON_CreateString (label)); cJSON_AddItemToArray (instr, cJSON_CreateString (label));
cJSON_AddItemToArray (s->instructions, instr); mach_gen_add_instr (s, instr);
} }
static void mach_gen_emit_get_prop (MachGenState *s, int dest, int obj, const char *prop) { static void mach_gen_emit_get_prop (MachGenState *s, int dest, int obj, const char *prop) {
@@ -32814,7 +32925,7 @@ static void mach_gen_emit_get_prop (MachGenState *s, int dest, int obj, const ch
cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj));
cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); cJSON_AddItemToArray (instr, cJSON_CreateString (prop));
cJSON_AddItemToArray (s->instructions, instr); mach_gen_add_instr (s, instr);
} }
static void mach_gen_emit_set_prop (MachGenState *s, int obj, const char *prop, int val) { static void mach_gen_emit_set_prop (MachGenState *s, int obj, const char *prop, int val) {
@@ -32823,7 +32934,7 @@ static void mach_gen_emit_set_prop (MachGenState *s, int obj, const char *prop,
cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (val)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (val));
cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); cJSON_AddItemToArray (instr, cJSON_CreateString (prop));
cJSON_AddItemToArray (s->instructions, instr); mach_gen_add_instr (s, instr);
} }
static void mach_gen_emit_get_elem (MachGenState *s, int dest, int obj, int idx) { static void mach_gen_emit_get_elem (MachGenState *s, int dest, int obj, int idx) {
@@ -33095,6 +33206,7 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node);
static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
if (!expr) return -1; if (!expr) return -1;
mach_gen_set_pos (s, expr);
const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "kind")); const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "kind"));
if (!kind) return -1; if (!kind) return -1;
@@ -33433,6 +33545,7 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
static void mach_gen_statement (MachGenState *s, cJSON *stmt) { static void mach_gen_statement (MachGenState *s, cJSON *stmt) {
if (!stmt) return; if (!stmt) return;
mach_gen_set_pos (s, stmt);
const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (stmt, "kind")); const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (stmt, "kind"));
if (!kind) return; if (!kind) return;
@@ -33740,6 +33853,7 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) {
s.scopes = parent->scopes; s.scopes = parent->scopes;
s.errors = parent->errors; s.errors = parent->errors;
s.has_error = parent->has_error; s.has_error = parent->has_error;
s.filename = parent->filename;
cJSON *result = cJSON_CreateObject (); cJSON *result = cJSON_CreateObject ();
@@ -33749,6 +33863,9 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) {
else else
cJSON_AddStringToObject (result, "name", "<anonymous>"); cJSON_AddStringToObject (result, "name", "<anonymous>");
if (s.filename)
cJSON_AddStringToObject (result, "filename", s.filename);
cJSON *is_arrow = cJSON_GetObjectItem (func_node, "arrow"); cJSON *is_arrow = cJSON_GetObjectItem (func_node, "arrow");
s.is_arrow = is_arrow && cJSON_IsTrue (is_arrow); s.is_arrow = is_arrow && cJSON_IsTrue (is_arrow);
@@ -33859,6 +33976,9 @@ static cJSON *mach_gen_program (MachGenState *s, cJSON *ast) {
cJSON *result = cJSON_CreateObject (); cJSON *result = cJSON_CreateObject ();
const char *filename = cJSON_GetStringValue (cJSON_GetObjectItem (ast, "filename")); const char *filename = cJSON_GetStringValue (cJSON_GetObjectItem (ast, "filename"));
cJSON_AddStringToObject (result, "name", filename ? filename : "<eval>"); cJSON_AddStringToObject (result, "name", filename ? filename : "<eval>");
s->filename = filename;
if (filename)
cJSON_AddStringToObject (result, "filename", filename);
s->data = cJSON_AddObjectToObject (result, "data"); s->data = cJSON_AddObjectToObject (result, "data");
s->functions = cJSON_AddArrayToObject (result, "functions"); s->functions = cJSON_AddArrayToObject (result, "functions");
@@ -34023,6 +34143,27 @@ static JSMCode *jsmcode_parse_one(cJSON *func_def) {
} }
code->instr_count = idx; code->instr_count = idx;
/* Extract line table from trailing numbers in each instruction array */
if (idx > 0) {
code->line_table = js_mallocz_rt(idx * sizeof(MachLineEntry));
for (uint32_t i = 0; i < idx; i++) {
cJSON *instr = code->instrs[i];
int n = cJSON_GetArraySize(instr);
if (n >= 2) {
cJSON *line_item = cJSON_GetArrayItem(instr, n - 2);
cJSON *col_item = cJSON_GetArrayItem(instr, n - 1);
if (cJSON_IsNumber(line_item) && cJSON_IsNumber(col_item)) {
code->line_table[i].line = (uint16_t)line_item->valuedouble;
code->line_table[i].col = (uint16_t)col_item->valuedouble;
}
}
}
}
/* Extract name and filename from function definition */
code->name = cJSON_GetStringValue(cJSON_GetObjectItem(func_def, "name"));
code->filename = cJSON_GetStringValue(cJSON_GetObjectItem(func_def, "filename"));
return code; return code;
} }
@@ -34062,6 +34203,7 @@ static void jsmcode_free(JSMCode *code) {
/* Don't free functions[i]->functions — it's the shared pointer */ /* Don't free functions[i]->functions — it's the shared pointer */
if (code->functions[i]->instrs) js_free_rt(code->functions[i]->instrs); if (code->functions[i]->instrs) js_free_rt(code->functions[i]->instrs);
if (code->functions[i]->labels) js_free_rt(code->functions[i]->labels); if (code->functions[i]->labels) js_free_rt(code->functions[i]->labels);
if (code->functions[i]->line_table) js_free_rt(code->functions[i]->line_table);
js_free_rt(code->functions[i]); js_free_rt(code->functions[i]);
} }
} }
@@ -34069,6 +34211,7 @@ static void jsmcode_free(JSMCode *code) {
} }
if (code->instrs) js_free_rt(code->instrs); if (code->instrs) js_free_rt(code->instrs);
if (code->labels) js_free_rt(code->labels); if (code->labels) js_free_rt(code->labels);
if (code->line_table) js_free_rt(code->line_table);
if (code->json_root) cJSON_Delete(code->json_root); if (code->json_root) cJSON_Delete(code->json_root);
js_free_rt(code); js_free_rt(code);
} }
@@ -34692,8 +34835,11 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
if (JS_IsNull(new_frame->slots[i])) break; if (JS_IsNull(new_frame->slots[i])) break;
c_argv[c_argc++] = new_frame->slots[i]; c_argv[c_argc++] = new_frame->slots[i];
} }
ctx->reg_current_frame = frame_ref.val;
ctx->rt->current_register_pc = pc > 0 ? pc - 1 : 0;
JSValue c_result = js_call_c_function(ctx, new_frame->function, new_frame->slots[0], c_argc, c_argv); JSValue c_result = js_call_c_function(ctx, new_frame->function, new_frame->slots[0], c_argc, c_argv);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
ctx->reg_current_frame = JS_NULL;
if (JS_IsException(c_result)) { result = c_result; goto done; } if (JS_IsException(c_result)) { result = c_result; goto done; }
frame->slots[ret_reg] = c_result; frame->slots[ret_reg] = c_result;
} else { } else {
@@ -34857,10 +35003,73 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
} }
done: done:
if (JS_IsException(result)) {
ctx->reg_current_frame = frame_ref.val;
ctx->rt->current_register_pc = pc > 0 ? pc - 1 : 0;
}
JS_DeleteGCRef(ctx, &frame_ref); JS_DeleteGCRef(ctx, &frame_ref);
return result; return result;
} }
/* Public API: get stack trace as cJSON array */
cJSON *JS_GetStack(JSContext *ctx) {
JSRuntime *rt = ctx->rt;
if (JS_IsNull(ctx->reg_current_frame)) return NULL;
JSFrameRegister *frame = (JSFrameRegister *)JS_VALUE_GET_PTR(ctx->reg_current_frame);
uint32_t cur_pc = rt->current_register_pc;
cJSON *arr = cJSON_CreateArray();
int is_first = 1;
while (frame) {
if (!JS_IsFunction(frame->function)) break;
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
const char *func_name = NULL;
const char *file = NULL;
uint16_t line = 0, col = 0;
uint32_t pc = is_first ? cur_pc : 0;
if (fn->kind == JS_FUNC_KIND_REGISTER && fn->u.reg.code) {
JSCodeRegister *code = fn->u.reg.code;
file = code->filename_cstr;
func_name = code->name_cstr;
if (!is_first) {
pc = (uint32_t)(JS_VALUE_GET_INT(frame->address) >> 16);
}
if (code->line_table && pc < code->instr_count) {
line = code->line_table[pc].line;
col = code->line_table[pc].col;
}
} else if (fn->kind == JS_FUNC_KIND_MCODE && fn->u.mcode.code) {
JSMCode *code = fn->u.mcode.code;
file = code->filename;
func_name = code->name;
if (!is_first) {
pc = (uint32_t)(JS_VALUE_GET_INT(frame->address) >> 16);
}
if (code->line_table && pc < code->instr_count) {
line = code->line_table[pc].line;
col = code->line_table[pc].col;
}
}
cJSON *entry = cJSON_CreateObject();
cJSON_AddStringToObject(entry, "function", func_name ? func_name : "<anonymous>");
cJSON_AddStringToObject(entry, "file", file ? file : "<unknown>");
cJSON_AddNumberToObject(entry, "line", line);
cJSON_AddNumberToObject(entry, "column", col);
cJSON_AddItemToArray(arr, entry);
if (JS_IsNull(frame->caller)) break;
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller);
is_first = 0;
}
ctx->reg_current_frame = JS_NULL;
return arr;
}
/* Public API: parse MCODE JSON and execute */ /* Public API: parse MCODE JSON and execute */
JSValue JS_CallMcode(JSContext *ctx, const char *mcode_json) { JSValue JS_CallMcode(JSContext *ctx, const char *mcode_json) {
cJSON *root = cJSON_Parse(mcode_json); cJSON *root = cJSON_Parse(mcode_json);

View File

@@ -1260,6 +1260,11 @@ char *JS_Mcode (const char *ast_json);
Returns result of execution, or JS_EXCEPTION on error. */ Returns result of execution, or JS_EXCEPTION on error. */
JSValue JS_CallMcode (JSContext *ctx, const char *mcode_json); JSValue JS_CallMcode (JSContext *ctx, const char *mcode_json);
/* Get stack trace as cJSON array of frame objects.
Returns NULL if no register VM frame is active.
Caller must call cJSON_Delete() on the result. */
struct cJSON *JS_GetStack (JSContext *ctx);
/* Integrate a CellModule with an environment and execute. /* Integrate a CellModule with an environment and execute.
Returns callable function value, or JS_EXCEPTION on error. */ Returns callable function value, or JS_EXCEPTION on error. */
JSValue cell_module_integrate (JSContext *ctx, CellModule *mod, JSValue env); JSValue cell_module_integrate (JSContext *ctx, CellModule *mod, JSValue env);