mcode fix tests
This commit is contained in:
340
source/quickjs.c
340
source/quickjs.c
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user