fix infinite loop in shop

This commit is contained in:
2026-02-15 15:41:09 -06:00
parent ebd624b772
commit 56de0ce803
19 changed files with 36483 additions and 35863 deletions

View File

@@ -850,9 +850,14 @@ static inline objhdr_t *chase(JSValue v) {
updated to follow the chain to the live copy. */
static inline void mach_resolve_forward(JSValue *slot) {
if (JS_IsPtr(*slot)) {
objhdr_t h = *(objhdr_t *)JS_VALUE_GET_PTR(*slot);
if (objhdr_type(h) == OBJ_FORWARD) {
*slot = JS_MKPTR(objhdr_fwd_ptr(h));
objhdr_t *oh = (objhdr_t *)JS_VALUE_GET_PTR(*slot);
if (objhdr_type(*oh) == OBJ_FORWARD) {
do {
objhdr_t *next = (objhdr_t *)objhdr_fwd_ptr(*oh);
if (!next) break;
oh = next;
} while (objhdr_type(*oh) == OBJ_FORWARD);
*slot = JS_MKPTR(oh);
}
}
}

View File

@@ -595,6 +595,10 @@ int rec_resize (JSContext *ctx, JSValue *pobj, uint64_t new_mask) {
/* Allocate new record with larger capacity - may trigger GC! */
size_t slots_size = sizeof (slot) * (new_mask + 1);
size_t total_size = sizeof (JSRecord) + slots_size;
if (total_size >= 100000) {
fprintf(stderr, "LARGE_REC_RESIZE: new_mask=%llu total=%zu old_mask=%llu\n",
(unsigned long long)new_mask, total_size, (unsigned long long)old_mask);
}
JSRecord *new_rec = js_malloc (ctx, total_size);
if (!new_rec) {
@@ -1327,9 +1331,6 @@ static inline int ptr_in_range (void *p, uint8_t *b, uint8_t *e) {
return q >= b && q < e;
}
static const char *gc_dbg_phase = "?";
static void *gc_dbg_parent = NULL;
JSValue gc_copy_value (JSContext *ctx, JSValue v, uint8_t *from_base, uint8_t *from_end, uint8_t *to_base, uint8_t **to_free, uint8_t *to_end) {
if (!JS_IsPtr (v)) return v;
@@ -1353,8 +1354,8 @@ JSValue gc_copy_value (JSContext *ctx, JSValue v, uint8_t *from_base, uint8_t *f
}
if (type != OBJ_ARRAY && type != OBJ_TEXT && type != OBJ_RECORD && type != OBJ_FUNCTION && type != OBJ_FRAME) {
fprintf (stderr, "gc_copy_value: invalid type %d at %p (hdr=0x%llx) phase=%s parent=%p\n",
type, ptr, (unsigned long long)hdr, gc_dbg_phase, gc_dbg_parent);
fprintf (stderr, "gc_copy_value: invalid type %d at %p (hdr=0x%llx)\n",
type, ptr, (unsigned long long)hdr);
fflush (stderr);
abort ();
}
@@ -1375,6 +1376,18 @@ JSValue gc_copy_value (JSContext *ctx, JSValue v, uint8_t *from_base, uint8_t *f
}
}
/* Recursively scan a code tree's cpools to arbitrary nesting depth */
static void gc_scan_code_tree (JSContext *ctx, JSCodeRegister *code,
uint8_t *from_base, uint8_t *from_end,
uint8_t *to_base, uint8_t **to_free, uint8_t *to_end) {
for (uint32_t i = 0; i < code->cpool_count; i++)
code->cpool[i] = gc_copy_value (ctx, code->cpool[i], from_base, from_end, to_base, to_free, to_end);
code->name = gc_copy_value (ctx, code->name, from_base, from_end, to_base, to_free, to_end);
for (uint32_t i = 0; i < code->func_count; i++)
if (code->functions[i])
gc_scan_code_tree (ctx, code->functions[i], from_base, from_end, to_base, to_free, to_end);
}
/* Scan a copied object and update its internal references */
void gc_scan_object (JSContext *ctx, void *ptr, uint8_t *from_base, uint8_t *from_end,
uint8_t *to_base, uint8_t **to_free, uint8_t *to_end) {
@@ -1398,8 +1411,9 @@ void gc_scan_object (JSContext *ctx, void *ptr, uint8_t *from_base, uint8_t *fro
#endif
/* Copy prototype */
rec->proto = gc_copy_value (ctx, rec->proto, from_base, from_end, to_base, to_free, to_end);
/* Copy table entries */
for (uint32_t i = 0; i <= mask; i++) {
/* Copy table entries — skip slot[0] which stores packed metadata
(class_id | rec_id << 32), not JSValues */
for (uint32_t i = 1; i <= mask; i++) {
JSValue k = rec->slots[i].key;
if (!rec_key_is_empty (k) && !rec_key_is_tomb (k)) {
rec->slots[i].key = gc_copy_value (ctx, k, from_base, from_end, to_base, to_free, to_end);
@@ -1413,26 +1427,11 @@ void gc_scan_object (JSContext *ctx, void *ptr, uint8_t *from_base, uint8_t *fro
/* Scan the function name */
fn->name = gc_copy_value (ctx, fn->name, from_base, from_end, to_base, to_free, to_end);
if (fn->kind == JS_FUNC_KIND_REGISTER && fn->u.reg.code) {
/* Register VM function - scan cpool (off-heap but contains JSValues) */
JSCodeRegister *code = fn->u.reg.code;
for (uint32_t i = 0; i < code->cpool_count; i++) {
code->cpool[i] = gc_copy_value (ctx, code->cpool[i], from_base, from_end, to_base, to_free, to_end);
}
/* Scan function name */
code->name = gc_copy_value (ctx, code->name, from_base, from_end, to_base, to_free, to_end);
/* Scan code tree to arbitrary nesting depth */
gc_scan_code_tree (ctx, fn->u.reg.code, from_base, from_end, to_base, to_free, to_end);
/* Scan outer_frame and env_record */
fn->u.reg.outer_frame = gc_copy_value (ctx, fn->u.reg.outer_frame, from_base, from_end, to_base, to_free, to_end);
fn->u.reg.env_record = gc_copy_value (ctx, fn->u.reg.env_record, from_base, from_end, to_base, to_free, to_end);
/* Recursively scan nested function cpools */
for (uint32_t i = 0; i < code->func_count; i++) {
if (code->functions[i]) {
JSCodeRegister *nested = code->functions[i];
for (uint32_t j = 0; j < nested->cpool_count; j++) {
nested->cpool[j] = gc_copy_value (ctx, nested->cpool[j], from_base, from_end, to_base, to_free, to_end);
}
nested->name = gc_copy_value (ctx, nested->name, from_base, from_end, to_base, to_free, to_end);
}
}
}
break;
}
@@ -1532,9 +1531,6 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
uint8_t *to_free = new_block;
uint8_t *to_end = new_block + new_size;
gc_dbg_phase = "roots";
gc_dbg_parent = NULL;
#ifdef VALIDATE_GC
/* Pre-GC: walk live frame chain and check for bad slot values */
if (JS_IsPtr (ctx->reg_current_frame)) {
@@ -1665,7 +1661,6 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
}
/* Cheney scan: scan copied objects to find more references */
gc_dbg_phase = "scan";
uint8_t *scan = to_base;
#ifdef DUMP_GC_DETAIL
printf(" scan: to_base=%p to_free=%p to_end=%p\n", (void*)to_base, (void*)to_free, (void*)to_end);
@@ -1682,7 +1677,6 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
printf(" size=%zu\n", obj_size);
fflush(stdout);
#endif
gc_dbg_parent = scan;
gc_scan_object (ctx, scan, from_base, from_end, to_base, &to_free, to_end);
scan += obj_size;
}
@@ -1698,6 +1692,7 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
ctx->gc_bytes_copied += new_used;
size_t recovered = old_used > new_used ? old_used - new_used : 0;
ctx->heap_base = to_base;
ctx->heap_free = to_free;
ctx->heap_end = to_end;
@@ -2322,7 +2317,17 @@ JSText *pretext_concat_value (JSContext *ctx, JSText *s, JSValue v) {
JSText *p = JS_VALUE_GET_STRING (v);
return pretext_concat (ctx, s, p, 0, (uint32_t)JSText_len (p));
}
/* Slow path: v needs conversion — root s across JS_ToString which can
allocate and trigger GC */
JSGCRef s_ref;
JS_PushGCRef (ctx, &s_ref);
s_ref.val = JS_MKPTR (s);
JSValue v1 = JS_ToString (ctx, v);
s = (JSText *)chase (s_ref.val); /* re-fetch after possible GC */
JS_PopGCRef (ctx, &s_ref);
if (JS_IsException (v1)) return NULL;
if (MIST_IsImmediateASCII (v1)) {
@@ -3467,7 +3472,7 @@ int JS_SetPropertyKey (JSContext *ctx, JSValue this_obj, JSValue key, JSValue va
JS_ThrowTypeError (ctx, "cannot modify frozen object");
return -1;
}
return rec_set_own (ctx, rec, key, val);
return rec_set_own (ctx, &this_obj, key, val);
}
/* For string keys, use text directly as key */
@@ -5211,16 +5216,31 @@ JSValue js_regexp_toString (JSContext *ctx, JSValue this_val, int argc, JSValue
JSText *b = pretext_init (ctx, 0);
if (!b) return JS_EXCEPTION;
/* Root b across allocating calls (JS_GetProperty can trigger GC) */
JSGCRef b_ref;
JS_PushGCRef (ctx, &b_ref);
b_ref.val = JS_MKPTR (b);
b = pretext_putc (ctx, b, '/');
if (!b) return JS_EXCEPTION;
if (!b) { JS_PopGCRef (ctx, &b_ref); return JS_EXCEPTION; }
b_ref.val = JS_MKPTR (b);
pattern = JS_GetProperty (ctx, this_val, JS_KEY_source);
b = (JSText *)chase (b_ref.val);
b = pretext_concat_value (ctx, b, pattern);
if (!b) return JS_EXCEPTION;
if (!b) { JS_PopGCRef (ctx, &b_ref); return JS_EXCEPTION; }
b_ref.val = JS_MKPTR (b);
b = pretext_putc (ctx, b, '/');
if (!b) return JS_EXCEPTION;
if (!b) { JS_PopGCRef (ctx, &b_ref); return JS_EXCEPTION; }
b_ref.val = JS_MKPTR (b);
flags = JS_GetProperty (ctx, this_val, JS_KEY_flags);
b = (JSText *)chase (b_ref.val);
b = pretext_concat_value (ctx, b, flags);
if (!b) return JS_EXCEPTION;
if (!b) { JS_PopGCRef (ctx, &b_ref); return JS_EXCEPTION; }
JS_PopGCRef (ctx, &b_ref);
return pretext_end (ctx, b);
}
@@ -7098,14 +7118,26 @@ JSValue js_cell_text_codepoint (JSContext *ctx, JSValue this_val, int argc, JSVa
* file. */
static JSText *pt_concat_value_to_string_free (JSContext *ctx, JSText *b, JSValue v) {
JSGCRef s_ref;
/* Root b across JS_ToString which can allocate and trigger GC */
JSGCRef b_ref, s_ref;
JS_PushGCRef (ctx, &b_ref);
b_ref.val = JS_MKPTR (b);
JSValue s = JS_ToString (ctx, v);
if (JS_IsException (s)) return NULL;
if (JS_IsException (s)) {
JS_PopGCRef (ctx, &b_ref);
return NULL;
}
/* Root s — pretext_concat_value can trigger GC and move the heap string */
JS_PushGCRef (ctx, &s_ref);
s_ref.val = s;
b = (JSText *)chase (b_ref.val); /* re-fetch after possible GC */
b = pretext_concat_value (ctx, b, s_ref.val);
JS_PopGCRef (ctx, &s_ref);
JS_PopGCRef (ctx, &b_ref);
return b;
}