fix vm suite tests
This commit is contained in:
114
source/quickjs.c
114
source/quickjs.c
@@ -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,6 +30552,8 @@ 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);
|
||||
}
|
||||
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];
|
||||
@@ -30550,9 +30576,11 @@ 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);
|
||||
}
|
||||
} 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];
|
||||
@@ -30563,6 +30591,7 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
|
||||
cJSON_AddStringToObject (left, "scope_name", sn);
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_sem_check_expr (st, scope, cJSON_GetObjectItem (stmt, "right"));
|
||||
return;
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user