mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-07 08:33:50 +08:00
Take number and string boxes out of the candidate buffers before pooling them
scr_dyn_release returned the last reference of a number or string box to the free list without scr_cyc_dead, because the release paths never buffer those boxes. A collector pass can: capture boxes trace their payload, and scr_xg_release, which settles the cross-generation edges a restricted pass skipped, calls scr_cyc_on_release on the target whatever its kind. The pooled block then stayed in its candidate buffer, and the next box that reused it carried the stale buffer position. Its death swapped another object into that slot, which corrupted the buffers and crashed a later scr_cyc_on_dead. The fast path now takes a buffered box out of its buffer first. The sanitized runtime does not pool boxes, so only plain builds were affected.
This commit is contained in:
@@ -1286,10 +1286,14 @@ void scr_dyn_release(ScrDyn *d) {
|
||||
return;
|
||||
}
|
||||
#ifndef SCR_RC_AUDIT
|
||||
/* Number and string boxes own at most one string, are never cycle
|
||||
* candidates and cannot be weak keys: return them to the free list. */
|
||||
/* Number and string boxes own at most one string and cannot be weak
|
||||
* keys: return them to the free list. Their releases never buffer them,
|
||||
* but a collector pass can (a box that traces its payload reaches them,
|
||||
* and scr_xg_release buffers what it settles), so a buffered box leaves
|
||||
* its candidate buffer first. */
|
||||
if ((d->kind == SCR_DYN_NUM || d->kind == SCR_DYN_STR) && !d->prototype &&
|
||||
!d->symbol_properties && !d->symbol_keys && scr_dyn_free_count < SCR_DYN_FREE_MAX) {
|
||||
scr_cyc_dead(d);
|
||||
if (d->kind == SCR_DYN_STR) scr_str_release(d->v.str);
|
||||
d->v.str = (ScrStr *)scr_dyn_free_misc;
|
||||
scr_dyn_free_misc = d;
|
||||
|
||||
@@ -391,6 +391,30 @@ static bool buffered_after_read(ScrDyn *d) {
|
||||
return buffered(d);
|
||||
}
|
||||
|
||||
/* A collector pass can buffer a number or string box that it reached
|
||||
* through an owner that traces it: scr_xg_release, settling an edge a
|
||||
* restricted pass skipped, calls scr_cyc_on_release on the target. The
|
||||
* box's last release must take it out of its candidate buffer before its
|
||||
* block returns to the free list, which hands it to the next box. */
|
||||
static void buffered_leaf_release(void) {
|
||||
for (uint16_t gen = SCR_CYC_NURSERY; gen < SCR_CYC_NGENS; gen++) {
|
||||
ScrStr *text = scr_str_new("buffered", 8);
|
||||
ScrDyn *leaves[2] = {scr_dyn_new_str(text), scr_dyn_new_num(0.5)};
|
||||
scr_str_release(text);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
scr_cyc_set_gen(scr_cyc_hdr(leaves[i]), gen);
|
||||
scr_cyc_on_release(leaves[i]);
|
||||
scr_dyn_release(leaves[i]);
|
||||
}
|
||||
ScrDyn *fresh[2] = {scr_dyn_new_num(1.5), scr_dyn_new_num(2.5)};
|
||||
assert(!buffered(fresh[0]) && !buffered(fresh[1]));
|
||||
scr_collect_cycles();
|
||||
assert(fresh[0]->v.num == 1.5 && fresh[1]->v.num == 2.5);
|
||||
scr_dyn_release(fresh[0]);
|
||||
scr_dyn_release(fresh[1]);
|
||||
}
|
||||
}
|
||||
|
||||
static ScrDyn *parse_text(const char *text) {
|
||||
ScrStr *input = scr_str_new(text, strlen(text));
|
||||
ScrDyn *parsed = scr_json_parse(input);
|
||||
@@ -823,6 +847,7 @@ int main(void) {
|
||||
shared_property_keys();
|
||||
checked_leaf_cycles();
|
||||
checked_number_storage();
|
||||
buffered_leaf_release();
|
||||
json_intact_regions();
|
||||
json_schema_parse();
|
||||
json_schema_special_names();
|
||||
|
||||
Reference in New Issue
Block a user