Problem
The Linux heap-limit failure behind #628/#634 reaches Environment.put after vars.getOrPut has published a new map entry but before dupeBindingName can allocate its owned key. On OOM, the entry temporarily points at parser/source storage and its value slot is not initialized by put.
That ordering violates the engine’s fail-closed allocation contract. A caught allocation failure or later Environment finalization must never observe/free a partially published binding entry. The same get-or-put-then-duplicate pattern exists in the const, global-lexical, function-name, deletable-binding, and module-alias side tables.
Required implementation
- Allocate owned binding keys before publishing new Environment map entries.
- On bucket/capacity failure, free every unpublished owned key and leave the map unchanged.
- Make multi-table operations such as
putConstandputAliastransactional: either all required key/value storage is ready or no new semantic state is visible. - Preserve existing in-place updates, write barriers, const/function-name semantics, aliases, locking, GC name-byte accounting, and no-GIL publication ordering.
- Do not swallow OOM, retain source-backed keys, or special-case the failing test.
Acceptance
- Fault-injection tests fail each owned-key/bucket allocation position and verify unchanged maps, exact live-name accounting, and leak-free finalization.
- A caught OOM cannot expose an uninitialized/mutable partial declaration or alias.
- Existing Environment, module binding, heap-limit, full unit, and TSan tests pass.