diff --git a/packages/runtime/src/scr_dyn.h b/packages/runtime/src/scr_dyn.h index 47f9046b6b..839e654f04 100644 --- a/packages/runtime/src/scr_dyn.h +++ b/packages/runtime/src/scr_dyn.h @@ -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. */ diff --git a/packages/runtime/src/scr_node_builtin.c b/packages/runtime/src/scr_node_builtin.c index 71b523742c..d312a6e805 100644 --- a/packages/runtime/src/scr_node_builtin.c +++ b/packages/runtime/src/scr_node_builtin.c @@ -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;