This commit is contained in:
2026-02-19 00:33:16 -06:00
parent e59bfe19f7
commit 19132c1517
4 changed files with 161 additions and 84 deletions

View File

@@ -460,6 +460,33 @@ JSFrameRegister *alloc_frame_register(JSContext *ctx, int slot_count) {
return frame;
}
static JSValue js_new_register_code(JSContext *ctx, JSCodeRegister *code) {
JSCode *jc;
if (!code) return JS_EXCEPTION;
jc = ct_alloc(ctx, sizeof(JSCode), 8);
if (!jc) return JS_EXCEPTION;
memset(jc, 0, sizeof(JSCode));
jc->header = objhdr_make(0, OBJ_CODE, 0, 0, 0, 0);
jc->kind = JS_CODE_KIND_REGISTER;
jc->arity = (int16_t)code->arity;
jc->u.reg.code = code;
return JS_MKPTR(jc);
}
static JSValue js_new_native_code(JSContext *ctx, void *fn_ptr, void *dl_handle,
uint16_t nr_slots, int arity) {
JSCode *jc = ct_alloc(ctx, sizeof(JSCode), 8);
if (!jc) return JS_EXCEPTION;
memset(jc, 0, sizeof(JSCode));
jc->header = objhdr_make(0, OBJ_CODE, 0, 0, 0, 0);
jc->kind = JS_CODE_KIND_NATIVE;
jc->arity = (int16_t)arity;
jc->u.native.fn_ptr = fn_ptr;
jc->u.native.dl_handle = dl_handle;
jc->u.native.nr_slots = nr_slots;
return JS_MKPTR(jc);
}
/* Create a register-based function from JSCodeRegister */
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
@@ -470,50 +497,84 @@ JSValue js_new_register_function(JSContext *ctx, JSCodeRegister *code, JSValue e
JS_PushGCRef(ctx, &frame_ref);
frame_ref.val = outer_frame;
JSFunction *fn = js_mallocz(ctx, sizeof(JSFunction));
JSGCRef fn_ref;
JSFunction *fn;
JSValue code_obj;
JS_AddGCRef(ctx, &fn_ref);
fn_ref.val = JS_NULL;
fn = js_mallocz(ctx, sizeof(JSFunction));
if (!fn) {
JS_DeleteGCRef(ctx, &fn_ref);
JS_PopGCRef(ctx, &frame_ref);
JS_PopGCRef(ctx, &env_ref);
return JS_EXCEPTION;
}
fn_ref.val = JS_MKPTR(fn);
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_ref.val;
fn->u.reg.outer_frame = frame_ref.val;
code_obj = js_new_register_code(ctx, code);
if (JS_IsException(code_obj)) {
JS_DeleteGCRef(ctx, &fn_ref);
JS_PopGCRef(ctx, &frame_ref);
JS_PopGCRef(ctx, &env_ref);
return JS_EXCEPTION;
}
fn = JS_VALUE_GET_FUNCTION(fn_ref.val);
fn->u.cell.code = code_obj;
fn->u.cell.env_record = env_ref.val;
fn->u.cell.outer_frame = frame_ref.val;
JSValue out = fn_ref.val;
JS_DeleteGCRef(ctx, &fn_ref);
JS_PopGCRef(ctx, &frame_ref);
JS_PopGCRef(ctx, &env_ref);
return JS_MKPTR(fn);
return out;
}
/* Create a native (QBE-compiled) function */
JSValue js_new_native_function(JSContext *ctx, void *fn_ptr, void *dl_handle,
uint16_t nr_slots, int arity, JSValue outer_frame) {
JSGCRef frame_ref;
JSGCRef fn_ref;
JSFunction *fn;
JSValue code_obj;
JS_PushGCRef(ctx, &frame_ref);
frame_ref.val = outer_frame;
JS_AddGCRef(ctx, &fn_ref);
fn_ref.val = JS_NULL;
JSFunction *fn = js_mallocz(ctx, sizeof(JSFunction));
fn = js_mallocz(ctx, sizeof(JSFunction));
if (!fn) {
JS_DeleteGCRef(ctx, &fn_ref);
JS_PopGCRef(ctx, &frame_ref);
return JS_EXCEPTION;
}
fn_ref.val = JS_MKPTR(fn);
fn->header = objhdr_make(0, OBJ_FUNCTION, 0, 0, 0, 0);
fn->kind = JS_FUNC_KIND_NATIVE;
fn->length = arity;
fn->name = JS_NULL;
fn->u.native.fn_ptr = fn_ptr;
fn->u.native.dl_handle = dl_handle;
fn->u.native.nr_slots = nr_slots;
fn->u.native.outer_frame = frame_ref.val;
code_obj = js_new_native_code(ctx, fn_ptr, dl_handle, nr_slots, arity);
if (JS_IsException(code_obj)) {
JS_DeleteGCRef(ctx, &fn_ref);
JS_PopGCRef(ctx, &frame_ref);
return JS_EXCEPTION;
}
fn = JS_VALUE_GET_FUNCTION(fn_ref.val);
fn->u.cell.code = code_obj;
fn->u.cell.env_record = JS_NULL;
fn->u.cell.outer_frame = frame_ref.val;
JSValue out = fn_ref.val;
JS_DeleteGCRef(ctx, &fn_ref);
JS_PopGCRef(ctx, &frame_ref);
return JS_MKPTR(fn);
return out;
}
/* Binary operations helper */
@@ -750,8 +811,8 @@ void __asan_on_error(void) {
const char *file = NULL;
uint16_t line = 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;
if (fn->kind == JS_FUNC_KIND_REGISTER && JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code) {
JSCodeRegister *code = JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code;
file = code->filename_cstr;
func_name = code->name_cstr;
if (!is_first)
@@ -787,8 +848,8 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
ctx->suspended_frame_ref.val = JS_NULL;
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
code = fn->u.reg.code;
env = fn->u.reg.env_record;
code = JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code;
env = fn->u.cell.env_record;
pc = ctx->suspended_pc;
result = JS_NULL;
#ifdef HAVE_ASAN
@@ -1499,7 +1560,7 @@ vm_dispatch:
/* Read env fresh from frame->function — C local env can go stale after GC */
int bx = MACH_GET_Bx(instr);
JSValue key = code->cpool[bx];
JSValue cur_env = JS_VALUE_GET_FUNCTION(frame->function)->u.reg.env_record;
JSValue cur_env = JS_VALUE_GET_FUNCTION(frame->function)->u.cell.env_record;
JSValue val = JS_GetProperty(ctx, cur_env, key);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
frame->slots[a] = val;
@@ -1511,7 +1572,7 @@ vm_dispatch:
int bx = MACH_GET_Bx(instr);
JSValue key = code->cpool[bx];
JSValue val = JS_NULL;
JSValue cur_env = JS_VALUE_GET_FUNCTION(frame->function)->u.reg.env_record;
JSValue cur_env = JS_VALUE_GET_FUNCTION(frame->function)->u.cell.env_record;
if (!JS_IsNull(cur_env)) {
val = JS_GetProperty(ctx, cur_env, key);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
@@ -1528,7 +1589,7 @@ vm_dispatch:
/* R(A) = outer_frame[B].slots[C] — walk lexical scope chain */
int depth = b;
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.reg.outer_frame);
JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
if (!target) {
fprintf(stderr, "GETUP: NULL outer_frame at depth 0! pc=%d a=%d depth=%d slot=%d nr_slots=%d instr=0x%08x\n",
pc-1, a, depth, c, code->nr_slots, instr);
@@ -1537,7 +1598,7 @@ vm_dispatch:
}
for (int d = 1; d < depth; d++) {
fn = JS_VALUE_GET_FUNCTION(target->function);
JSFrameRegister *next = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.reg.outer_frame);
JSFrameRegister *next = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
if (!next) {
fprintf(stderr, "GETUP: NULL outer_frame at depth %d! pc=%d a=%d depth=%d slot=%d nr_slots=%d instr=0x%08x\n",
d, pc-1, a, depth, c, code->nr_slots, instr);
@@ -1554,10 +1615,10 @@ vm_dispatch:
/* outer_frame[B].slots[C] = R(A) — walk lexical scope chain */
int depth = b;
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.reg.outer_frame);
JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
for (int d = 1; d < depth; d++) {
fn = JS_VALUE_GET_FUNCTION(target->function);
target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.reg.outer_frame);
target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
}
target->slots[c] = frame->slots[a];
VM_BREAK();
@@ -1651,9 +1712,9 @@ vm_dispatch:
const char *callee_file = "?";
{
JSFunction *callee_fn = JS_VALUE_GET_FUNCTION(frame->function);
if (callee_fn->kind == JS_FUNC_KIND_REGISTER && callee_fn->u.reg.code) {
if (callee_fn->u.reg.code->name_cstr) callee_name = callee_fn->u.reg.code->name_cstr;
if (callee_fn->u.reg.code->filename_cstr) callee_file = callee_fn->u.reg.code->filename_cstr;
if (callee_fn->kind == JS_FUNC_KIND_REGISTER && JS_VALUE_GET_CODE(callee_fn->u.cell.code)->u.reg.code) {
if (JS_VALUE_GET_CODE(callee_fn->u.cell.code)->u.reg.code->name_cstr) callee_name = JS_VALUE_GET_CODE(callee_fn->u.cell.code)->u.reg.code->name_cstr;
if (JS_VALUE_GET_CODE(callee_fn->u.cell.code)->u.reg.code->filename_cstr) callee_file = JS_VALUE_GET_CODE(callee_fn->u.cell.code)->u.reg.code->filename_cstr;
}
}
#endif
@@ -1663,8 +1724,8 @@ vm_dispatch:
frame_ref.val = JS_MKPTR(frame);
int ret_info = JS_VALUE_GET_INT(frame->address);
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
code = fn->u.reg.code;
env = fn->u.reg.env_record;
code = JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code;
env = fn->u.cell.env_record;
pc = ret_info >> 16;
int ret_slot = ret_info & 0xFFFF;
if (ret_slot != 0xFFFF) {
@@ -1696,8 +1757,8 @@ vm_dispatch:
frame_ref.val = JS_MKPTR(frame);
int ret_info = JS_VALUE_GET_INT(frame->address);
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
code = fn->u.reg.code;
env = fn->u.reg.env_record;
code = JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code;
env = fn->u.cell.env_record;
pc = ret_info >> 16;
int ret_slot = ret_info & 0xFFFF;
if (ret_slot != 0xFFFF) frame->slots[ret_slot] = result;
@@ -1725,7 +1786,7 @@ vm_dispatch:
if ((uint32_t)bx < code->func_count) {
JSCodeRegister *fn_code = code->functions[bx];
/* Read env fresh from frame->function — C local can be stale */
JSValue cur_env = JS_VALUE_GET_FUNCTION(frame->function)->u.reg.env_record;
JSValue cur_env = JS_VALUE_GET_FUNCTION(frame->function)->u.cell.env_record;
JSValue fn_val = js_new_register_function(ctx, fn_code, cur_env, frame_ref.val);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
frame->slots[a] = fn_val;
@@ -2112,7 +2173,7 @@ vm_dispatch:
if (fn->kind == JS_FUNC_KIND_REGISTER) {
/* Register function: switch frames inline (fast path) */
JSCodeRegister *fn_code = fn->u.reg.code;
JSCodeRegister *fn_code = JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code;
JSFrameRegister *new_frame = alloc_frame_register(ctx, fn_code->nr_slots);
if (!new_frame) {
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
@@ -2122,7 +2183,7 @@ vm_dispatch:
fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->slots[a]);
fn_val = fr->function;
fn = JS_VALUE_GET_FUNCTION(fn_val);
fn_code = fn->u.reg.code;
fn_code = JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code;
new_frame->function = fn_val;
/* Copy this + args from call frame to new frame */
int copy_count = (c_argc < fn_code->arity) ? c_argc : fn_code->arity;
@@ -2135,7 +2196,7 @@ vm_dispatch:
frame = new_frame;
frame_ref.val = JS_MKPTR(frame);
code = fn_code;
env = fn->u.reg.env_record;
env = fn->u.cell.env_record;
pc = code->entry_point;
} else {
/* C, native, or bytecode function */
@@ -2182,7 +2243,7 @@ vm_dispatch:
JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val);
if (fn->kind == JS_FUNC_KIND_REGISTER) {
JSCodeRegister *fn_code = fn->u.reg.code;
JSCodeRegister *fn_code = JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code;
int current_slots = (int)objhdr_cap56(frame->header);
if (fn_code->nr_slots <= current_slots) {
@@ -2197,7 +2258,7 @@ vm_dispatch:
frame->function = fn_val;
/* caller stays the same — we're reusing this frame */
code = fn_code;
env = fn->u.reg.env_record;
env = fn->u.cell.env_record;
pc = code->entry_point;
} else {
/* SLOW PATH: callee needs more slots, must allocate */
@@ -2210,7 +2271,7 @@ vm_dispatch:
fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->slots[a]);
fn_val = fr->function;
fn = JS_VALUE_GET_FUNCTION(fn_val);
fn_code = fn->u.reg.code;
fn_code = JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code;
new_frame->function = fn_val;
int copy_count = (c_argc < fn_code->arity) ? c_argc : fn_code->arity;
new_frame->slots[0] = fr->slots[0]; /* this */
@@ -2221,7 +2282,7 @@ vm_dispatch:
frame = new_frame;
frame_ref.val = JS_MKPTR(frame);
code = fn_code;
env = fn->u.reg.env_record;
env = fn->u.cell.env_record;
pc = code->entry_point;
}
} else {
@@ -2249,8 +2310,8 @@ vm_dispatch:
frame_ref.val = JS_MKPTR(frame);
int ret_info = JS_VALUE_GET_INT(frame->address);
JSFunction *ret_fn = JS_VALUE_GET_FUNCTION(frame->function);
code = ret_fn->u.reg.code;
env = ret_fn->u.reg.env_record;
code = JS_VALUE_GET_CODE(ret_fn->u.cell.code)->u.reg.code;
env = ret_fn->u.cell.env_record;
pc = ret_info >> 16;
int ret_slot = ret_info & 0xFFFF;
if (ret_slot != 0xFFFF) frame->slots[ret_slot] = ret;
@@ -2302,10 +2363,10 @@ vm_dispatch:
uint32_t frame_pc = pc;
for (;;) {
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
code = fn->u.reg.code;
code = JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code;
/* Only enter handler if we're not already inside it */
if (code->disruption_pc > 0 && frame_pc < code->disruption_pc) {
env = fn->u.reg.env_record;
env = fn->u.cell.env_record;
pc = code->disruption_pc;
ctx->disruption_reported = FALSE;
frame_ref.val = JS_MKPTR(frame); /* root handler frame for GC */