Summary
#9879 made aliased dynamic-import bindings LIVE, which is the semantics that
matters and was genuinely broken. It implements liveness by installing the
export as an accessor property on the namespace object, so the descriptor
shape now diverges from the spec.
The divergence
crates/perry-runtime/src/object/namespace_create.rs: an entry whose parallel
live_flags byte is non-zero is installed with js_object_define_accessor
plus PropertyAttrs::new(false, true, false).
ECMA-262 §10.4.6 makes a module namespace an exotic object whose
[[GetOwnProperty]] returns a data descriptor holding the binding's
current value:
// Node
Object.getOwnPropertyDescriptor(ns, "x")
// { value: 1, writable: true, enumerable: true, configurable: false }
// Perry, for a live (aliased) export after #9879
// { get: [Function], set: undefined, enumerable: true, configurable: false }
So "value" in desc, desc.writable, and anything that branches on
desc.get sees a different shape. Liveness itself is correct either way —
the accessor reads through to the binding.
Why it was landed anyway
Before #9879 the aliased binding was a dead snapshot: the value was simply
wrong after reassignment. Trading a wrong value for a right value with an
unusual descriptor is a clear net improvement, and the alternative — a real
module-namespace exotic object whose internal methods do the liveness — is a
much larger change than the bug warranted.
What this issue wants
Either implement the namespace as an exotic object so [[GetOwnProperty]]
synthesises a data descriptor over the live binding, or special-case
getOwnPropertyDescriptor / Object.keys / JSON.stringify on namespace
objects to report the data shape while keeping the accessor storage.
A test is worth adding regardless: test_gap_dynamic_import_alias_binding.ts
covers liveness under GC stress but asserts nothing about descriptor shape, so
this deviation is currently invisible to the suite.
Introduced by #9879, landed via merge train #9888.
Summary
#9879 made aliased dynamic-import bindings LIVE, which is the semantics that
matters and was genuinely broken. It implements liveness by installing the
export as an accessor property on the namespace object, so the descriptor
shape now diverges from the spec.
The divergence
crates/perry-runtime/src/object/namespace_create.rs: an entry whose parallellive_flagsbyte is non-zero is installed withjs_object_define_accessorplus
PropertyAttrs::new(false, true, false).ECMA-262 §10.4.6 makes a module namespace an exotic object whose
[[GetOwnProperty]]returns a data descriptor holding the binding'scurrent value:
So
"value" in desc,desc.writable, and anything that branches ondesc.getsees a different shape. Liveness itself is correct either way —the accessor reads through to the binding.
Why it was landed anyway
Before #9879 the aliased binding was a dead snapshot: the value was simply
wrong after reassignment. Trading a wrong value for a right value with an
unusual descriptor is a clear net improvement, and the alternative — a real
module-namespace exotic object whose internal methods do the liveness — is a
much larger change than the bug warranted.
What this issue wants
Either implement the namespace as an exotic object so
[[GetOwnProperty]]synthesises a data descriptor over the live binding, or special-case
getOwnPropertyDescriptor/Object.keys/JSON.stringifyon namespaceobjects to report the data shape while keeping the accessor storage.
A test is worth adding regardless:
test_gap_dynamic_import_alias_binding.tscovers liveness under GC stress but asserts nothing about descriptor shape, so
this deviation is currently invisible to the suite.
Introduced by #9879, landed via merge train #9888.