mcode fix tests

This commit is contained in:
2026-02-07 14:19:17 -06:00

View File

@@ -4098,6 +4098,7 @@ static int js_array_grow (JSContext *ctx, JSValue *arr_ptr, word_t min_cap) {
new_arr->mist_hdr = objhdr_make (new_cap, OBJ_ARRAY, false, false, false, false);
new_arr->len = arr->len;
JSValue old_ptr = *arr_ptr;
for (word_t i = 0; i < arr->len; i++)
new_arr->values[i] = arr->values[i];
for (word_t i = arr->len; i < new_cap; i++)
@@ -4109,6 +4110,12 @@ static int js_array_grow (JSContext *ctx, JSValue *arr_ptr, word_t min_cap) {
/* Update the tracked JSValue to point to new array */
*arr_ptr = JS_MKPTR (new_arr);
/* Fix self-references: update elements that pointed to the old array */
for (word_t i = 0; i < new_arr->len; i++) {
if (new_arr->values[i] == old_ptr)
new_arr->values[i] = *arr_ptr;
}
return 0;
}
@@ -4921,15 +4928,8 @@ int JS_SetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop, JSVal
obj_ref.val = this_obj;
val_ref.val = val;
/* Create JSValue key from string - try immediate ASCII first */
int len = strlen (prop);
JSValue key;
if (len <= MIST_ASCII_MAX_LEN) {
key = MIST_TryNewImmediateASCII (prop, len);
if (JS_IsNull (key)) { key = js_new_string8_len (ctx, prop, len); }
} else {
key = js_new_string8_len (ctx, prop, len);
}
/* Create JSValue key from string - use js_key_new for interned stone keys */
JSValue key = js_key_new (ctx, prop);
if (JS_IsException (key)) {
JS_DeleteGCRef (ctx, &val_ref);
JS_DeleteGCRef (ctx, &obj_ref);
@@ -23441,7 +23441,7 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu
}
/* array(object) - keys */
if (JS_IsObject (arg) && !JS_IsArray (arg)) {
if (JS_IsRecord (arg)) {
/* Return object keys */
return JS_GetOwnPropertyNames (ctx, arg);
}
@@ -25481,10 +25481,8 @@ static JSValue js_cell_proto (JSContext *ctx, JSValue this_val, int argc, JSValu
JSValue obj = argv[0];
/* Arrays cannot have prototypes */
if (JS_IsArray (obj)) {
JS_ThrowTypeError (ctx, "cannot get prototype of array");
return JS_EXCEPTION;
return JS_ThrowTypeError (ctx, "arrays do not have prototypes");
}
if (!JS_IsObject (obj)) return JS_NULL;
@@ -30273,9 +30271,7 @@ static int ast_sem_in_loop (ASTSemScope *scope) {
static void ast_sem_check_expr (ASTSemState *st, ASTSemScope *scope, cJSON *expr);
static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt);
/* Pre-register function declarations in a statement list (hoisting).
This allows mutual recursion: function A can reference function B declared later. */
static void ast_sem_predeclare_vars (ASTSemScope *scope, cJSON *stmts) {
static void ast_sem_predeclare_vars (ASTSemState *st, ASTSemScope *scope, cJSON *stmts) {
cJSON *stmt;
cJSON_ArrayForEach (stmt, stmts) {
const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (stmt, "kind"));
@@ -30284,6 +30280,22 @@ static void ast_sem_predeclare_vars (ASTSemScope *scope, cJSON *stmts) {
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (stmt, "name"));
if (name && !ast_sem_find_var (scope, name))
ast_sem_add_var (scope, name, 0, "function", scope->function_nr);
} else if (strcmp (kind, "var") == 0) {
cJSON *left = cJSON_GetObjectItem (stmt, "left");
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name"));
if (name && !ast_sem_find_var (scope, name))
ast_sem_add_var (scope, name, 0, kind, scope->function_nr);
} else if (strcmp (kind, "var_list") == 0) {
cJSON *item;
cJSON_ArrayForEach (item, cJSON_GetObjectItem (stmt, "list")) {
const char *ik = cJSON_GetStringValue (cJSON_GetObjectItem (item, "kind"));
if (ik && strcmp (ik, "var") == 0) {
cJSON *left = cJSON_GetObjectItem (item, "left");
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name"));
if (name && !ast_sem_find_var (scope, name))
ast_sem_add_var (scope, name, 0, ik, scope->function_nr);
}
}
}
}
}
@@ -30468,12 +30480,13 @@ static void ast_sem_check_expr (ASTSemState *st, ASTSemScope *scope, cJSON *expr
if (def_val) ast_sem_check_expr (st, &fn_scope, def_val);
}
/* Pre-register all var/def/function declarations for mutual recursion */
ast_sem_predeclare_vars (&fn_scope, cJSON_GetObjectItem (expr, "statements"));
/* Pre-register all declarations for mutual recursion / forward references */
cJSON *fn_stmts = cJSON_GetObjectItem (expr, "statements");
ast_sem_predeclare_vars (st, &fn_scope, fn_stmts);
/* Check function body */
cJSON *stmt;
cJSON_ArrayForEach (stmt, cJSON_GetObjectItem (expr, "statements")) {
cJSON_ArrayForEach (stmt, fn_stmts) {
ast_sem_check_stmt (st, &fn_scope, stmt);
}
@@ -30549,7 +30562,9 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
if (existing && existing->is_const) {
ast_sem_error (st, left, "cannot redeclare constant '%s'", name);
}
ast_sem_add_var (scope, name, 0, "var", scope->function_nr);
if (!existing || existing->function_nr != scope->function_nr
|| scope->block_depth > 0)
ast_sem_add_var (scope, name, 0, "var", scope->function_nr);
if (scope->block_depth > 0) {
char buf[128];
snprintf (buf, sizeof (buf), "_%s_%d", name, st->block_var_counter++);
@@ -30571,17 +30586,20 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
ASTSemVar *existing = ast_sem_find_var (scope, name);
if (existing && existing->is_const) {
ast_sem_error (st, left, "cannot redeclare constant '%s'", name);
} else if (existing) {
ast_sem_error (st, left, "cannot redeclare '%s' as constant", name);
}
ast_sem_add_var (scope, name, 1, "def", scope->function_nr);
if (scope->block_depth > 0) {
char buf[128];
snprintf (buf, sizeof (buf), "_%s_%d", name, st->block_var_counter++);
char *sn = sys_malloc (strlen (buf) + 1);
strcpy (sn, buf);
scope->vars[scope->var_count - 1].scope_name = sn;
cJSON_AddStringToObject (left, "scope_name", sn);
} else if (existing && !existing->is_const && existing->function_nr == scope->function_nr) {
/* Pre-scanned as var, now upgrading to const */
existing->is_const = 1;
existing->make = "def";
} else {
ast_sem_add_var (scope, name, 1, "def", scope->function_nr);
if (scope->block_depth > 0) {
char buf[128];
snprintf (buf, sizeof (buf), "_%s_%d", name, st->block_var_counter++);
char *sn = sys_malloc (strlen (buf) + 1);
strcpy (sn, buf);
scope->vars[scope->var_count - 1].scope_name = sn;
cJSON_AddStringToObject (left, "scope_name", sn);
}
}
}
ast_sem_check_expr (st, scope, cJSON_GetObjectItem (stmt, "right"));
@@ -30751,7 +30769,7 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
}
/* Pre-register all var/def/function declarations for mutual recursion */
ast_sem_predeclare_vars (&fn_scope, cJSON_GetObjectItem (stmt, "statements"));
ast_sem_predeclare_vars (st, &fn_scope, cJSON_GetObjectItem (stmt, "statements"));
cJSON *s2;
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "statements")) {
@@ -31498,6 +31516,46 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
return dest;
}
if (fn_kind && strcmp(fn_kind, "[") == 0) {
/* Method call with bracket notation: obj[expr](args) */
int save_freereg = cs->freereg;
int base = mach_reserve_reg(cs); /* R(base) = obj */
if (dest < 0) dest = base;
int key_reg = mach_reserve_reg(cs); /* R(base+1) = key */
/* 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));
/* Compile key expr into R(base+1) */
cJSON *idx_expr = cJSON_GetObjectItem(fn_expr, "index");
if (!idx_expr) idx_expr = cJSON_GetObjectItem(fn_expr, "right");
int kr = mach_compile_expr(cs, idx_expr, key_reg);
if (kr != key_reg)
mach_emit(cs, MACH_ABC(MACH_MOVE, key_reg, kr, 0));
/* 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));
}
/* C=0xFF signals key is in R(base+1) */
mach_emit(cs, MACH_ABC(MACH_CALLMETHOD, base, nargs, 0xFF));
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;
@@ -31937,16 +31995,26 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
cJSON *elems = cJSON_GetObjectItem(node, "list");
int count = elems ? cJSON_GetArraySize(elems) : 0;
/* Reserve consecutive regs for elements starting at dest+1 */
/* Reserve consecutive regs for elements starting at arr_base+1.
If dest is below freereg, other temps occupy dest+1..freereg-1
so we must use a fresh base to avoid clobbering them. */
int save = cs->freereg;
cs->freereg = dest + 1;
int arr_base;
if (dest + 1 >= cs->freereg) {
arr_base = dest;
cs->freereg = dest + 1;
} else {
arr_base = mach_reserve_reg(cs);
}
for (int i = 0; i < count; i++) {
int er = mach_reserve_reg(cs);
cJSON *elem = cJSON_GetArrayItem(elems, i);
int r = mach_compile_expr(cs, elem, er);
if (r != er) mach_emit(cs, MACH_ABC(MACH_MOVE, er, r, 0));
}
mach_emit(cs, MACH_ABC(MACH_NEWARRAY, dest, count, 0));
mach_emit(cs, MACH_ABC(MACH_NEWARRAY, arr_base, count, 0));
if (arr_base != dest)
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, arr_base, 0));
mach_free_reg_to(cs, save);
return dest;
}
@@ -32858,10 +32926,14 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
/* Comparison ops allow mixed types — return false for mismatches */
if (op >= MACH_EQ && op <= MACH_GE) {
/* Fast path: bitwise-identical values (same object/pointer) */
if (a == b) {
if (op == MACH_EQ || op == MACH_LE || op == MACH_GE) return JS_TRUE;
if (op == MACH_NEQ) return JS_FALSE;
/* Fast path: identical values (chase pointers for forwarded objects) */
{
JSValue ca = JS_IsPtr(a) ? JS_MKPTR(chase(a)) : a;
JSValue cb = JS_IsPtr(b) ? JS_MKPTR(chase(b)) : b;
if (ca == cb) {
if (op == MACH_EQ || op == MACH_LE || op == MACH_GE) return JS_TRUE;
if (op == MACH_NEQ) return JS_FALSE;
}
}
if (JS_IsNumber(a) && JS_IsNumber(b)) {
double da, db;
@@ -33460,15 +33532,14 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
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).
Result stored in R(A). C=0xFF means key is in R(A+1).
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];
JSValue key = (c == 0xFF) ? frame->slots[base + 1] : code->cpool[c];
if (JS_IsFunction(obj)) {
if (JS_IsFunction(frame->slots[base]) && JS_IsText(key)) {
/* Proxy call: obj(name, [args...]) */
JSValue arr = JS_NewArray(ctx);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
@@ -33491,6 +33562,10 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
ctx->reg_current_frame = JS_NULL;
if (JS_IsException(ret)) goto disrupt;
frame->slots[base] = ret;
} else if (JS_IsFunction(frame->slots[base])) {
/* Non-proxy function with non-text key: disrupt */
JS_ThrowTypeError(ctx, "cannot use bracket notation on non-proxy function");
goto disrupt;
} else {
/* Record method call: get property, call with this=obj */
JSValue method = JS_GetProperty(ctx, frame->slots[base], key);
@@ -34054,6 +34129,21 @@ static void mach_gen_emit_call_method (MachGenState *s, int dest, int obj, const
mach_gen_add_instr (s, instr);
}
static void mach_gen_emit_call_method_dyn (MachGenState *s, int dest, int obj, int key_reg, cJSON *args) {
/* Emit a dynamic callmethod instruction:
["callmethod_dyn", dest, obj_reg, key_reg, arg_reg0, arg_reg1, ...] */
cJSON *instr = cJSON_CreateArray ();
cJSON_AddItemToArray (instr, cJSON_CreateString ("callmethod_dyn"));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj));
cJSON_AddItemToArray (instr, cJSON_CreateNumber (key_reg));
cJSON *arg;
cJSON_ArrayForEach (arg, args) {
cJSON_AddItemToArray (instr, cJSON_CreateNumber (arg->valueint));
}
mach_gen_add_instr (s, instr);
}
static void mach_gen_emit_go_call (MachGenState *s, int func_slot, cJSON *args) {
int argc = cJSON_GetArraySize (args);
int frame_slot = mach_gen_alloc_slot (s);
@@ -34497,6 +34587,12 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
const char *prop = cJSON_GetStringValue (cJSON_GetObjectItem (callee, "right"));
int obj_slot = mach_gen_expr (s, obj, -1);
mach_gen_emit_call_method (s, dest, obj_slot, prop, arg_slots);
} else if (strcmp (callee_kind, "[") == 0) {
cJSON *obj = cJSON_GetObjectItem (callee, "left");
cJSON *key_expr = cJSON_GetObjectItem (callee, "right");
int obj_slot = mach_gen_expr (s, obj, -1);
int key_slot = mach_gen_expr (s, key_expr, -1);
mach_gen_emit_call_method_dyn (s, dest, obj_slot, key_slot, arg_slots);
} else {
int func_slot = mach_gen_expr (s, callee, -1);
mach_gen_emit_call (s, dest, func_slot, arg_slots);
@@ -35197,8 +35293,8 @@ static cJSON *mach_gen_program (MachGenState *s, cJSON *ast) {
s->next_temp_slot = 1 + s->nr_local_slots;
if (s->next_temp_slot > s->max_slot) s->max_slot = s->next_temp_slot;
/* Pre-load intrinsics (global names) */
mach_gen_load_intrinsics (s, cJSON_GetObjectItem (ast, "intrinsics"));
/* Intrinsics are loaded lazily at point of use instead of pre-loading,
to avoid loading nested function intrinsics in the wrong scope. */
/* Compile hoisted function declarations from ast["functions"] */
cJSON *hoisted = cJSON_GetObjectItem (ast, "functions");
@@ -35519,6 +35615,17 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
JSValue val = JS_GetProperty(ctx, ctx->global_obj, key);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsNull(val)) {
key = JS_NewString(ctx, iname);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
int has = JS_HasProperty(ctx, ctx->global_obj, key);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (has <= 0) {
JS_ThrowReferenceError(ctx, "'%s' is not defined", iname);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
}
}
frame->slots[dest] = val;
} else {
frame->slots[dest] = JS_NULL;
@@ -36289,10 +36396,19 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
} else {
JSValue idx = frame->slots[(int)a3->valuedouble];
int ret;
if (JS_IsInt(idx))
if (JS_IsInt(idx)) {
ret = JS_SetPropertyUint32(ctx, obj, JS_VALUE_GET_INT(idx), val);
else
} else if (JS_IsArray(obj)) {
JS_ThrowTypeError(ctx, "array index must be a number");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
} else if (JS_IsRecord(obj) && !JS_IsText(idx) && !JS_IsRecord(idx)) {
JS_ThrowTypeError(ctx, "object key must be a string or object");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
} else {
ret = JS_SetProperty(ctx, obj, idx, val);
}
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (ret < 0) goto disrupt;
}
@@ -36406,16 +36522,17 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
code = fn->u.mcode.code;
pc = 0;
} else {
/* C or bytecode function — collect args from frame slots */
/* C or bytecode function — collect args on value stack (GC-safe) */
int nr_slots = (int)objhdr_cap56(new_frame->hdr);
int c_argc = (nr_slots >= 2) ? nr_slots - 2 : 0;
if (c_argc > 16) c_argc = 16;
JSValue c_argv[16];
int vs_base = ctx->value_stack_top;
for (int i = 0; i < c_argc; i++)
c_argv[i] = new_frame->slots[i + 1];
ctx->value_stack[vs_base + i] = new_frame->slots[i + 1];
ctx->value_stack_top = vs_base + c_argc;
ctx->reg_current_frame = frame_ref.val;
ctx->rt->current_register_pc = pc > 0 ? pc - 1 : 0;
JSValue c_result = JS_Call(ctx, new_frame->function, new_frame->slots[0], c_argc, c_argv);
JSValue c_result = JS_Call(ctx, new_frame->function, new_frame->slots[0], c_argc, &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(c_result)) { goto disrupt; }
@@ -36430,14 +36547,18 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
int obj_reg = (int)a2->valuedouble;
JSValue obj = frame->slots[obj_reg];
const char *method_name = a3->valuestring;
/* Count arg registers (items after a3) */
/* Count arg registers (items after a3, minus trailing line/col) */
int nargs = 0;
for (cJSON *p = a3->next; p && !cJSON_IsString(p); p = p->next)
for (cJSON *p = a3->next; p; p = p->next)
nargs++;
nargs -= 2; /* subtract line and col metadata */
if (nargs < 0) nargs = 0;
if (JS_IsFunction(obj)) {
/* Proxy call: obj(name, [args...]) */
JSValue key = JS_NewString(ctx, method_name);
/* Store key on value stack immediately to protect from GC */
int vs_base = ctx->value_stack_top;
ctx->value_stack[vs_base] = JS_NewString(ctx, method_name);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
JSValue arr = JS_NewArray(ctx);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
@@ -36450,8 +36571,6 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
JS_SetPropertyUint32(ctx, frame->slots[dest], i, frame->slots[areg]);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
}
int vs_base = ctx->value_stack_top;
ctx->value_stack[vs_base] = key;
ctx->value_stack[vs_base + 1] = frame->slots[dest];
ctx->value_stack_top = vs_base + 2;
ctx->reg_current_frame = frame_ref.val;
@@ -36516,6 +36635,99 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
}
}
else if (strcmp(op, "callmethod_dyn") == 0) {
/* ["callmethod_dyn", dest, obj_reg, key_reg, arg0_reg, ...] */
int dest = (int)a1->valuedouble;
int obj_reg = (int)a2->valuedouble;
int key_reg = (int)a3->valuedouble;
JSValue obj = frame->slots[obj_reg];
JSValue key = frame->slots[key_reg];
/* Count arg registers (items after a3, minus trailing line/col) */
int nargs = 0;
for (cJSON *p = a3->next; p; p = p->next)
nargs++;
nargs -= 2;
if (nargs < 0) nargs = 0;
if (JS_IsFunction(obj) && JS_VALUE_IS_TEXT(key)) {
/* Proxy call: obj(key, [args...]) */
int vs_base = ctx->value_stack_top;
ctx->value_stack[vs_base] = key; /* protect key on value stack */
JSValue arr = JS_NewArray(ctx);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsException(arr)) goto disrupt;
frame->slots[dest] = arr; /* protect from GC */
cJSON *p = a3->next;
for (int i = 0; i < nargs; i++, p = p->next) {
int areg = (int)p->valuedouble;
JS_SetPropertyUint32(ctx, frame->slots[dest], i, frame->slots[areg]);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
}
ctx->value_stack[vs_base + 1] = frame->slots[dest];
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[obj_reg], 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[dest] = ret;
} else if (JS_IsFunction(obj)) {
JS_ThrowTypeError(ctx, "cannot use non-text bracket notation on function");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
} else {
/* Record method call: get property, call with this=obj */
JSValue method = JS_GetProperty(ctx, frame->slots[obj_reg], key);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsException(method)) goto disrupt;
if (!JS_IsFunction(method)) {
frame->slots[dest] = JS_NULL;
} else {
JSFunction *fn = JS_VALUE_GET_FUNCTION(method);
if (fn->kind == JS_FUNC_KIND_MCODE) {
JSFrameRegister *new_frame = alloc_frame_register(ctx, fn->u.mcode.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[obj_reg], frame->slots[key_reg]);
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[obj_reg]; /* this */
cJSON *p = a3->next;
for (int i = 0; i < nargs && i < fn->u.mcode.code->nr_slots - 1; i++, p = p->next) {
new_frame->slots[1 + i] = frame->slots[(int)p->valuedouble];
}
frame->address = JS_NewInt32(ctx, (pc << 16) | dest);
new_frame->caller = JS_MKPTR(frame);
frame = new_frame;
frame_ref.val = JS_MKPTR(frame);
code = fn->u.mcode.code;
pc = 0;
} else {
int vs_base = ctx->value_stack_top;
cJSON *p = a3->next;
for (int i = 0; i < nargs; i++, p = p->next) {
ctx->value_stack[vs_base + i] = frame->slots[(int)p->valuedouble];
}
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(ctx, method, frame->slots[obj_reg], 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[dest] = ret;
}
}
}
}
/* ---- Tail calls ---- */
else if (strcmp(op, "goframe") == 0) {
int frame_reg = (int)a1->valuedouble;
@@ -36727,14 +36939,12 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
JS_PopGCRef(ctx, &arr_gc);
if (rc < 0) goto disrupt;
if (arr_gc.val != frame->slots[arr_slot]) {
frame->slots[arr_slot] = arr_gc.val;
/* If we pushed the array onto itself and it was relocated,
fix the dangling reference in the last element */
if (val_slot == arr_slot) {
JSArray *a = JS_VALUE_GET_ARRAY(arr_gc.val);
a->values[a->len - 1] = arr_gc.val;
}
frame->slots[arr_slot] = arr_gc.val;
/* If we pushed the array onto itself, the stored val may be stale
(val was passed by value to JS_ArrayPush before GC could update it) */
if (val_slot == arr_slot) {
JSArray *a = JS_VALUE_GET_ARRAY(arr_gc.val);
a->values[a->len - 1] = arr_gc.val;
}
}