fixes
This commit is contained in:
233
source/quickjs.c
233
source/quickjs.c
@@ -25481,9 +25481,10 @@ static JSValue js_cell_proto (JSContext *ctx, JSValue this_val, int argc, JSValu
|
||||
|
||||
JSValue obj = argv[0];
|
||||
|
||||
/* Intrinsic arrays return the Object prototype */
|
||||
/* Arrays cannot have prototypes */
|
||||
if (JS_IsArray (obj)) {
|
||||
return ctx->class_proto[JS_CLASS_OBJECT];
|
||||
JS_ThrowTypeError (ctx, "cannot get prototype of array");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
if (!JS_IsObject (obj)) return JS_NULL;
|
||||
@@ -30272,6 +30273,21 @@ 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) {
|
||||
cJSON *stmt;
|
||||
cJSON_ArrayForEach (stmt, stmts) {
|
||||
const char *kind = cJSON_GetStringValue (cJSON_GetObjectItem (stmt, "kind"));
|
||||
if (!kind) continue;
|
||||
if (strcmp (kind, "function") == 0) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
@@ -30452,6 +30468,9 @@ 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"));
|
||||
|
||||
/* Check function body */
|
||||
cJSON *stmt;
|
||||
cJSON_ArrayForEach (stmt, cJSON_GetObjectItem (expr, "statements")) {
|
||||
@@ -30731,6 +30750,9 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
|
||||
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 (stmt, "statements"));
|
||||
|
||||
cJSON *s2;
|
||||
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "statements")) {
|
||||
ast_sem_check_stmt (st, &fn_scope, s2);
|
||||
@@ -34018,18 +34040,18 @@ static void mach_gen_emit_call (MachGenState *s, int dest, int func_slot, cJSON
|
||||
}
|
||||
|
||||
static void mach_gen_emit_call_method (MachGenState *s, int dest, int obj, const char *prop, cJSON *args) {
|
||||
int func_slot = mach_gen_alloc_slot (s);
|
||||
mach_gen_emit_get_prop (s, func_slot, obj, prop);
|
||||
int argc = cJSON_GetArraySize (args);
|
||||
int frame_slot = mach_gen_alloc_slot (s);
|
||||
mach_gen_emit_3 (s, "frame", frame_slot, func_slot, argc);
|
||||
mach_gen_emit_3 (s, "setarg", frame_slot, 0, obj);
|
||||
int arg_idx = 1;
|
||||
/* Emit a single callmethod instruction:
|
||||
["callmethod", dest, obj_reg, "method_name", arg_reg0, arg_reg1, ...] */
|
||||
cJSON *instr = cJSON_CreateArray ();
|
||||
cJSON_AddItemToArray (instr, cJSON_CreateString ("callmethod"));
|
||||
cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest));
|
||||
cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj));
|
||||
cJSON_AddItemToArray (instr, cJSON_CreateString (prop));
|
||||
cJSON *arg;
|
||||
cJSON_ArrayForEach (arg, args) {
|
||||
mach_gen_emit_3 (s, "setarg", frame_slot, arg_idx++, arg->valueint);
|
||||
cJSON_AddItemToArray (instr, cJSON_CreateNumber (arg->valueint));
|
||||
}
|
||||
mach_gen_emit_2 (s, "invoke", frame_slot, dest);
|
||||
mach_gen_add_instr (s, instr);
|
||||
}
|
||||
|
||||
static void mach_gen_emit_go_call (MachGenState *s, int func_slot, cJSON *args) {
|
||||
@@ -34315,6 +34337,62 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
|
||||
mach_gen_emit_const_str (s, slot, val ? val : "");
|
||||
return slot;
|
||||
}
|
||||
/* 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) {
|
||||
cJSON *list = cJSON_GetObjectItem (expr, "list");
|
||||
int nexpr = list ? cJSON_GetArraySize (list) : 0;
|
||||
/* Evaluate each expression */
|
||||
int *expr_slots = NULL;
|
||||
if (nexpr > 0) {
|
||||
expr_slots = alloca (nexpr * sizeof (int));
|
||||
for (int i = 0; i < nexpr; i++) {
|
||||
cJSON *item = cJSON_GetArrayItem (list, i);
|
||||
expr_slots[i] = mach_gen_expr (s, item, -1);
|
||||
}
|
||||
}
|
||||
/* Create array from expression results using the "array" opcode */
|
||||
int arr_slot = mach_gen_alloc_slot (s);
|
||||
{
|
||||
cJSON *instr = cJSON_CreateArray ();
|
||||
cJSON_AddItemToArray (instr, cJSON_CreateString ("array"));
|
||||
cJSON_AddItemToArray (instr, cJSON_CreateNumber (arr_slot));
|
||||
cJSON_AddItemToArray (instr, cJSON_CreateNumber (nexpr));
|
||||
for (int i = 0; i < nexpr; i++)
|
||||
cJSON_AddItemToArray (instr, cJSON_CreateNumber (expr_slots[i]));
|
||||
mach_gen_add_instr (s, instr);
|
||||
}
|
||||
/* Load format intrinsic */
|
||||
int fmt_func_slot = mach_gen_find_intrinsic (s, "format");
|
||||
if (fmt_func_slot < 0) {
|
||||
fmt_func_slot = mach_gen_alloc_slot (s);
|
||||
cJSON *instr = cJSON_CreateArray ();
|
||||
cJSON_AddItemToArray (instr, cJSON_CreateString ("access"));
|
||||
cJSON_AddItemToArray (instr, cJSON_CreateNumber (fmt_func_slot));
|
||||
cJSON *lit = cJSON_CreateObject ();
|
||||
cJSON_AddStringToObject (lit, "kind", "name");
|
||||
cJSON_AddStringToObject (lit, "name", "format");
|
||||
cJSON_AddStringToObject (lit, "make", "intrinsic");
|
||||
cJSON_AddItemToArray (instr, lit);
|
||||
mach_gen_add_instr (s, instr);
|
||||
}
|
||||
/* Load format string */
|
||||
const char *fmt = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "value"));
|
||||
int fmt_str_slot = mach_gen_alloc_slot (s);
|
||||
mach_gen_emit_const_str (s, fmt_str_slot, fmt ? fmt : "");
|
||||
/* Call format(fmt_str, array) */
|
||||
int result_slot = target >= 0 ? target : mach_gen_alloc_slot (s);
|
||||
{
|
||||
cJSON *call_args = cJSON_CreateArray ();
|
||||
cJSON_AddItemToArray (call_args, cJSON_CreateNumber (fmt_str_slot));
|
||||
cJSON_AddItemToArray (call_args, cJSON_CreateNumber (arr_slot));
|
||||
mach_gen_emit_call (s, result_slot, fmt_func_slot, call_args);
|
||||
cJSON_Delete (call_args);
|
||||
}
|
||||
return result_slot;
|
||||
}
|
||||
|
||||
if (strcmp (kind, "regexp") == 0) {
|
||||
int slot = target >= 0 ? target : mach_gen_alloc_slot (s);
|
||||
const char *pattern = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "pattern"));
|
||||
@@ -36165,6 +36243,14 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
|
||||
int dest = (int)a1->valuedouble;
|
||||
int obj_reg = (int)a2->valuedouble;
|
||||
JSValue obj = frame->slots[obj_reg];
|
||||
if (JS_IsFunction(obj)) {
|
||||
JSFunction *fn_chk = JS_VALUE_GET_FUNCTION(obj);
|
||||
if (fn_chk->length != 2) {
|
||||
JS_ThrowTypeError(ctx, "cannot read property of non-proxy function");
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
goto disrupt;
|
||||
}
|
||||
}
|
||||
JSValue val;
|
||||
if (cJSON_IsString(a3)) {
|
||||
JSValue key = JS_NewString(ctx, a3->valuestring);
|
||||
@@ -36179,6 +36265,7 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
|
||||
val = JS_GetProperty(ctx, obj, idx);
|
||||
}
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
if (JS_IsException(val)) goto disrupt;
|
||||
frame->slots[dest] = val;
|
||||
}
|
||||
else if (strcmp(op, "store") == 0) {
|
||||
@@ -36186,20 +36273,29 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
|
||||
int val_reg = (int)a2->valuedouble;
|
||||
JSValue obj = frame->slots[obj_reg];
|
||||
JSValue val = frame->slots[val_reg];
|
||||
if (JS_IsFunction(obj)) {
|
||||
JS_ThrowTypeError(ctx, "cannot set property of function");
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
goto disrupt;
|
||||
}
|
||||
if (cJSON_IsString(a3)) {
|
||||
JSValue key = JS_NewString(ctx, a3->valuestring);
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
obj = frame->slots[obj_reg];
|
||||
val = frame->slots[val_reg];
|
||||
JS_SetProperty(ctx, obj, key, val);
|
||||
int ret = JS_SetProperty(ctx, obj, key, val);
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
if (ret < 0) goto disrupt;
|
||||
} else {
|
||||
JSValue idx = frame->slots[(int)a3->valuedouble];
|
||||
int ret;
|
||||
if (JS_IsInt(idx))
|
||||
JS_SetPropertyUint32(ctx, obj, JS_VALUE_GET_INT(idx), val);
|
||||
ret = JS_SetPropertyUint32(ctx, obj, JS_VALUE_GET_INT(idx), val);
|
||||
else
|
||||
JS_SetProperty(ctx, obj, idx, val);
|
||||
ret = JS_SetProperty(ctx, obj, idx, val);
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
if (ret < 0) goto disrupt;
|
||||
}
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
}
|
||||
else if (strcmp(op, "delete") == 0) {
|
||||
int dest = (int)a1->valuedouble;
|
||||
@@ -36215,6 +36311,7 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
|
||||
}
|
||||
int ret = JS_DeleteProperty(ctx, obj, key);
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
if (ret < 0) goto disrupt;
|
||||
frame->slots[dest] = JS_NewBool(ctx, ret >= 0);
|
||||
}
|
||||
|
||||
@@ -36326,6 +36423,99 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Method call (handles function proxies) ---- */
|
||||
else if (strcmp(op, "callmethod") == 0) {
|
||||
/* ["callmethod", dest, obj_reg, "method_name", arg0_reg, arg1_reg, ...] */
|
||||
int dest = (int)a1->valuedouble;
|
||||
int obj_reg = (int)a2->valuedouble;
|
||||
JSValue obj = frame->slots[obj_reg];
|
||||
const char *method_name = a3->valuestring;
|
||||
/* Count arg registers (items after a3) */
|
||||
int nargs = 0;
|
||||
for (cJSON *p = a3->next; p && !cJSON_IsString(p); p = p->next)
|
||||
nargs++;
|
||||
|
||||
if (JS_IsFunction(obj)) {
|
||||
/* Proxy call: obj(name, [args...]) */
|
||||
JSValue key = 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);
|
||||
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) {
|
||||
if (cJSON_IsString(p)) break; /* hit line/col */
|
||||
int areg = (int)p->valuedouble;
|
||||
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;
|
||||
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 {
|
||||
/* Record method call: get property, call with this=obj */
|
||||
JSValue key = JS_NewString(ctx, method_name);
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
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) {
|
||||
/* mcode function — set up frame and jump */
|
||||
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);
|
||||
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) {
|
||||
if (cJSON_IsString(p)) break;
|
||||
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 {
|
||||
/* C or bytecode function */
|
||||
int vs_base = ctx->value_stack_top;
|
||||
cJSON *p = a3->next;
|
||||
for (int i = 0; i < nargs; i++, p = p->next) {
|
||||
if (cJSON_IsString(p)) break;
|
||||
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;
|
||||
@@ -36527,7 +36717,8 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
|
||||
/* ---- Push (append to array) ---- */
|
||||
else if (strcmp(op, "push") == 0) {
|
||||
int arr_slot = (int)a1->valuedouble;
|
||||
JSValue val = frame->slots[(int)a2->valuedouble];
|
||||
int val_slot = (int)a2->valuedouble;
|
||||
JSValue val = frame->slots[val_slot];
|
||||
if (!JS_IsArray(frame->slots[arr_slot])) { goto disrupt; }
|
||||
JSGCRef arr_gc;
|
||||
JS_PushGCRef(ctx, &arr_gc);
|
||||
@@ -36536,7 +36727,15 @@ 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 (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Pop (remove last from array) ---- */
|
||||
|
||||
Reference in New Issue
Block a user