Release path module members through the object core's accessors

The exit-time cleanup of the path module objects swapped each member
value out of the object's storage by reading v.obj.slots and
v.obj.entries directly. It now goes through scr_dyn_obj_count and a new
scr_dyn.h accessor, scr_dyn_obj_exchange_value_at, which stores a value at
an index without reading the keys, so the cleanup still runs after the
exit-time layout reset.
This commit is contained in:
Chris Tate
2026-10-29 04:55:52 -05:00
parent 6d170bf082
commit 7d1f9580ee
2 changed files with 15 additions and 8 deletions
+11
View File
@@ -770,6 +770,17 @@ static inline __attribute__((always_inline)) ScrDynProp scr_dyn_obj_prop_at(cons
e->accessor, e->writable, e->enumerable, e->configurable};
}
/* Stores `value` (moved) as the value of data property i and returns the
* previous value (owned by the caller). Reads no keys and does not follow
* the layout pointer, so exit-time teardown can use it after the layouts
* are freed. */
static inline ScrDyn *scr_dyn_obj_exchange_value_at(ScrDyn *o, size_t i, ScrDyn *value) {
ScrDyn **slot = o->v.obj.layout ? &o->v.obj.slots[i] : &o->v.obj.entries[i].value;
ScrDyn *previous = *slot;
*slot = value;
return previous;
}
/* The index of own property `key`, or SIZE_MAX. A linear scan in
* dictionary mode: for code that looks up options and headers, not for hot
* property reads. */
+4 -8
View File
@@ -368,18 +368,14 @@ static void scr_builtin_release(void *handle) {
static void scr_builtin_cleanup(void) {
/* An exports object can hold itself and the other path family
* (path.posix.posix, path.posix.win32): its member values are released
* first, so the objects are freed. The values are swapped out of the
* object's own storage, without reading its keys: the exit-time layout
* first, so the objects are freed. The values are swapped out without
* reading the keys (scr_dyn_obj_exchange_value_at): the exit-time layout
* reset can run before this and free the layout the keys live in. */
for (ScrBuiltinModule *module = scr_builtin_modules; module; module = module->next) {
if (!module->object) continue;
ScrDyn *exports = module->exports;
for (size_t i = 0; i < exports->v.obj.len; i++) {
ScrDyn **slot = exports->v.obj.layout ? &exports->v.obj.slots[i] : &exports->v.obj.entries[i].value;
ScrDyn *value = *slot;
*slot = scr_dyn_retain(scr_dyn_undefined());
scr_dyn_release(value);
}
for (size_t i = 0; i < scr_dyn_obj_count(exports); i++)
scr_dyn_release(scr_dyn_obj_exchange_value_at(exports, i, scr_dyn_retain(scr_dyn_undefined())));
}
while (scr_builtin_modules) {
ScrBuiltinModule *module = scr_builtin_modules;