From 836227c8d3d02bf29f6a578a80405b07eb2c0cbf Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sat, 7 Feb 2026 11:53:39 -0600 Subject: [PATCH] fix mach proxy and templates --- source/quickjs.c | 366 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 350 insertions(+), 16 deletions(-) diff --git a/source/quickjs.c b/source/quickjs.c index be21780b..39ff7d99 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -568,6 +568,8 @@ typedef enum MachOpcode { MACH_HASPROP, /* R(A) = R(C) in R(B) — has property check */ MACH_REGEXP, /* R(A) = regexp(K(B), K(C)) — regex literal */ + MACH_CALLMETHOD, /* Method call: R(A)=obj, B=nargs in R(A+2)..R(A+1+B), C=cpool key */ + MACH_NOP, MACH_OP_COUNT @@ -629,6 +631,7 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = { [MACH_DELETEINDEX] = "deleteindex", [MACH_HASPROP] = "hasprop", [MACH_REGEXP] = "regexp", + [MACH_CALLMETHOD] = "callmethod", [MACH_NOP] = "nop", }; @@ -19642,10 +19645,9 @@ int JS_GetLength (JSContext *ctx, JSValue obj, int64_t *pres) { } static void free_arg_list (JSContext *ctx, JSValue *tab, uint32_t len) { - /* With copying GC, no explicit freeing needed - GC handles it */ (void)ctx; - (void)tab; (void)len; + js_free_rt(tab); } /* XXX: should use ValueArray */ @@ -19662,9 +19664,9 @@ static JSValue *build_arg_list (JSContext *ctx, uint32_t *plen, JSValue *parray_ ctx, "too many arguments in function call (only %d allowed)", JS_MAX_LOCAL_VARS); return NULL; } - tab = js_mallocz (ctx, sizeof (tab[0]) * max_uint32 (1, len)); + tab = js_mallocz_rt (sizeof (tab[0]) * max_uint32 (1, len)); if (!tab) return NULL; - arr = JS_VALUE_GET_ARRAY (*parray_arg); /* re-chase after malloc via argv */ + arr = JS_VALUE_GET_ARRAY (*parray_arg); for (i = 0; i < len; i++) { tab[i] = arr->values[i]; } @@ -22814,9 +22816,19 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc, /* Get value from collection */ JSValue coll_value = JS_NULL; if (is_array) { - /* Parse name as integer index */ + /* Parse name as integer index — parse digits from the text directly + since JS_ToInt32 doesn't handle text values */ + int name_len = js_string_value_len (name_val); int32_t idx = 0; - if (JS_ToInt32 (ctx, &idx, name_val) == 0 && idx >= 0) { + int valid = (name_len > 0); + for (int ni = 0; ni < name_len && valid; ni++) { + uint32_t ch = js_string_value_get (name_val, ni); + if (ch >= '0' && ch <= '9') + idx = idx * 10 + (ch - '0'); + else + valid = 0; + } + if (valid && idx >= 0) { coll_value = JS_GetPropertyUint32 (ctx, collection, (uint32_t)idx); } } else { @@ -25912,11 +25924,91 @@ static void JS_AddIntrinsicBasicObjects (JSContext *ctx) { JS_PopGCRef (ctx, &proto_ref); } +/* logical(val) — false for 0/false/"false"/null, true for 1/true/"true", null otherwise */ +static JSValue js_cell_logical(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + if (argc < 1) return JS_NULL; + JSValue v = argv[0]; + if (JS_IsNull(v) || (JS_IsInt(v) && JS_VALUE_GET_INT(v) == 0) || + (JS_IsBool(v) && !JS_VALUE_GET_BOOL(v))) return JS_FALSE; + if ((JS_IsInt(v) && JS_VALUE_GET_INT(v) == 1) || + (JS_IsBool(v) && JS_VALUE_GET_BOOL(v))) return JS_TRUE; + if (JS_IsText(v)) { + char buf[8]; + JS_KeyGetStr(ctx, buf, sizeof(buf), v); + if (strcmp(buf, "false") == 0) return JS_FALSE; + if (strcmp(buf, "true") == 0) return JS_TRUE; + } + return JS_NULL; +} + +/* starts_with(str, prefix) — search(str, prefix) == 0 */ +static JSValue js_cell_starts_with(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + if (argc < 2) return JS_NULL; + JSValue args[3] = { argv[0], argv[1], JS_NULL }; + JSValue pos = js_cell_text_search(ctx, JS_NULL, 2, args); + if (JS_IsInt(pos) && JS_VALUE_GET_INT(pos) == 0) return JS_TRUE; + return JS_FALSE; +} + +/* ends_with(str, suffix) — search(str, suffix, -length(suffix)) != null */ +static JSValue js_cell_ends_with(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + if (argc < 2) return JS_NULL; + JSValue len_val = js_cell_length(ctx, JS_NULL, 1, &argv[1]); + int slen = JS_IsInt(len_val) ? JS_VALUE_GET_INT(len_val) : 0; + JSValue offset = JS_NewInt32(ctx, -slen); + JSValue args[3] = { argv[0], argv[1], offset }; + JSValue pos = js_cell_text_search(ctx, JS_NULL, 3, args); + if (!JS_IsNull(pos)) return JS_TRUE; + return JS_FALSE; +} + +/* every(arr, pred) — find(arr, x => !pred(x)) == null */ +static JSValue js_cell_every(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + if (argc < 2) return JS_NULL; + if (!JS_IsArray(argv[0]) || !JS_IsFunction(argv[1])) return JS_NULL; + JSGCRef arr_ref, fn_ref; + JS_PushGCRef(ctx, &arr_ref); + JS_PushGCRef(ctx, &fn_ref); + arr_ref.val = argv[0]; + fn_ref.val = argv[1]; + JSArray *arr = JS_VALUE_GET_ARRAY(arr_ref.val); + for (int i = 0; i < arr->len; i++) { + JSValue elem = arr->values[i]; + JSValue r = JS_CallInternal(ctx, fn_ref.val, JS_NULL, 1, &elem, 0); + arr = JS_VALUE_GET_ARRAY(arr_ref.val); + if (JS_IsException(r)) { + JS_PopGCRef(ctx, &fn_ref); + JS_PopGCRef(ctx, &arr_ref); + return r; + } + if (!JS_ToBool(ctx, r)) { + JS_PopGCRef(ctx, &fn_ref); + JS_PopGCRef(ctx, &arr_ref); + return JS_FALSE; + } + } + JS_PopGCRef(ctx, &fn_ref); + JS_PopGCRef(ctx, &arr_ref); + return JS_TRUE; +} + +/* some(arr, pred) — find(arr, pred) != null */ +static JSValue js_cell_some(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + if (argc < 2) return JS_NULL; + JSValue r = js_cell_array_find(ctx, JS_NULL, argc, argv); + if (JS_IsException(r)) return r; + if (!JS_IsNull(r)) return JS_TRUE; + return JS_FALSE; +} + /* GC-SAFE: Helper to set a global function. Creates function first, then reads ctx->global_obj to ensure it's not stale if GC ran during function creation. */ static void js_set_global_cfunc(JSContext *ctx, const char *name, JSCFunction *func, int length) { - JSValue fn = JS_NewCFunction(ctx, func, name, length); - JS_SetPropertyStr(ctx, ctx->global_obj, name, fn); + JSGCRef ref; + JS_PushGCRef(ctx, &ref); + ref.val = JS_NewCFunction(ctx, func, name, length); + JS_SetPropertyStr(ctx, ctx->global_obj, name, ref.val); + JS_PopGCRef(ctx, &ref); } static void JS_AddIntrinsicBaseObjects (JSContext *ctx) { @@ -26035,6 +26127,24 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) { js_set_global_cfunc(ctx, "pop", js_cell_pop, 1); js_set_global_cfunc(ctx, "meme", js_cell_meme, 2); + /* Engine builtins (normally from engine.cm, needed for --mach-run) */ + js_set_global_cfunc(ctx, "logical", js_cell_logical, 1); + js_set_global_cfunc(ctx, "starts_with", js_cell_starts_with, 2); + js_set_global_cfunc(ctx, "ends_with", js_cell_ends_with, 2); + js_set_global_cfunc(ctx, "every", js_cell_every, 2); + js_set_global_cfunc(ctx, "some", js_cell_some, 2); + + /* fn record with apply property */ + { + JSGCRef fn_ref; + JS_PushGCRef(ctx, &fn_ref); + fn_ref.val = JS_NewObject(ctx); + JSValue apply_fn = JS_NewCFunction(ctx, js_cell_fn_apply, "apply", 2); + JS_SetPropertyStr(ctx, fn_ref.val, "apply", apply_fn); + JS_SetPropertyStr(ctx, ctx->global_obj, "fn", fn_ref.val); + JS_PopGCRef(ctx, &fn_ref); + } + /* 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); @@ -30364,7 +30474,7 @@ static void ast_sem_check_expr (ASTSemState *st, ASTSemScope *scope, cJSON *expr } /* Template literal */ - if (strcmp (kind, "template") == 0) { + if (strcmp (kind, "template") == 0 || strcmp (kind, "text literal") == 0) { cJSON *el; cJSON_ArrayForEach (el, cJSON_GetObjectItem (expr, "list")) { ast_sem_check_expr (st, scope, el); @@ -31203,6 +31313,58 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { return dest; } + /* Template literal with expressions: kind="text literal" + Format: value = "hello {0} world {1}", list = [expr0, expr1] + Compile as: format(fmt_string, [expr0, expr1, ...]) */ + if (strcmp(kind, "text literal") == 0) { + if (dest < 0) dest = mach_reserve_reg(cs); + int save_freereg = cs->freereg; + + cJSON *list = cJSON_GetObjectItem(node, "list"); + int nexpr = list ? cJSON_GetArraySize(list) : 0; + + /* Reserve consecutive regs for call: [format_fn, fmt_str, arr] */ + int call_base = mach_reserve_reg(cs); + int arg1_reg = mach_reserve_reg(cs); + int arg2_reg = mach_reserve_reg(cs); + + /* Reserve consecutive regs for NEWARRAY: arr_reg, then elem slots */ + int arr_base = mach_reserve_reg(cs); + for (int i = 0; i < nexpr; i++) mach_reserve_reg(cs); + /* Now arr_base+1..arr_base+nexpr are the element slots */ + + /* Compile expressions into arr_base+1..arr_base+nexpr */ + for (int i = 0; i < nexpr; i++) { + cJSON *expr = cJSON_GetArrayItem(list, i); + int slot = arr_base + 1 + i; + int r = mach_compile_expr(cs, expr, slot); + if (r != slot) mach_emit(cs, MACH_ABC(MACH_MOVE, slot, r, 0)); + } + + /* Create array from consecutive element regs */ + mach_emit(cs, MACH_ABC(MACH_NEWARRAY, arr_base, nexpr, 0)); + + /* Load format function */ + int ki_format = mach_cpool_add_str(cs, "format"); + mach_emit(cs, MACH_ABx(MACH_GETINTRINSIC, call_base, ki_format)); + + /* Load format string */ + const char *fmt = cJSON_GetStringValue(cJSON_GetObjectItem(node, "value")); + int ki_fmt = mach_cpool_add_str(cs, fmt ? fmt : ""); + mach_emit(cs, MACH_ABx(MACH_LOADK, arg1_reg, ki_fmt)); + + /* Move array to arg2 position */ + mach_emit(cs, MACH_ABC(MACH_MOVE, arg2_reg, arr_base, 0)); + + /* Call format(fmt, arr) */ + mach_emit(cs, MACH_ABC(MACH_CALL, call_base, 2, 1)); + + mach_free_reg_to(cs, save_freereg); + if (dest != call_base) + mach_emit(cs, MACH_ABC(MACH_MOVE, dest, call_base, 0)); + return dest; + } + /* Boolean/null literals */ if (strcmp(kind, "true") == 0) { if (dest < 0) dest = mach_reserve_reg(cs); @@ -31263,6 +31425,55 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { cJSON *args = cJSON_GetObjectItem(node, "list"); int nargs = args ? cJSON_GetArraySize(args) : 0; + /* Check if this is a method call: obj.method(args) or obj[key](args) */ + const char *fn_kind = NULL; + if (fn_expr) + fn_kind = cJSON_GetStringValue(cJSON_GetObjectItem(fn_expr, "kind")); + + if (fn_kind && strcmp(fn_kind, ".") == 0) { + /* Method call with dot notation: obj.method(args) */ + int save_freereg = cs->freereg; + int base = mach_reserve_reg(cs); /* R(base) = obj */ + if (dest < 0) dest = base; + mach_reserve_reg(cs); /* R(base+1) = temp slot for VM */ + + /* Compile obj into base */ + cJSON *obj_expr = cJSON_GetObjectItem(fn_expr, "expression"); + if (!obj_expr) obj_expr = cJSON_GetObjectItem(fn_expr, "left"); + int obj_r = mach_compile_expr(cs, obj_expr, base); + if (obj_r != base) + mach_emit(cs, MACH_ABC(MACH_MOVE, base, obj_r, 0)); + + /* Extract property name */ + cJSON *prop = cJSON_GetObjectItem(fn_expr, "name"); + if (!prop) prop = cJSON_GetObjectItem(fn_expr, "right"); + const char *prop_name = NULL; + if (cJSON_IsString(prop)) prop_name = cJSON_GetStringValue(prop); + else if (prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItem(prop, "value")); + if (!prop_name && prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItem(prop, "name")); + if (!prop_name) prop_name = cJSON_GetStringValue(cJSON_GetObjectItem(fn_expr, "value")); + if (!prop_name) prop_name = "unknown"; + + int ki = mach_cpool_add_str(cs, prop_name); + + /* Compile args into R(base+2)..R(base+1+nargs) */ + for (int i = 0; i < nargs; i++) { + int arg_reg = mach_reserve_reg(cs); + cJSON *arg = cJSON_GetArrayItem(args, i); + int r = mach_compile_expr(cs, arg, arg_reg); + if (r != arg_reg) + mach_emit(cs, MACH_ABC(MACH_MOVE, arg_reg, r, 0)); + } + + mach_emit(cs, MACH_ABC(MACH_CALLMETHOD, base, nargs, ki)); + mach_free_reg_to(cs, save_freereg); + if (dest >= 0 && dest != base) + mach_emit(cs, MACH_ABC(MACH_MOVE, dest, base, 0)); + else + dest = base; + return dest; + } + /* Save freereg so we can allocate consecutive regs for call */ int save_freereg = cs->freereg; @@ -31320,6 +31531,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { cJSON *right = cJSON_GetObjectItem(node, "right"); int lr = mach_compile_expr(cs, left, -1); + if (cs->freereg <= lr) cs->freereg = lr + 1; /* protect lr from reuse */ int rr = mach_compile_expr(cs, right, -1); MachOpcode op; @@ -31512,6 +31724,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { cJSON *right = cJSON_GetObjectItem(node, "right"); int save = cs->freereg; int lr = mach_compile_expr(cs, left, -1); + if (cs->freereg <= lr) cs->freereg = lr + 1; int rr = mach_compile_expr(cs, right, -1); mach_emit(cs, MACH_ABC(MACH_HASPROP, dest, rr, lr)); mach_free_reg_to(cs, save); @@ -31590,6 +31803,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { if (!prop_name) prop_name = cJSON_GetStringValue(cJSON_GetObjectItem(left, "value")); int obj_r = mach_compile_expr(cs, obj_expr, -1); + if (cs->freereg <= obj_r) cs->freereg = obj_r + 1; int val_r = mach_compile_expr(cs, right, dest); if (prop_name) { int ki = mach_cpool_add_str(cs, prop_name); @@ -31609,7 +31823,9 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { if (!idx_expr) idx_expr = cJSON_GetObjectItem(left, "right"); int obj_r = mach_compile_expr(cs, obj_expr, -1); + if (cs->freereg <= obj_r) cs->freereg = obj_r + 1; int idx_r = mach_compile_expr(cs, idx_expr, -1); + if (cs->freereg <= idx_r) cs->freereg = idx_r + 1; int val_r = mach_compile_expr(cs, right, dest); mach_emit(cs, MACH_ABC(MACH_SETINDEX, obj_r, idx_r, val_r)); mach_free_reg_to(cs, save); @@ -31655,6 +31871,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { if (!idx_expr) idx_expr = cJSON_GetObjectItem(node, "right"); int obj_r = mach_compile_expr(cs, obj_expr, -1); + if (cs->freereg <= obj_r) cs->freereg = obj_r + 1; int idx_r = mach_compile_expr(cs, idx_expr, -1); mach_emit(cs, MACH_ABC(MACH_GETINDEX, dest, obj_r, idx_r)); mach_free_reg_to(cs, save); @@ -32796,9 +33013,21 @@ void __asan_on_error(void) { static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JSValue this_obj, int argc, JSValue *argv, JSValue env, JSValue outer_frame) { + /* Protect env and outer_frame from GC — alloc_frame_register can trigger + collection which moves heap objects, invalidating stack-local copies */ + JSGCRef env_gc, of_gc; + JS_PushGCRef(ctx, &env_gc); + env_gc.val = env; + JS_PushGCRef(ctx, &of_gc); + of_gc.val = outer_frame; + /* Allocate initial frame */ JSFrameRegister *frame = alloc_frame_register(ctx, code->nr_slots); - if (!frame) return JS_EXCEPTION; + if (!frame) { + JS_PopGCRef(ctx, &of_gc); + JS_PopGCRef(ctx, &env_gc); + return JS_EXCEPTION; + } /* Protect frame from GC */ JSGCRef frame_ref; @@ -32810,7 +33039,9 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, /* Setup initial frame — wrap top-level code in a function object so that returning from a called register function can read code/env from frame */ - JSValue top_fn = js_new_register_function(ctx, code, env, outer_frame); + JSValue top_fn = js_new_register_function(ctx, code, env_gc.val, of_gc.val); + JS_PopGCRef(ctx, &of_gc); + JS_PopGCRef(ctx, &env_gc); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame->function = top_fn; frame->slots[0] = this_obj; /* slot 0 = this */ @@ -33018,13 +33249,11 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, int ret; if (JS_IsInt(idx)) { ret = JS_SetPropertyUint32(ctx, obj, JS_VALUE_GET_INT(idx), val); - } else if (JS_IsText(idx) && JS_IsRecord(obj)) { - ret = JS_SetProperty(ctx, obj, idx, val); } else if (JS_IsArray(obj)) { JS_ThrowTypeError(ctx, "array index must be a number"); ret = -1; - } else if (JS_IsRecord(obj)) { - JS_ThrowTypeError(ctx, "object key must be a string"); + } else if (JS_IsRecord(obj) && !JS_IsText(idx) && !JS_IsRecord(idx)) { + JS_ThrowTypeError(ctx, "object key must be a string or object"); ret = -1; } else { ret = JS_SetProperty(ctx, obj, idx, val); @@ -33039,6 +33268,16 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JSValue key = code->cpool[bx]; JSValue val = JS_GetProperty(ctx, ctx->global_obj, key); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsNull(val)) { + int has = JS_HasProperty(ctx, ctx->global_obj, key); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (has <= 0) { + char buf[128]; + JS_KeyGetStr(ctx, buf, sizeof(buf), key); + JS_ThrowReferenceError(ctx, "'%s' is not defined", buf); + goto disrupt; + } + } frame->slots[a] = val; break; } @@ -33186,7 +33425,7 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, for (int i = 0; i < nargs; i++) ctx->value_stack[vs_base + i] = frame->slots[base + 1 + i]; ctx->value_stack_top = vs_base + nargs; - JSValue ret = js_call_c_function(ctx, func_val, JS_NULL, nargs, &ctx->value_stack[vs_base]); + JSValue ret = JS_CallInternal(ctx, func_val, JS_NULL, nargs, &ctx->value_stack[vs_base], 0); ctx->value_stack_top = vs_base; frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(ret)) { goto disrupt; } @@ -33195,6 +33434,101 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, break; } + case MACH_CALLMETHOD: { + /* Method call: R(A)=obj, B=nargs in R(A+2)..R(A+1+B), C=cpool key index + Result stored in R(A). + If obj is a function (proxy): call obj(key_str, [args...]) + Else (record): get property, call property(obj_as_this, args...) */ + int base = a; + int nargs = b; + JSValue obj = frame->slots[base]; + JSValue key = code->cpool[c]; + + if (JS_IsFunction(obj)) { + /* Proxy call: obj(name, [args...]) */ + JSValue arr = JS_NewArray(ctx); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(arr)) goto disrupt; + frame->slots[base + 1] = arr; /* protect from GC in temp slot */ + for (int i = 0; i < nargs; i++) { + JS_SetPropertyUint32(ctx, frame->slots[base + 1], i, frame->slots[base + 2 + i]); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + } + /* Push proxy args onto value stack; re-read obj since GC may have moved it */ + int vs_base = ctx->value_stack_top; + ctx->value_stack[vs_base] = key; + ctx->value_stack[vs_base + 1] = frame->slots[base + 1]; /* the array */ + ctx->value_stack_top = vs_base + 2; + ctx->reg_current_frame = frame_ref.val; + ctx->rt->current_register_pc = pc > 0 ? pc - 1 : 0; + JSValue ret = JS_CallInternal(ctx, frame->slots[base], JS_NULL, 2, &ctx->value_stack[vs_base], 0); + ctx->value_stack_top = vs_base; + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + ctx->reg_current_frame = JS_NULL; + if (JS_IsException(ret)) goto disrupt; + frame->slots[base] = ret; + } else { + /* Record method call: get property, call with this=obj */ + JSValue method = JS_GetProperty(ctx, frame->slots[base], key); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(method)) goto disrupt; + if (!JS_IsFunction(method)) { + frame->slots[base] = JS_NULL; + break; + } + JSFunction *fn = JS_VALUE_GET_FUNCTION(method); + if (fn->kind == JS_FUNC_KIND_C) { + int vs_base = ctx->value_stack_top; + for (int i = 0; i < nargs; i++) + ctx->value_stack[vs_base + i] = frame->slots[base + 2 + i]; + ctx->value_stack_top = vs_base + nargs; + ctx->reg_current_frame = frame_ref.val; + ctx->rt->current_register_pc = pc > 0 ? pc - 1 : 0; + JSValue ret = js_call_c_function(ctx, method, frame->slots[base], nargs, &ctx->value_stack[vs_base]); + ctx->value_stack_top = vs_base; + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + ctx->reg_current_frame = JS_NULL; + if (JS_IsException(ret)) goto disrupt; + frame->slots[base] = ret; + } else if (fn->kind == JS_FUNC_KIND_REGISTER) { + JSCodeRegister *fn_code = fn->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); + goto disrupt; + } + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + method = JS_GetProperty(ctx, frame->slots[base], key); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + fn = JS_VALUE_GET_FUNCTION(method); + new_frame->function = method; + new_frame->slots[0] = frame->slots[base]; /* this */ + for (int i = 0; i < nargs && i < fn_code->arity; i++) + new_frame->slots[1 + i] = frame->slots[base + 2 + i]; + int ret_slot = base; + frame->address = JS_NewInt32(ctx, (pc << 16) | ret_slot); + new_frame->caller = JS_MKPTR(frame); + frame = new_frame; + frame_ref.val = JS_MKPTR(frame); + code = fn_code; + env = fn->u.reg.env_record; + pc = code->entry_point; + } else { + /* Bytecode or other function */ + int vs_base = ctx->value_stack_top; + for (int i = 0; i < nargs; i++) + ctx->value_stack[vs_base + i] = frame->slots[base + 2 + i]; + ctx->value_stack_top = vs_base + nargs; + JSValue ret = JS_CallInternal(ctx, method, frame->slots[base], nargs, &ctx->value_stack[vs_base], 0); + ctx->value_stack_top = vs_base; + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(ret)) goto disrupt; + frame->slots[base] = ret; + } + } + break; + } + case MACH_RETURN: result = frame->slots[a]; if (JS_IsNull(frame->caller)) goto done;