fix mach proxy and templates

This commit is contained in:
2026-02-07 11:53:39 -06:00
parent 8e2607b6ca
commit 836227c8d3

View File

@@ -568,6 +568,8 @@ typedef enum MachOpcode {
MACH_HASPROP, /* R(A) = R(C) in R(B) — has property check */ MACH_HASPROP, /* R(A) = R(C) in R(B) — has property check */
MACH_REGEXP, /* R(A) = regexp(K(B), K(C)) — regex literal */ 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_NOP,
MACH_OP_COUNT MACH_OP_COUNT
@@ -629,6 +631,7 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
[MACH_DELETEINDEX] = "deleteindex", [MACH_DELETEINDEX] = "deleteindex",
[MACH_HASPROP] = "hasprop", [MACH_HASPROP] = "hasprop",
[MACH_REGEXP] = "regexp", [MACH_REGEXP] = "regexp",
[MACH_CALLMETHOD] = "callmethod",
[MACH_NOP] = "nop", [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) { 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)ctx;
(void)tab;
(void)len; (void)len;
js_free_rt(tab);
} }
/* XXX: should use ValueArray */ /* 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); ctx, "too many arguments in function call (only %d allowed)", JS_MAX_LOCAL_VARS);
return NULL; 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; 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++) { for (i = 0; i < len; i++) {
tab[i] = arr->values[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 */ /* Get value from collection */
JSValue coll_value = JS_NULL; JSValue coll_value = JS_NULL;
if (is_array) { 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; 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); coll_value = JS_GetPropertyUint32 (ctx, collection, (uint32_t)idx);
} }
} else { } else {
@@ -25912,11 +25924,91 @@ static void JS_AddIntrinsicBasicObjects (JSContext *ctx) {
JS_PopGCRef (ctx, &proto_ref); 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 /* 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. */ 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) { static void js_set_global_cfunc(JSContext *ctx, const char *name, JSCFunction *func, int length) {
JSValue fn = JS_NewCFunction(ctx, func, name, length); JSGCRef ref;
JS_SetPropertyStr(ctx, ctx->global_obj, name, fn); 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) { 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, "pop", js_cell_pop, 1);
js_set_global_cfunc(ctx, "meme", js_cell_meme, 2); 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 */ /* I/O functions */
js_set_global_cfunc(ctx, "print", js_print, -1); /* variadic: length < 0 means no arg limit */ 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); 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 */ /* Template literal */
if (strcmp (kind, "template") == 0) { if (strcmp (kind, "template") == 0 || strcmp (kind, "text literal") == 0) {
cJSON *el; cJSON *el;
cJSON_ArrayForEach (el, cJSON_GetObjectItem (expr, "list")) { cJSON_ArrayForEach (el, cJSON_GetObjectItem (expr, "list")) {
ast_sem_check_expr (st, scope, el); ast_sem_check_expr (st, scope, el);
@@ -31203,6 +31313,58 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
return 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 */ /* Boolean/null literals */
if (strcmp(kind, "true") == 0) { if (strcmp(kind, "true") == 0) {
if (dest < 0) dest = mach_reserve_reg(cs); 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"); cJSON *args = cJSON_GetObjectItem(node, "list");
int nargs = args ? cJSON_GetArraySize(args) : 0; 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 */ /* Save freereg so we can allocate consecutive regs for call */
int save_freereg = cs->freereg; 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"); cJSON *right = cJSON_GetObjectItem(node, "right");
int lr = mach_compile_expr(cs, left, -1); 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); int rr = mach_compile_expr(cs, right, -1);
MachOpcode op; MachOpcode op;
@@ -31512,6 +31724,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
cJSON *right = cJSON_GetObjectItem(node, "right"); cJSON *right = cJSON_GetObjectItem(node, "right");
int save = cs->freereg; int save = cs->freereg;
int lr = mach_compile_expr(cs, left, -1); int lr = mach_compile_expr(cs, left, -1);
if (cs->freereg <= lr) cs->freereg = lr + 1;
int rr = mach_compile_expr(cs, right, -1); int rr = mach_compile_expr(cs, right, -1);
mach_emit(cs, MACH_ABC(MACH_HASPROP, dest, rr, lr)); mach_emit(cs, MACH_ABC(MACH_HASPROP, dest, rr, lr));
mach_free_reg_to(cs, save); 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")); if (!prop_name) prop_name = cJSON_GetStringValue(cJSON_GetObjectItem(left, "value"));
int obj_r = mach_compile_expr(cs, obj_expr, -1); 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); int val_r = mach_compile_expr(cs, right, dest);
if (prop_name) { if (prop_name) {
int ki = mach_cpool_add_str(cs, 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"); if (!idx_expr) idx_expr = cJSON_GetObjectItem(left, "right");
int obj_r = mach_compile_expr(cs, obj_expr, -1); 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); 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); int val_r = mach_compile_expr(cs, right, dest);
mach_emit(cs, MACH_ABC(MACH_SETINDEX, obj_r, idx_r, val_r)); mach_emit(cs, MACH_ABC(MACH_SETINDEX, obj_r, idx_r, val_r));
mach_free_reg_to(cs, save); 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"); if (!idx_expr) idx_expr = cJSON_GetObjectItem(node, "right");
int obj_r = mach_compile_expr(cs, obj_expr, -1); 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); int idx_r = mach_compile_expr(cs, idx_expr, -1);
mach_emit(cs, MACH_ABC(MACH_GETINDEX, dest, obj_r, idx_r)); mach_emit(cs, MACH_ABC(MACH_GETINDEX, dest, obj_r, idx_r));
mach_free_reg_to(cs, save); mach_free_reg_to(cs, save);
@@ -32796,9 +33013,21 @@ void __asan_on_error(void) {
static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
JSValue this_obj, int argc, JSValue *argv, JSValue this_obj, int argc, JSValue *argv,
JSValue env, JSValue outer_frame) { 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 */ /* Allocate initial frame */
JSFrameRegister *frame = alloc_frame_register(ctx, code->nr_slots); 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 */ /* Protect frame from GC */
JSGCRef frame_ref; 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 /* 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 */ 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 = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
frame->function = top_fn; frame->function = top_fn;
frame->slots[0] = this_obj; /* slot 0 = this */ frame->slots[0] = this_obj; /* slot 0 = this */
@@ -33018,13 +33249,11 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
int ret; int ret;
if (JS_IsInt(idx)) { if (JS_IsInt(idx)) {
ret = JS_SetPropertyUint32(ctx, obj, JS_VALUE_GET_INT(idx), val); 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)) { } else if (JS_IsArray(obj)) {
JS_ThrowTypeError(ctx, "array index must be a number"); JS_ThrowTypeError(ctx, "array index must be a number");
ret = -1; ret = -1;
} else if (JS_IsRecord(obj)) { } else if (JS_IsRecord(obj) && !JS_IsText(idx) && !JS_IsRecord(idx)) {
JS_ThrowTypeError(ctx, "object key must be a string"); JS_ThrowTypeError(ctx, "object key must be a string or object");
ret = -1; ret = -1;
} else { } else {
ret = JS_SetProperty(ctx, obj, idx, val); 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 key = code->cpool[bx];
JSValue val = JS_GetProperty(ctx, ctx->global_obj, key); JSValue val = JS_GetProperty(ctx, ctx->global_obj, key);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); 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; frame->slots[a] = val;
break; break;
} }
@@ -33186,7 +33425,7 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
for (int i = 0; i < nargs; i++) for (int i = 0; i < nargs; i++)
ctx->value_stack[vs_base + i] = frame->slots[base + 1 + i]; ctx->value_stack[vs_base + i] = frame->slots[base + 1 + i];
ctx->value_stack_top = vs_base + nargs; 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; ctx->value_stack_top = vs_base;
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsException(ret)) { goto disrupt; } if (JS_IsException(ret)) { goto disrupt; }
@@ -33195,6 +33434,101 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
break; 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: case MACH_RETURN:
result = frame->slots[a]; result = frame->slots[a];
if (JS_IsNull(frame->caller)) goto done; if (JS_IsNull(frame->caller)) goto done;