Skip to content

Adopt define_method for Map/Set/WeakMap/WeakSet prototype builtins#199

Open
pmatos wants to merge 1 commit into
mainfrom
ptt/refactor-audit/20260703T061649Z
Open

Adopt define_method for Map/Set/WeakMap/WeakSet prototype builtins#199
pmatos wants to merge 1 commit into
mainfrom
ptt/refactor-audit/20260703T061649Z

Conversation

@pmatos

@pmatos pmatos commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Refactoring goal

Continue the define_method migration started in #197 / commit 1d1152d (Adopt define_method for Array.prototype builtins). collections.rs still hand-rolled the two-step create_function + insert_builtin dance at 40 method call sites across setup_map/set/weakmap/weakset_prototype (plus WeakRef and FinalizationRegistry), even though Interpreter::define_method — whose own doc comment says it "replaces the two-step create_function + insert_builtin dance repeated at every builtin call site" — already captures exactly that pattern.

This was surfaced as the single highest impact × safety opportunity by an improve-codebase-architecture audit (top finding in 3 of 4 subsystem explorers). Switching collections.rs over removes ~200 lines of net duplication and brings the file in line with the one established convention.

What changed

  • 40 let x = self.create_function(JsFunction::native("name", len, |…| {…})); cell.borrow_mut().insert_builtin("name", x); sites → one-line self.define_method(target, "name", len, |…| {…}) calls.
  • Reused iterator functions keep their let-binding so aliasing is preserved: Map.prototype.entries is reused for [@@iterator]; Set.prototype.values is reused for keys and [@@iterator].
  • Deliberately untouched (not the plain method-install shape): the size/species getters, symbol-keyed installs, the constructor data property, and the static Map.groupBy.

define_method installs the same writable / non-enumerable / configurable own property the two-step dance did, so behavior is unchanged. The large line delta is mechanical re-indentation (cargo fmt de-indents each closure body one level once the wrapper is gone) — the same shape as the Array migration's diff.

Tests that validate it

Written before the refactor (TDD) and left in place as a regression guard:

collection_prototype_methods_have_correctly_shaped_builtins (src/interpreter/tests.rs) — a JS-level characterization test (public seam, not internals) pinning, for every migrated method:

  • typeof === "function", correct .name and .length;
  • the writable:true / enumerable:false / configurable:true descriptor (Object.getOwnPropertyDescriptor);
  • the well-known aliasing (Map.prototype[@@iterator] === entries; Set.prototype.keys === values === [@@iterator]);
  • functional smoke of Map/Set/WeakMap/WeakSet get/set/has/delete/size/forEach/iteration and Set union/intersection/isSubsetOf.

Verification

Check Result
cargo test --release 197/197 pass (incl. new test + smoke oracle)
cargo clippy --release -- -D warnings clean
cargo fmt --check clean
run-custom-tests.py 3/3
test262 built-ins/{Map,Set,WeakMap,WeakSet,WeakRef,FinalizationRegistry} 1772/1772 (100%), 0 regressions
Full test262 suite 0 regressions attributable to this change

The full-suite run reports 22 baseline deltas, but they are entirely in intl402/DateTimeFormat / Temporal toLocaleString (ICU-data-sensitive locale formatting) and reproduce identically with this change stashed — pre-existing environment/baseline drift, not caused by this refactor, which touches no Intl code.

🤖 Generated with Claude Code

setup_map/set/weakmap/weakset_prototype (plus WeakRef and
FinalizationRegistry) hand-rolled the create_function + insert_builtin
dance at 40 method call sites, even though define_method — added for
atomics.rs and adopted for Array.prototype in 1d1152d — already captures
that exact pattern. Switching collections.rs over removes ~200 lines of
repetition and brings the file in line with the one established
convention.

Behavior is unchanged: define_method installs the same
writable/non-enumerable/configurable own property the two-step dance did.
The reused iterator functions (Map.prototype.entries reused for
[@@iterator]; Set.prototype.values reused for keys and [@@iterator]) keep
their let-bindings so the aliasing is preserved; getters (size, species),
symbol-keyed installs, the constructor property, and the static
Map.groupBy are deliberately left untouched.

Adds collection_prototype_methods_have_correctly_shaped_builtins, a
JS-level characterization test pinning the name/length/descriptor shape
and functional behavior of every migrated method plus the iterator
aliasing, written before the refactor and left in place as a regression
guard.

Verified: cargo test (197/197), cargo clippy -D warnings, cargo fmt
--check, and test262 built-ins/{Map,Set,WeakMap,WeakSet,WeakRef,
FinalizationRegistry} (1772/1772, 100%) all green before and after. The
full-suite run shows 0 regressions attributable to this change — the 22
baseline deltas are pre-existing intl402/DateTimeFormat ICU-data drift,
identical with this change stashed.

🤖 Generated with Claude Code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant