diff --git a/source/mach.c b/source/mach.c index 93341ac9..61bd67f3 100644 --- a/source/mach.c +++ b/source/mach.c @@ -494,6 +494,37 @@ static int mach_check_call_arity(JSContext *ctx, JSFunction *fn, int argc) { return 1; } +/* Scan backwards from pc to find what loaded the callee register. + Returns the name in buf, or NULL if unknown. */ +static const char *mach_callee_name(JSContext *ctx, JSCodeRegister *code, + uint32_t pc, int reg, + char *buf, size_t buf_size) { + int hops = 4; /* limit move-chain depth */ + for (int i = (int)pc - 2; i >= 0 && hops > 0; i--) { + MachInstr32 prev = code->instructions[i]; + int prev_op = MACH_GET_OP(prev); + int prev_a = MACH_GET_A(prev); + if (prev_a != reg) continue; + if (prev_op == MACH_GETENV || prev_op == MACH_GETINTRINSIC) { + int bx = MACH_GET_Bx(prev); + if ((uint32_t)bx < code->cpool_count && JS_IsText(code->cpool[bx])) + return JS_KeyGetStr(ctx, buf, buf_size, code->cpool[bx]); + } + if (prev_op == MACH_LOAD_FIELD) { + int ci = MACH_GET_C(prev); + if ((uint32_t)ci < code->cpool_count && JS_IsText(code->cpool[ci])) + return JS_KeyGetStr(ctx, buf, buf_size, code->cpool[ci]); + } + if (prev_op == MACH_MOVE) { + reg = MACH_GET_B(prev); + hops--; + continue; + } + break; /* some other op wrote to this reg — give up */ + } + return NULL; +} + /* ---- Link pass: resolve GETNAME to GETINTRINSIC or GETENV ---- */ static void mach_link_code(JSContext *ctx, JSCodeRegister *code, JSValue env) { @@ -2560,7 +2591,12 @@ vm_dispatch: /* A=frame_slot, B=func_reg, C=argc */ JSValue func_val = frame->slots[b]; if (!mist_is_function(func_val)) { - JS_RaiseDisrupt(ctx, "not a function"); + char nbuf[KEY_GET_STR_BUF_SIZE]; + const char *name = mach_callee_name(ctx, code, pc, b, nbuf, sizeof(nbuf)); + if (name) + JS_RaiseDisrupt(ctx, "%s is not a function", name); + else + JS_RaiseDisrupt(ctx, "not a function"); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); goto disrupt; }