-
-
Notifications
You must be signed in to change notification settings - Fork 158
fix(runtime): make iterator own properties readable; present-undefined .next throws (follow-up to #9066) #9075
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| ### Fixed: iterator own properties were write-only through the by-name GET; `it.next = undefined` silently ran the builtin (follow-up to #9066, PR #9075) | ||
|
|
||
| The Map/Set-iterator arm in the by-name GET tail returned `undefined` for every non-`next` key without consulting own fields, so the #9066 reserved-floor storage was write-only through that lane — user properties stored past the floor (and hole-squeeze survivors) read back `undefined` while their values sat intact in the overflow spill. The arm moved to `accessors::map_set_iterator_property` with own-field shadowing first (ordinary [[Get]] order, so an own `return` patch also shadows the synthetic bound method). | ||
|
|
||
| Also per review: an own `next` explicitly assigned `undefined` is present-but-non-callable and now throws per IteratorNext (bytes-based presence scan, no allocation on the unpatched hot path), and a failed reserved-floor seed drops the write instead of proceeding unseeded onto the backing-collection field. | ||
|
|
||
| Validated byte-for-byte against the pinned Node 26.5.1 oracle (16 gap cases, including the reviewer's 12-add/10-delete survivor shape) plus 6 `reserved_floor` unit tests. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1227,36 +1227,17 @@ pub(crate) fn get_field_by_name_object_tail( | |
| } | ||
| } | ||
|
|
||
| // #2856: a property READ (not a call) of `next` on a Map/Set | ||
| // iterator object must yield a callable (so `typeof it.next === | ||
| // "function"` and `const n = it.next; n()` work). The iterators | ||
| // dispatch via class id and store no `next` field, so bind the | ||
| // method to the receiver. Also bind the self-iterator methods. | ||
| // #2856 synthetic method reads + #9019 own-field shadowing for | ||
| // Map/Set iterator receivers — body in | ||
| // `accessors::map_set_iterator_property`. `None` means the key is | ||
| // `next` with no own patch: the generic scans below resolve the | ||
| // prototype thunk. | ||
| if !key.is_null() | ||
| && ((*obj).class_id == crate::collection_iter_object::MAP_ITERATOR_CLASS_ID | ||
| || (*obj).class_id == crate::collection_iter_object::SET_ITERATOR_CLASS_ID) | ||
| { | ||
| let key_ptr = (key as *const u8).add(std::mem::size_of::<crate::StringHeader>()); | ||
| let key_len = (*key).byte_len as usize; | ||
| let key_bytes = std::slice::from_raw_parts(key_ptr, key_len); | ||
| // `next` is an ordinary prototype method. Do not bind it to the | ||
| // iterator at property-read time: `iterator.next.call(other)` must | ||
| // receive `other` and perform the spec brand check. The remaining | ||
| // legacy synthetic methods still use the bound-method path. | ||
| let bind_name: Option<&'static [u8]> = match key_bytes { | ||
| b"return" => Some(b"return"), | ||
| b"throw" => Some(b"throw"), | ||
| b"@@iterator" => Some(b"@@iterator"), | ||
| _ => None, | ||
| }; | ||
| if let Some(name) = bind_name { | ||
| let this_f64 = | ||
| f64::from_bits(crate::value::js_nanbox_pointer(obj as i64).to_bits()); | ||
| let result = js_class_method_bind(this_f64, name.as_ptr(), name.len()); | ||
| return JSValue::from_bits(result.to_bits()); | ||
| } | ||
| if key_bytes != b"next" { | ||
| return JSValue::undefined(); | ||
| if let Some(v) = super::accessors::map_set_iterator_property(obj, key) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- target delegation ---'
sed -n '1200,1265p' crates/perry-runtime/src/object/field_get_set/get_field_by_name_tail.rs
printf '%s\n' '--- bound helper ---'
rg -n -A100 -B20 'fn map_set_iterator_property|map_set_iterator_property' crates/perry-runtime/src
printf '%s\n' '--- descriptor and own-property lookup definitions ---'
rg -n -A45 -B15 'own_data_field_by_name|own.*property|property.*descriptor|Accessor' crates/perry-runtime/src/object
printf '%s\n' '--- scoped repository guidance ---'
find /tmp/coderabbit-repo-knowledge/perryts-perry-d4a878bc -type f -name '*.md' -maxdepth 3 -printRepository: PerryTS/perry Length of output: 50370 🏁 Script executed: #!/bin/bash
set -e
log=/tmp/coderabbit-shell-logs/shell-output-UyBoIf
printf '%s\n' '--- helper references from prior output ---'
rg -n -C 8 'map_set_iterator_property' "$log" | head -120
printf '%s\n' '--- target lines ---'
sed -n '1225,1250p' crates/perry-runtime/src/object/field_get_set/get_field_by_name_tail.rs
printf '%s\n' '--- descriptor-related definitions in field_get_set ---'
rg -n -C 12 'fn (own_data_field_by_name|get_property_attrs|.*descriptor.*lookup)|own_data_field_by_name|get_property_attrs' crates/perry-runtime/src/object/field_get_setRepository: PerryTS/perry Length of output: 50369 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- map/set iterator helper ---'
sed -n '130,225p' crates/perry-runtime/src/object/field_get_set/accessors.rs
printf '%s\n' '--- own data lookup binding ---'
rg -n -C 10 'pub.*fn own_data_field_by_name|fn own_data_field_by_name' crates/perry-runtime/src/object
printf '%s\n' '--- accessor descriptor storage and read path ---'
rg -n -C 8 'get_accessor_descriptor|accessor_descriptor_keys_for_obj|js_closure_call0|acc\.get' crates/perry-runtime/src/object crates/perry-runtime/src/closure.rsRepository: PerryTS/perry Length of output: 50370 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- get-by-name control flow ---'
rg -n 'pub.*js_object_get_field_by_name|fn js_object_get_field_by_name|fn get_field_by_name' crates/perry-runtime/src/object/field_get_set
sed -n '1,120p' crates/perry-runtime/src/object/field_get_set/accessors.rs
sed -n '1,180p' crates/perry-runtime/src/object/field_get_set/map_set_receiver.rs
printf '%s\n' '--- iterator creation and descriptor/expando handling ---'
rg -n -C 10 'MAP_ITERATOR_CLASS_ID|SET_ITERATOR_CLASS_ID|ExoticKind::(Map|Set)|map_set|collection_iter' crates/perry-runtime/src/object crates/perry-runtime/src/collection_iter_object.rs crates/perry-runtime/src | head -500Repository: PerryTS/perry Length of output: 50369 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- public by-name getter ---'
sed -n '1,180p' crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs
printf '%s\n' '--- tail entry and early branches ---'
sed -n '1,180p' crates/perry-runtime/src/object/field_get_set/get_field_by_name_tail.rs
printf '%s\n' '--- generic descriptor lookup in the by-name path ---'
rg -n -C 12 'get_accessor_descriptor|invoke_accessor_getter|descriptor_state|accessor' crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs crates/perry-runtime/src/object/field_get_set/get_field_by_name_tail.rsRepository: PerryTS/perry Length of output: 50370 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- ordinary-object fast lanes before tail ---'
sed -n '180,340p' crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs
printf '%s\n' '--- tail collection and generic lookup branches ---'
rg -n -C 10 'MAP_ITERATOR_CLASS_ID|object_tail|resolve_inherited_field|js_object_get_field_by_name' crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs crates/perry-runtime/src/object/field_get_set/get_field_by_name_tail.rs
printf '%s\n' '--- tail around iterator branch and following fallback ---'
sed -n '1160,1325p' crates/perry-runtime/src/object/field_get_set/get_field_by_name_tail.rsRepository: PerryTS/perry Length of output: 50369 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- tail descriptor-aware read and iterator placement ---'
sed -n '1506,1870p' crates/perry-runtime/src/object/field_get_set/get_field_by_name_tail.rs
printf '%s\n' '--- tail invocation from wrapper ---'
rg -n -C 12 'get_field_by_name_object_tail' crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs
printf '%s\n' '--- accessor API signatures ---'
sed -n '790,970p' crates/perry-runtime/src/object/descriptor_state.rsRepository: PerryTS/perry Length of output: 30412 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- defineProperty dispatch for iterator objects ---'
rg -n -C 14 'define_property|js_object_define_property|GC_TYPE_OBJECT|collection_iter_object|MAP_ITERATOR_CLASS_ID|SET_ITERATOR_CLASS_ID' crates/perry-runtime/src/object/descriptor*.rs crates/perry-runtime/src/object/*define* crates/perry-runtime/src/object/field_get_set crates/perry-runtime/src | head -600
printf '%s\n' '--- own data lookup remainder and descriptor-aware generic branch ---'
sed -n '86,150p' crates/perry-runtime/src/object/field_get_set/accessors.rs
sed -n '1515,1565p' crates/perry-runtime/src/object/field_get_set/get_field_by_name_tail.rsRepository: PerryTS/perry Length of output: 50369 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- descriptor entry points and non-ordinary receiver handling ---'
rg -n 'pub extern "C" fn .*define|define_property|GC_TYPE_MAP|GC_TYPE_SET|GC_TYPE_OBJECT' crates/perry-runtime/src/object/descriptors.rs crates/perry-runtime/src/object/descriptor_state.rs
printf '%s\n' '--- iterator object allocation layout ---'
sed -n '45,100p' crates/perry-runtime/src/collection_iter_object.rs
printf '%s\n' '--- existing iterator accessor/return tests ---'
rg -n -C 8 'own_return|Object.defineProperty|accessor|return.*synthetic|iterator.*return' crates/perry-runtime/src/object/reserved_floor.rs crates/perry-runtime/src/collection_iter_object.rs crates/perry-runtime/src/objectRepository: PerryTS/perry Length of output: 50370 Preserve own accessor precedence before synthetic methods.
🤖 Prompt for AI Agents |
||
| return v; | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use the standard spelling
built-in.The changelog title uses
builtinas an adjective. Replace it withbuilt-in.🧰 Tools
🪛 LanguageTool
[grammar] ~1-~1: Ensure spelling is correct
Context: ...
it.next = undefinedsilently ran the builtin (follow-up to#9066, PR#9075) The Map...(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🤖 Prompt for AI Agents
Source: Linters/SAST tools