fix mach crashes
This commit is contained in:
359
source/quickjs.c
359
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user