fix mach proxy and templates
This commit is contained in:
366
source/quickjs.c
366
source/quickjs.c
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user