fix vm suite tests

This commit is contained in:
2026-02-07 12:34:18 -06:00
parent 836227c8d3
commit 31d67f6710

View File

@@ -4921,15 +4921,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 +23434,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,9 +25474,9 @@ static JSValue js_cell_proto (JSContext *ctx, JSValue this_val, int argc, JSValu
JSValue obj = argv[0];
/* Intrinsic arrays return the Object prototype */
/* Intrinsic arrays do not have prototypes - disrupt */
if (JS_IsArray (obj)) {
return ctx->class_proto[JS_CLASS_OBJECT];
return JS_ThrowTypeError (ctx, "arrays do not have prototypes");
}
if (!JS_IsObject (obj)) return JS_NULL;
@@ -30272,6 +30265,33 @@ 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-scan statements to add var/def names to scope before processing bodies.
This allows forward references (e.g., mutual recursion). */
static void ast_sem_prescan_vars (ASTSemState *st, ASTSemScope *scope, cJSON *stmts) {
cJSON *stmt;
cJSON_ArrayForEach (stmt, stmts) {
const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (stmt, "kind"));
if (!kind) continue;
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);
}
}
}
}
}
/* Check whether an expression is being assigned to (=, +=, etc.) */
static void ast_sem_check_assign_target (ASTSemState *st, ASTSemScope *scope, cJSON *left) {
if (!left) return;
@@ -30450,9 +30470,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-scan for forward references (e.g., mutual recursion) */
cJSON *fn_stmts = cJSON_GetObjectItem (expr, "statements");
ast_sem_prescan_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);
}
@@ -30528,7 +30552,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++);
@@ -30550,17 +30576,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"));
@@ -31474,6 +31503,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;
@@ -33436,15 +33505,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);
@@ -33467,6 +33535,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);
@@ -33693,7 +33765,7 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
break;
}
if (JS_IsNull(frame->caller)) {
result = JS_NULL;
result = JS_EXCEPTION;
goto done;
}
/* Unwind one frame — read caller's saved pc from its address field */