Repository navigation
Map/Set indexed reads return NaN after array pointer hardening #8060
Description
Regression
At exact current main ef6e111a4fb85dcc9fd81f69c30bf57a0bf40a61, indexed reads from live Map/Set receivers routed through array-like runtime fallbacks return NaN. Six pre-existing tests fail individually and deterministically:
array::collection_tag_tests::a_live_map_receiver_still_reports_its_size_through_the_registry:NaN, expected map key1.0array::collection_tag_tests::a_live_set_receiver_still_reads_its_elements_through_the_registry:NaN, expected set value5.0value::dyn_index::collection_tag_tests::collection_receivers_still_use_their_authoritative_registries:NaN, expected map key1.0typed_feedback::tests::typed_feedback_boxed_fallback_preserves_fractional_keys_for_array_like_receivers:NaN, expected set value20.0typed_feedback::tests::runtime_dynamic_index_fallbacks_preserve_fractional_keys_for_array_like_receivers:NaN, expected set value20.0typed_feedback::tests::polymorphic_index_fallbacks_preserve_fractional_keys_for_array_like_receivers:NaN, expected set value20.0
For example:
cargo test -p perry-runtime --lib \
array::collection_tag_tests::a_live_map_receiver_still_reports_its_size_through_the_registry \
-- --exactCausal proof
This is an ordering regression introduced by #8041 (971d6ffb6b10c04724cd28d9ee6da2d750c84420), not collection-registry lifetime or GC rooting:
- fix(gc): install array growth forwarding for low-address arenas #8041 correctly hardened
clean_arr_ptrto return null for every tracked object whoseGcHeader::obj_type != GC_TYPE_ARRAY. js_array_get_f64callsclean_arr_ptr(arr)before its existingGC_TYPE_MAP/GC_TYPE_SETbranches.- A genuine live Map or Set is therefore rejected at the clean funnel and returns
NaN; its collection tag and authoritative registry can never be consulted. js_array_lengthis the positive control: it checks the Map/Set tag plus authoritative registry beforeclean_arr_ptr, so the same live receivers still report the correct sizes. The array collection tests reach their expected length assertions and fail only on the subsequent indexed read.js_dyn_index_getfirst confirms the receiver through the authoritative collection registry, then delegates tojs_array_get_f64, which reproduces the same early rejection.
This also explains the three typed-feedback failures, since their get fallbacks converge on that dynamic/array accessor. Integer indexed reads are broken; the fractional-key undefined/no-mutation assertions remain the required control.
Required fix
Route authoritative Map/Set receivers before strict array cleaning in js_array_get_f64, matching the proven js_array_length ordering, or use an equivalent dedicated collection accessor.
Keep #8041's strict rejection unchanged for genuine non-array/non-collection receivers. The fix should retain:
- receiver-tag gating before registry probes;
- the authoritative Map/Set registry check (never trust the tag alone);
- no collection registry probes for genuine arrays;
- fractional Map/Set keys returning undefined without mutation;
- all six exact tests passing locally.
Activity
fix(array): route a Map/Set receiver before the array-only funnel in …