Merge branch 'stacktrace' into mach

This commit is contained in:
2026-02-06 20:44:43 -06:00
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)) {
JSValue exc = JS_GetException(ctx);
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)) {
const char *str = JS_ToCString(ctx, result);
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);
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;
} else if (!JS_IsNull(result)) {
const char *str = JS_ToCString(ctx, result);

View File

@@ -390,6 +390,10 @@ struct JSRuntime {
/* Interrupt handler for checking execution limits */
JSInterruptHandler *interrupt_handler;
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 {
@@ -462,6 +466,8 @@ typedef struct JSFrameRegister {
typedef uint32_t MachInstr32;
typedef struct { uint16_t line; uint16_t col; } MachLineEntry;
/* 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_ABx(op, a, bx) ((uint32_t)(op) | ((uint32_t)(a)<<8) | ((uint32_t)(bx)<<16))
@@ -633,6 +639,9 @@ typedef struct JSCodeRegister {
/* Debug info */
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;
/* Pre-parsed MCODE for a single function (off-heap, never GC'd).
@@ -652,6 +661,9 @@ typedef struct JSMCode {
uint32_t func_count;
/* Keep root cJSON alive (owns all the cJSON nodes instrs[] point into) */
cJSON *json_root;
MachLineEntry *line_table; /* [instr_count], parallel to instrs[] */
const char *name; /* function name (points into cJSON tree) */
const char *filename; /* source filename (points into cJSON tree) */
} JSMCode;
/* 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_text_format (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);
static JSValue JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx);
@@ -22899,6 +22912,23 @@ static JSValue js_print (JSContext *ctx, JSValue this_val, int argc, JSValue *ar
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)
* ----------------------------------------------------------------------------
@@ -26042,6 +26072,7 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
/* I/O functions */
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);
}
}
@@ -30770,6 +30801,8 @@ typedef struct MachCode {
struct MachCode **functions;
char *name; /* owned C string, or NULL */
MachLineEntry *line_table; /* [instr_count], parallel to instructions[] */
char *filename; /* source filename (sys_malloc'd) */
} MachCode;
/* ---- Compiler state ---- */
@@ -30816,6 +30849,12 @@ typedef struct MachCompState {
/* Error tracking */
int has_error;
/* Line tracking for debug info */
int cur_line, cur_col;
MachLineEntry *line_info; /* growable, parallel to code[] */
int line_capacity;
const char *filename; /* pointer into AST cJSON (not owned) */
} MachCompState;
/* Forward declarations */
@@ -30824,12 +30863,25 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt);
/* ---- 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) {
if (cs->code_count >= cs->code_capacity) {
int new_cap = cs->code_capacity ? cs->code_capacity * 2 : 64;
cs->code = sys_realloc(cs->code, new_cap * sizeof(MachInstr32));
cs->code_capacity = new_cap;
}
if (cs->code_count >= cs->line_capacity) {
int new_cap = cs->line_capacity ? cs->line_capacity * 2 : 64;
cs->line_info = sys_realloc(cs->line_info, new_cap * sizeof(MachLineEntry));
cs->line_capacity = new_cap;
}
cs->line_info[cs->code_count] = (MachLineEntry){cs->cur_line, cs->cur_col};
cs->code[cs->code_count++] = instr;
}
@@ -30998,6 +31050,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
return dest;
}
mach_set_pos(cs, node);
const char *kind = cJSON_GetStringValue(cJSON_GetObjectItem(node, "kind"));
if (!kind) {
if (dest < 0) dest = mach_reserve_reg(cs);
@@ -31459,6 +31512,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
MachCompState child = {0};
child.parent = cs;
child.scopes = cs->scopes;
child.filename = cs->filename;
child.freereg = 1; /* slot 0 = this */
/* Read function_nr from AST node */
@@ -31516,6 +31570,8 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
fn_code->cpool = child.cpool;
fn_code->func_count = child.func_count;
fn_code->functions = child.functions;
fn_code->line_table = child.line_info;
fn_code->filename = cs->filename ? strdup(cs->filename) : NULL;
cJSON *fname = cJSON_GetObjectItem(node, "name");
if (fname && cJSON_IsString(fname)) {
@@ -31546,6 +31602,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
if (!stmt) return;
mach_set_pos(cs, stmt);
const char *kind = cJSON_GetStringValue(cJSON_GetObjectItem(stmt, "kind"));
if (!kind) return;
@@ -31817,6 +31874,9 @@ static MachCode *mach_compile_program(MachCompState *cs, cJSON *ast) {
cs->scopes = cJSON_GetObjectItem(ast, "scopes");
cs->function_nr = 0;
/* Extract filename for debug info */
cs->filename = cJSON_GetStringValue(cJSON_GetObjectItem(ast, "filename"));
/* Scan scope record for declarations */
mach_scan_scope(cs);
@@ -31861,6 +31921,8 @@ static MachCode *mach_compile_program(MachCompState *cs, cJSON *ast) {
code->func_count = cs->func_count;
code->functions = cs->functions;
code->name = NULL;
code->line_table = cs->line_info;
code->filename = cs->filename ? strdup(cs->filename) : NULL;
return code;
}
@@ -31898,6 +31960,8 @@ void JS_FreeMachCode(MachCode *mc) {
JS_FreeMachCode(mc->functions[i]);
sys_free(mc->functions);
sys_free(mc->name);
sys_free(mc->line_table);
sys_free(mc->filename);
sys_free(mc);
}
@@ -31928,6 +31992,12 @@ JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env) {
/* Intern function name */
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 */
mach_link_code(ctx, code, env);
@@ -31943,6 +32013,9 @@ static void js_free_code_register(JSCodeRegister *code) {
js_free_code_register(code->functions[i]);
}
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);
}
@@ -31973,17 +32046,31 @@ static JSFrameRegister *alloc_frame_register(JSContext *ctx, int slot_count) {
/* Create a register-based function from JSCodeRegister */
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));
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->kind = JS_FUNC_KIND_REGISTER;
fn->length = code->arity;
fn->name = code->name;
fn->u.reg.code = code;
fn->u.reg.env_record = env;
fn->u.reg.outer_frame = outer_frame;
fn->u.reg.env_record = env_ref.val;
fn->u.reg.outer_frame = frame_ref.val;
JS_PopGCRef(ctx, &frame_ref);
JS_PopGCRef(ctx, &env_ref);
return JS_MKPTR(fn);
}
@@ -32453,8 +32540,11 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
JSFunction *fn = JS_VALUE_GET_FUNCTION(func_val);
if (fn->kind == JS_FUNC_KIND_C) {
/* 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]);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
ctx->reg_current_frame = JS_NULL;
if (JS_IsException(ret)) { result = ret; goto done; }
if (nresults > 0) frame->slots[base] = ret;
} else if (fn->kind == JS_FUNC_KIND_REGISTER) {
@@ -32572,6 +32662,10 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
}
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);
return result;
}
@@ -32616,6 +32710,10 @@ typedef struct MachGenState {
/* Error tracking */
cJSON *errors;
int has_error;
/* Line tracking for debug info */
int cur_line, cur_col;
const char *filename;
} MachGenState;
static int mach_gen_expr (MachGenState *s, cJSON *expr, int target);
@@ -32740,17 +32838,30 @@ static void mach_gen_emit_label (MachGenState *s, const char *label) {
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) {
cJSON *instr = cJSON_CreateArray ();
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) {
cJSON *instr = cJSON_CreateArray ();
cJSON_AddItemToArray (instr, cJSON_CreateString (op));
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) {
@@ -32758,7 +32869,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_CreateNumber (a));
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) {
@@ -32767,7 +32878,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 (b));
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) {
@@ -32775,7 +32886,7 @@ static void mach_gen_emit_const_num (MachGenState *s, int dest, double val) {
cJSON_AddItemToArray (instr, cJSON_CreateString ("access"));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest));
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) {
@@ -32783,7 +32894,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_CreateNumber (dest));
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) {
@@ -32798,7 +32909,7 @@ static void mach_gen_emit_jump (MachGenState *s, const char *label) {
cJSON *instr = cJSON_CreateArray ();
cJSON_AddItemToArray (instr, cJSON_CreateString ("jump"));
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) {
@@ -32806,7 +32917,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_CreateNumber (slot));
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) {
@@ -32815,7 +32926,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 (obj));
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) {
@@ -32824,7 +32935,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 (val));
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) {
@@ -33096,6 +33207,7 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node);
static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
if (!expr) return -1;
mach_gen_set_pos (s, expr);
const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "kind"));
if (!kind) return -1;
@@ -33434,6 +33546,7 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
static void mach_gen_statement (MachGenState *s, cJSON *stmt) {
if (!stmt) return;
mach_gen_set_pos (s, stmt);
const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (stmt, "kind"));
if (!kind) return;
@@ -33741,6 +33854,7 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) {
s.scopes = parent->scopes;
s.errors = parent->errors;
s.has_error = parent->has_error;
s.filename = parent->filename;
cJSON *result = cJSON_CreateObject ();
@@ -33750,6 +33864,9 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) {
else
cJSON_AddStringToObject (result, "name", "<anonymous>");
if (s.filename)
cJSON_AddStringToObject (result, "filename", s.filename);
cJSON *is_arrow = cJSON_GetObjectItem (func_node, "arrow");
s.is_arrow = is_arrow && cJSON_IsTrue (is_arrow);
@@ -33860,6 +33977,9 @@ static cJSON *mach_gen_program (MachGenState *s, cJSON *ast) {
cJSON *result = cJSON_CreateObject ();
const char *filename = cJSON_GetStringValue (cJSON_GetObjectItem (ast, "filename"));
cJSON_AddStringToObject (result, "name", filename ? filename : "<eval>");
s->filename = filename;
if (filename)
cJSON_AddStringToObject (result, "filename", filename);
s->data = cJSON_AddObjectToObject (result, "data");
s->functions = cJSON_AddArrayToObject (result, "functions");
@@ -34024,6 +34144,27 @@ static JSMCode *jsmcode_parse_one(cJSON *func_def) {
}
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;
}
@@ -34063,6 +34204,7 @@ static void jsmcode_free(JSMCode *code) {
/* Don't free functions[i]->functions — it's the shared pointer */
if (code->functions[i]->instrs) js_free_rt(code->functions[i]->instrs);
if (code->functions[i]->labels) js_free_rt(code->functions[i]->labels);
if (code->functions[i]->line_table) js_free_rt(code->functions[i]->line_table);
js_free_rt(code->functions[i]);
}
}
@@ -34070,6 +34212,7 @@ static void jsmcode_free(JSMCode *code) {
}
if (code->instrs) js_free_rt(code->instrs);
if (code->labels) js_free_rt(code->labels);
if (code->line_table) js_free_rt(code->line_table);
if (code->json_root) cJSON_Delete(code->json_root);
js_free_rt(code);
}
@@ -34693,8 +34836,11 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
if (JS_IsNull(new_frame->slots[i])) break;
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);
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; }
frame->slots[ret_reg] = c_result;
} else {
@@ -34858,10 +35004,73 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
}
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);
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 */
JSValue JS_CallMcode(JSContext *ctx, const char *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. */
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.
Returns callable function value, or JS_EXCEPTION on error. */
JSValue cell_module_integrate (JSContext *ctx, CellModule *mod, JSValue env);