diff --git a/source/quickjs.c b/source/quickjs.c index c6c3f88b..66b367eb 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -104,7 +104,7 @@ // #define DUMP_ROPE_REBALANCE /* test the GC by forcing it before each object allocation */ -#define FORCE_GC_AT_MALLOC +/* #define FORCE_GC_AT_MALLOC */ #define POISON_HEAP /* POISON_HEAP: Use ASan's memory poisoning to detect stale pointer access */ @@ -3448,6 +3448,32 @@ static JSValue js_sub_string (JSContext *ctx, JSText *p, int start, int end) { return pretext_end (ctx, str); } +/* Substring from a JSValue (handles both immediate ASCII and heap strings) */ +static JSValue js_sub_string_val (JSContext *ctx, JSValue src, int start, int end) { + int len = end - start; + if (len <= 0) return JS_NewString (ctx, ""); + + if (MIST_IsImmediateASCII (src)) { + /* IMM: extract chars directly, try to return IMM */ + if (len <= MIST_ASCII_MAX_LEN) { + char buf[MIST_ASCII_MAX_LEN + 1]; + for (int i = 0; i < len; i++) + buf[i] = (char)MIST_GetImmediateASCIIChar (src, start + i); + return js_new_string8_len (ctx, buf, len); + } + /* Longer than 7 — shouldn't happen for IMM (max 7 chars) but handle it */ + JSText *str = js_alloc_string (ctx, len); + if (!str) return JS_EXCEPTION; + for (int i = 0; i < len; i++) + string_put (str, i, MIST_GetImmediateASCIIChar (src, start + i)); + str->length = len; + return pretext_end (ctx, str); + } + + /* Heap string — delegate to existing js_sub_string */ + return js_sub_string (ctx, JS_VALUE_GET_STRING (src), start, end); +} + /* Allocate a new pretext (mutable JSText) with initial capacity */ static JSText *pretext_init (JSContext *ctx, int capacity) { if (capacity <= 0) capacity = 16; @@ -21507,12 +21533,8 @@ static JSValue js_cell_character (JSContext *ctx, JSValue this_val, int argc, JS /* Handle string - return first character */ if (tag == JS_TAG_STRING || tag == JS_TAG_STRING_IMM) { - JSText *p = JS_VALUE_GET_STRING (arg); - if (JSText_len (p) == 0) return JS_NewString (ctx, ""); - - /* UTF-32: each element is a full code point, no surrogate handling needed - */ - return js_sub_string (ctx, p, 0, 1); + if (js_string_value_len (arg) == 0) return JS_NewString (ctx, ""); + return js_sub_string_val (ctx, arg, 0, 1); } /* Handle integer - return character from codepoint */ @@ -21569,17 +21591,14 @@ static JSValue js_cell_text (JSContext *ctx, JSValue this_val, int argc, JSValue if (argc == 1) return str; - JSText *p = JS_VALUE_GET_STRING (str); - int len = (int)JSText_len (p); - if (argc >= 2) { int tag1 = JS_VALUE_GET_TAG (argv[1]); if (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64) { + int len = js_string_value_len (str); int from, to; - if (JS_ToInt32 (ctx, &from, argv[1])) { + if (JS_ToInt32 (ctx, &from, argv[1])) return JS_EXCEPTION; - } if (from < 0) from += len; if (from < 0) from = 0; @@ -21587,20 +21606,17 @@ static JSValue js_cell_text (JSContext *ctx, JSValue this_val, int argc, JSValue to = len; if (argc >= 3) { - if (JS_ToInt32 (ctx, &to, argv[2])) { + if (JS_ToInt32 (ctx, &to, argv[2])) return JS_EXCEPTION; - } if (to < 0) to += len; if (to < 0) to = 0; if (to > len) to = len; } - if (from > to) { + if (from > to) return JS_NULL; - } - JSValue sub = js_sub_string (ctx, p, from, to); - return sub; + return js_sub_string_val (ctx, str, from, to); } } @@ -22106,9 +22122,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc, } if (boundary < len) { - /* Re-chase sp after GC points */ - JSText *sp = JS_VALUE_GET_STRING (argv[0]); - JSValue ch = js_sub_string (ctx, sp, boundary, boundary + 1); + JSValue ch = js_sub_string_val (ctx, argv[0], boundary, boundary + 1); if (JS_IsException (ch)) goto fail_str_target; b = pretext_concat_value (ctx, b, ch); if (!b) goto fail_str_target; @@ -22120,7 +22134,6 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc, int pos = 0; int32_t count = 0; - JSText *sp; while (pos <= len - t_len && (limit < 0 || count < limit)) { int found = -1; @@ -22194,7 +22207,6 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc, if (!b) goto fail_str_target; } - (void)sp; /* Suppress unused variable warning */ return pretext_end (ctx, b); fail_str_target: @@ -22214,8 +22226,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc, if (JS_SetPropertyStr (ctx, rx, "lastIndex", JS_NewInt32 (ctx, 0)) < 0) goto fail_rx; - JSText *sp = JS_VALUE_GET_STRING (argv[0]); /* Re-chase before js_sub_string */ - JSValue sub_str = js_sub_string (ctx, sp, pos, len); + JSValue sub_str = js_sub_string_val (ctx, argv[0], pos, len); if (JS_IsException (sub_str)) goto fail_rx; JSValue exec_res @@ -22262,8 +22273,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc, if (match_len < 0) match_len = 0; if (found > pos) { - sp = JS_VALUE_GET_STRING (argv[0]); /* Re-chase before js_sub_string */ - JSValue prefix = js_sub_string (ctx, sp, pos, found); + JSValue prefix = js_sub_string_val (ctx, argv[0], pos, found); if (JS_IsException (prefix)) goto fail_rx; b = pretext_concat_value (ctx, b, prefix); if (!b) goto fail_rx; @@ -22290,8 +22300,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc, } if (pos < len) { - JSText *sp = JS_VALUE_GET_STRING (argv[0]); /* Re-chase before js_sub_string */ - JSValue tail = js_sub_string (ctx, sp, pos, len); + JSValue tail = js_sub_string_val (ctx, argv[0], pos, len); if (JS_IsException (tail)) goto fail_rx; b = pretext_concat_value (ctx, b, tail); if (!b) goto fail_rx; @@ -23316,8 +23325,7 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu /* array(text, separator) - split by separator */ /* array(text, length) - dice into chunks */ if (JS_VALUE_IS_TEXT (arg)) { - JSText *p = JS_VALUE_GET_STRING (arg); - int len = (int)JSText_len (p); + int len = js_string_value_len (arg); if (argc < 2 || JS_IsNull (argv[1])) { /* Split into characters */ @@ -23325,7 +23333,7 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu if (JS_IsException (result)) { return result; } JSArray *out = JS_VALUE_GET_ARRAY (result); for (int i = 0; i < len; i++) { - JSValue ch = js_sub_string (ctx, p, i, i + 1); + JSValue ch = js_sub_string_val (ctx, arg, i, i + 1); if (JS_IsException (ch)) { return JS_EXCEPTION; } @@ -23374,7 +23382,7 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu if (sep_len == 0) { for (int i = 0; i < len; i++) { - JSValue ch = js_sub_string (ctx, p, i, i + 1); + JSValue ch = js_sub_string_val (ctx, arg, i, i + 1); JS_SetPropertyInt64 (ctx, result, idx++, ch); } } else { @@ -23409,12 +23417,10 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu int64_t out_idx = 0; while (pos <= len) { - /* force lastIndex = 0 so flags don't matter and we fully control - * iteration */ if (JS_SetPropertyStr (ctx, rx, "lastIndex", JS_NewInt32 (ctx, 0)) < 0) goto fail_rx_split; - JSValue sub_str = js_sub_string (ctx, p, pos, len); + JSValue sub_str = js_sub_string_val (ctx, arg, pos, len); if (JS_IsException (sub_str)) goto fail_rx_split; JSValue exec_res @@ -23422,59 +23428,44 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu if (JS_IsException (exec_res)) goto fail_rx_split; if (JS_IsNull (exec_res)) { - /* remainder */ - JSValue tail = js_sub_string (ctx, p, pos, len); + JSValue tail = js_sub_string_val (ctx, arg, pos, len); if (JS_IsException (tail)) goto fail_rx_split; if (JS_ArrayPush (ctx, &result, tail) < 0) { goto fail_rx_split; } break; } - /* local match index within sub_str */ JSValue idx_val = JS_GetPropertyStr (ctx, exec_res, "index"); - if (JS_IsException (idx_val)) { - goto fail_rx_split; - } + if (JS_IsException (idx_val)) goto fail_rx_split; int32_t local_index = 0; - if (JS_ToInt32 (ctx, &local_index, idx_val)) { - goto fail_rx_split; - } - + if (JS_ToInt32 (ctx, &local_index, idx_val)) goto fail_rx_split; if (local_index < 0) local_index = 0; int found = pos + local_index; if (found < pos) found = pos; if (found > len) { - /* treat as no more matches */ - JSValue tail = js_sub_string (ctx, p, pos, len); + JSValue tail = js_sub_string_val (ctx, arg, pos, len); if (JS_IsException (tail)) goto fail_rx_split; JS_SetPropertyInt64 (ctx, result, out_idx++, tail); break; } JSValue end_val = JS_GetPropertyStr (ctx, exec_res, "end"); - if (JS_IsException (end_val)) { - goto fail_rx_split; - } + if (JS_IsException (end_val)) goto fail_rx_split; int32_t end = 0; - if (JS_ToInt32 (ctx, &end, end_val)) { - goto fail_rx_split; - } + if (JS_ToInt32 (ctx, &end, end_val)) goto fail_rx_split; int match_len = end - local_index; if (match_len < 0) match_len = 0; - /* emit piece before match */ - JSValue part = js_sub_string (ctx, p, pos, found); + JSValue part = js_sub_string_val (ctx, arg, pos, found); if (JS_IsException (part)) goto fail_rx_split; if (JS_ArrayPush (ctx, &result, part) < 0) { goto fail_rx_split; } - /* advance past match; ensure progress on empty matches */ pos = found + match_len; if (match_len == 0) { if (found >= len) { - /* match at end: add trailing empty field and stop */ JSValue empty = JS_NewStringLen (ctx, "", 0); if (JS_IsException (empty)) goto fail_rx_split; if (JS_ArrayPush (ctx, &result, empty) < 0) { goto fail_rx_split; } @@ -23484,58 +23475,42 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu } } - /* restore lastIndex */ JS_SetPropertyStr (ctx, rx, "lastIndex", orig_last_index); - - /* str removed - arg not owned */ return result; fail_rx_split: - /* best-effort restore lastIndex */ if (!JS_IsException (orig_last_index)) { JS_SetPropertyStr (ctx, rx, "lastIndex", orig_last_index); - } else { } - /* str removed - arg not owned */ return JS_EXCEPTION; } if (JS_VALUE_IS_NUMBER (argv[1])) { /* Dice into chunks */ int chunk_len; - if (JS_ToInt32 (ctx, &chunk_len, argv[1])) { - /* str removed - arg not owned */ + if (JS_ToInt32 (ctx, &chunk_len, argv[1])) return JS_NULL; - } - if (chunk_len <= 0) { - /* str removed - arg not owned */ + if (chunk_len <= 0) return JS_NULL; - } int64_t count = (len + chunk_len - 1) / chunk_len; JSValue result = JS_NewArrayLen (ctx, count); - if (JS_IsException (result)) { - /* str removed - arg not owned */ + if (JS_IsException (result)) return result; - } int64_t idx = 0; for (int i = 0; i < len; i += chunk_len) { int end = i + chunk_len; if (end > len) end = len; - JSValue chunk = js_sub_string (ctx, p, i, end); - if (JS_IsException (chunk)) { - /* str removed - arg not owned */ + JSValue chunk = js_sub_string_val (ctx, arg, i, end); + if (JS_IsException (chunk)) return JS_EXCEPTION; - } JS_SetPropertyInt64 (ctx, result, idx++, chunk); } - /* str removed - arg not owned */ return result; } - /* str removed - arg not owned */ return JS_NULL; } @@ -31133,7 +31108,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { } /* Assignment */ - if (strcmp(kind, "=") == 0) { + if (strcmp(kind, "assign") == 0) { cJSON *left = cJSON_GetObjectItem(node, "left"); cJSON *right = cJSON_GetObjectItem(node, "right"); const char *lk = cJSON_GetStringValue(cJSON_GetObjectItem(left, "kind")); @@ -31523,18 +31498,31 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { mach_emit(cs, MACH_AsBx(MACH_JMPFALSE, cr, 0)); mach_free_reg_to(cs, save); - /* Compile then branch */ + /* Compile then branch — "then" is a direct array of statements */ if (then_body) { - cJSON *stmts = cJSON_GetObjectItem(then_body, "statements"); - if (stmts && cJSON_IsArray(stmts)) { - int count = cJSON_GetArraySize(stmts); + if (cJSON_IsArray(then_body)) { + int count = cJSON_GetArraySize(then_body); for (int i = 0; i < count; i++) - mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); + mach_compile_stmt(cs, cJSON_GetArrayItem(then_body, i)); } else { - mach_compile_stmt(cs, then_body); + cJSON *stmts = cJSON_GetObjectItem(then_body, "statements"); + if (stmts && cJSON_IsArray(stmts)) { + int count = cJSON_GetArraySize(stmts); + for (int i = 0; i < count; i++) + mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); + } else { + mach_compile_stmt(cs, then_body); + } } } + /* Check for else-if chain ("list") or plain else */ + if (!else_body) { + cJSON *list = cJSON_GetObjectItem(stmt, "list"); + if (list && cJSON_IsArray(list) && cJSON_GetArraySize(list) > 0) + else_body = cJSON_GetArrayItem(list, 0); + } + if (else_body) { int jmpend_pc = mach_current_pc(cs); mach_emit(cs, MACH_sJ(MACH_JMP, 0)); @@ -31543,14 +31531,20 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { int offset = mach_current_pc(cs) - (jmpfalse_pc + 1); cs->code[jmpfalse_pc] = MACH_AsBx(MACH_JMPFALSE, cr, (int16_t)offset); - /* Compile else */ - cJSON *stmts = cJSON_GetObjectItem(else_body, "statements"); - if (stmts && cJSON_IsArray(stmts)) { - int count = cJSON_GetArraySize(stmts); + /* Compile else — could be a direct array, object, or else-if stmt */ + if (cJSON_IsArray(else_body)) { + int count = cJSON_GetArraySize(else_body); for (int i = 0; i < count; i++) - mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); + mach_compile_stmt(cs, cJSON_GetArrayItem(else_body, i)); } else { - mach_compile_stmt(cs, else_body); + cJSON *stmts = cJSON_GetObjectItem(else_body, "statements"); + if (stmts && cJSON_IsArray(stmts)) { + int count = cJSON_GetArraySize(stmts); + for (int i = 0; i < count; i++) + mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); + } else { + mach_compile_stmt(cs, else_body); + } } /* Patch jmpend */ @@ -31568,8 +31562,11 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { if (strcmp(kind, "while") == 0) { cJSON *cond = cJSON_GetObjectItem(stmt, "expression"); if (!cond) cond = cJSON_GetObjectItem(stmt, "condition"); - cJSON *body = cJSON_GetObjectItem(stmt, "block"); - if (!body) body = cJSON_GetObjectItem(stmt, "body"); + + int old_break = cs->loop_break; + int old_continue = cs->loop_continue; + cs->loop_break = -1; + cs->loop_continue = -1; int loop_top = mach_current_pc(cs); @@ -31579,18 +31576,32 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { mach_emit(cs, MACH_AsBx(MACH_JMPFALSE, cr, 0)); mach_free_reg_to(cs, save); - /* Compile body */ - if (body) { - cJSON *stmts = cJSON_GetObjectItem(body, "statements"); + /* Compile body — "statements" on a child "block"/"body", or directly on the node */ + { + cJSON *body = cJSON_GetObjectItem(stmt, "block"); + if (!body) body = cJSON_GetObjectItem(stmt, "body"); + cJSON *stmts = body ? cJSON_GetObjectItem(body, "statements") : NULL; + if (!stmts) stmts = cJSON_GetObjectItem(stmt, "statements"); if (stmts && cJSON_IsArray(stmts)) { int count = cJSON_GetArraySize(stmts); for (int i = 0; i < count; i++) mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); - } else { + } else if (body) { mach_compile_stmt(cs, body); } } + /* Patch continue chain to loop_top */ + { + int cp = cs->loop_continue; + while (cp >= 0) { + int prev = MACH_GET_sJ(cs->code[cp]); + int off = loop_top - (cp + 1); + cs->code[cp] = MACH_sJ(MACH_JMP, off); + cp = prev; + } + } + /* Jump back to loop top */ int offset = loop_top - (mach_current_pc(cs) + 1); mach_emit(cs, MACH_sJ(MACH_JMP, offset)); @@ -31598,18 +31609,33 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { /* Patch jmpfalse to after loop */ offset = mach_current_pc(cs) - (jmpfalse_pc + 1); cs->code[jmpfalse_pc] = MACH_AsBx(MACH_JMPFALSE, cr, (int16_t)offset); + + /* Patch break chain */ + int bp = cs->loop_break; + while (bp >= 0) { + int prev = MACH_GET_sJ(cs->code[bp]); + offset = mach_current_pc(cs) - (bp + 1); + cs->code[bp] = MACH_sJ(MACH_JMP, offset); + bp = prev; + } + cs->loop_break = old_break; + cs->loop_continue = old_continue; return; } /* For loop */ if (strcmp(kind, "for") == 0) { - cJSON *init = cJSON_GetObjectItem(stmt, "initial"); - cJSON *cond = cJSON_GetObjectItem(stmt, "condition"); - if (!cond) cond = cJSON_GetObjectItem(stmt, "expression"); + cJSON *init = cJSON_GetObjectItem(stmt, "init"); + cJSON *cond = cJSON_GetObjectItem(stmt, "test"); cJSON *update = cJSON_GetObjectItem(stmt, "update"); cJSON *body = cJSON_GetObjectItem(stmt, "block"); if (!body) body = cJSON_GetObjectItem(stmt, "body"); + int old_break = cs->loop_break; + int old_continue = cs->loop_continue; + cs->loop_break = -1; + cs->loop_continue = -1; + /* Init */ if (init) mach_compile_stmt(cs, init); @@ -31625,23 +31651,34 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { mach_free_reg_to(cs, save); } - /* Body */ - if (body) { - cJSON *stmts = cJSON_GetObjectItem(body, "statements"); + /* Body — "statements" on a child "block"/"body", or directly on the for node */ + { + cJSON *stmts = body ? cJSON_GetObjectItem(body, "statements") : NULL; + if (!stmts) stmts = cJSON_GetObjectItem(stmt, "statements"); if (stmts && cJSON_IsArray(stmts)) { int count = cJSON_GetArraySize(stmts); for (int i = 0; i < count; i++) mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i)); - } else { + } else if (body) { mach_compile_stmt(cs, body); } } - /* Update */ + /* Patch continue chain to update (or loop_top if no update) */ + { + int continue_target = mach_current_pc(cs); + int cp = cs->loop_continue; + while (cp >= 0) { + int prev = MACH_GET_sJ(cs->code[cp]); + int off = continue_target - (cp + 1); + cs->code[cp] = MACH_sJ(MACH_JMP, off); + cp = prev; + } + } + + /* Update — assignment expressions must be compiled as statements */ if (update) { - int save = cs->freereg; - mach_compile_expr(cs, update, -1); - mach_free_reg_to(cs, save); + mach_compile_stmt(cs, update); } /* Jump back */ @@ -31655,6 +31692,48 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) { int cr = MACH_GET_A(cs->code[jmpfalse_pc]); cs->code[jmpfalse_pc] = MACH_AsBx(MACH_JMPFALSE, cr, (int16_t)offset); } + + /* Patch break chain */ + int bp = cs->loop_break; + while (bp >= 0) { + int prev = MACH_GET_sJ(cs->code[bp]); + offset = mach_current_pc(cs) - (bp + 1); + cs->code[bp] = MACH_sJ(MACH_JMP, offset); + bp = prev; + } + cs->loop_break = old_break; + cs->loop_continue = old_continue; + return; + } + + /* Break */ + if (strcmp(kind, "break") == 0) { + int pc = mach_current_pc(cs); + mach_emit(cs, MACH_sJ(MACH_JMP, cs->loop_break)); + cs->loop_break = pc; + return; + } + + /* Continue */ + if (strcmp(kind, "continue") == 0) { + int pc = mach_current_pc(cs); + mach_emit(cs, MACH_sJ(MACH_JMP, cs->loop_continue)); + cs->loop_continue = pc; + return; + } + + /* Assignment as statement */ + if (strcmp(kind, "assign") == 0 || + strcmp(kind, "+=") == 0 || strcmp(kind, "-=") == 0 || + strcmp(kind, "*=") == 0 || strcmp(kind, "/=") == 0 || + strcmp(kind, "%=") == 0 || strcmp(kind, "**=") == 0 || + strcmp(kind, "&=") == 0 || strcmp(kind, "|=") == 0 || + strcmp(kind, "^=") == 0 || strcmp(kind, "<<=") == 0 || + strcmp(kind, ">>=") == 0 || strcmp(kind, ">>>=") == 0 || + strcmp(kind, "++") == 0 || strcmp(kind, "--") == 0) { + int save = cs->freereg; + mach_compile_expr(cs, stmt, -1); + mach_free_reg_to(cs, save); return; } @@ -32409,13 +32488,15 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JSFunction *fn = JS_VALUE_GET_FUNCTION(func_val); if (fn->kind == JS_FUNC_KIND_C) { - /* C function: copy args to C stack so GC can't invalidate argv */ - JSValue c_argv[nargs > 0 ? nargs : 1]; + /* C function: push args onto value stack (C-allocated, GC-scanned) */ + int vs_base = ctx->value_stack_top; for (int i = 0; i < nargs; i++) - c_argv[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->reg_current_frame = frame_ref.val; ctx->rt->current_register_pc = pc > 0 ? pc - 1 : 0; - JSValue ret = js_call_c_function(ctx, func_val, JS_NULL, nargs, c_argv); + JSValue ret = js_call_c_function(ctx, func_val, JS_NULL, 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; } @@ -32448,11 +32529,13 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, env = fn->u.reg.env_record; pc = code->entry_point; } else { - /* Other function kinds (bytecode) — copy args to C stack */ - JSValue bc_argv[nargs > 0 ? nargs : 1]; + /* Other function kinds (bytecode) — push args onto value stack */ + int vs_base = ctx->value_stack_top; for (int i = 0; i < nargs; i++) - bc_argv[i] = frame->slots[base + 1 + i]; - JSValue ret = js_call_c_function(ctx, func_val, JS_NULL, nargs, bc_argv); + ctx->value_stack[vs_base + i] = frame->slots[base + 1 + i]; + ctx->value_stack_top = vs_base + nargs; + JSValue ret = js_call_c_function(ctx, func_val, JS_NULL, nargs, &ctx->value_stack[vs_base]); + ctx->value_stack_top = vs_base; frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(ret)) { goto disrupt; } if (nresults > 0) frame->slots[base] = ret; @@ -32540,24 +32623,32 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, continue; disrupt: - /* Search frame chain for a disruption handler */ - for (;;) { - JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function); - code = fn->u.reg.code; - if (code->disruption_pc > 0) { - env = fn->u.reg.env_record; - pc = code->disruption_pc; - break; + /* Search frame chain for a disruption handler. + Use frame_pc to track each frame's execution point: + - For the faulting frame, it's the current pc. + - For unwound caller frames, read from frame->address. */ + { + uint32_t frame_pc = pc; + for (;;) { + JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function); + code = fn->u.reg.code; + /* Only enter handler if we're not already inside it */ + if (code->disruption_pc > 0 && frame_pc < code->disruption_pc) { + env = fn->u.reg.env_record; + pc = code->disruption_pc; + break; + } + if (JS_IsNull(frame->caller)) { + result = JS_NULL; + goto done; + } + /* Unwind one frame — read caller's saved pc from its address field */ + JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller); + frame->caller = JS_NULL; + frame = caller; + frame_ref.val = JS_MKPTR(frame); + frame_pc = (uint32_t)(JS_VALUE_GET_INT(frame->address) >> 16); } - if (JS_IsNull(frame->caller)) { - result = JS_NULL; - goto done; - } - /* Unwind one frame */ - JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller); - frame->caller = JS_NULL; - frame = caller; - frame_ref.val = JS_MKPTR(frame); } }