Merge branch 'audit_gc' into fix_slots
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
104
source/runtime.c
104
source/runtime.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user