stack traces
This commit is contained in:
@@ -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);
|
||||
|
||||
235
source/quickjs.c
235
source/quickjs.c
@@ -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);
|
||||
|
||||
@@ -22898,6 +22911,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)
|
||||
* ----------------------------------------------------------------------------
|
||||
@@ -26041,6 +26071,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30769,6 +30800,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 ---- */
|
||||
@@ -30815,6 +30848,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 */
|
||||
@@ -30823,12 +30862,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;
|
||||
}
|
||||
|
||||
@@ -30997,6 +31049,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);
|
||||
@@ -31458,6 +31511,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 */
|
||||
@@ -31515,6 +31569,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)) {
|
||||
@@ -31545,6 +31601,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;
|
||||
|
||||
@@ -31816,6 +31873,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);
|
||||
|
||||
@@ -31860,6 +31920,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;
|
||||
}
|
||||
@@ -31897,6 +31959,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);
|
||||
}
|
||||
|
||||
@@ -31927,6 +31991,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);
|
||||
|
||||
@@ -31942,6 +32012,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);
|
||||
}
|
||||
|
||||
@@ -31972,17 +32045,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);
|
||||
}
|
||||
|
||||
@@ -32452,8 +32539,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) {
|
||||
@@ -32571,6 +32661,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;
|
||||
}
|
||||
@@ -32615,6 +32709,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);
|
||||
@@ -32739,17 +32837,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) {
|
||||
@@ -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_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) {
|
||||
@@ -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 (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) {
|
||||
@@ -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_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) {
|
||||
@@ -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_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) {
|
||||
@@ -32797,7 +32908,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) {
|
||||
@@ -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_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) {
|
||||
@@ -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 (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) {
|
||||
@@ -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 (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) {
|
||||
@@ -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) {
|
||||
if (!expr) return -1;
|
||||
mach_gen_set_pos (s, expr);
|
||||
const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "kind"));
|
||||
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) {
|
||||
if (!stmt) return;
|
||||
mach_gen_set_pos (s, stmt);
|
||||
const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (stmt, "kind"));
|
||||
if (!kind) return;
|
||||
|
||||
@@ -33740,6 +33853,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 ();
|
||||
|
||||
@@ -33749,6 +33863,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);
|
||||
|
||||
@@ -33859,6 +33976,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");
|
||||
@@ -34023,6 +34143,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;
|
||||
}
|
||||
|
||||
@@ -34062,6 +34203,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]);
|
||||
}
|
||||
}
|
||||
@@ -34069,6 +34211,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);
|
||||
}
|
||||
@@ -34692,8 +34835,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 {
|
||||
@@ -34857,10 +35003,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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user