Repository navigation
fix(state): wrap fill and copyWithin through the Proxy - #188
Merged
Merged
Conversation
…planning files Move the wrap-array-methods-on-hookless-state and drop-unread-hooks-slot-on-objects changes to the archive, add the synced array-mutation main spec, the planning files of the wrap-fill-and-copywithin change, and the inbox, ledger and review files the previous runs wrote under .do/.
The wrap-fill-and-copywithin change alters how many wakes one fill call causes. This case subscribes one effect that reads every index of a hookless array and times fill calls that change every element, so the unchanged library records the baseline. Typed perf because it measures performance; no library behaviour changes.
A reactive array hands out the native fill and copyWithin, so each element write wakes an every-index subscriber on its own. Add both names to the mutating method set so they run in a batch and wake each subscriber once after the call returns. Cover wake counts, batches, bounds and frozen arrays on both kinds of state, and add fill and copyWithin to the property-based array test.
A sync effect that only calls fill raises nothing and raises once it also read a written index. copyWithin raises only when its source and target ranges overlap. Child and microtask effects raise nothing. On hooked state, onRead sees the method name and every property read, and onWrite sees every property written and no other.
The main array-mutation spec purpose and requirement, the root key behaviors and the source wrapArrayMethod row now count fill and copyWithin.
Once fill and copyWithin run through the Proxy inside a batch, array targets reach handleBatchFastPath, which until then sees only plain objects. Its [SUBSCRIBERS] load then deoptimizes 36 times per 10-cycle run (wrong map), and the case 'many dependencies', which runs after 'array fill (hookless)', gets slower: +10.6% on medians and +6.8% on totals over three interleaved A/B runs of 10 cycles against 87c8823 (A/A spread 4.3% and 2.4%). Control: on 87c8823, with fill wrapped by hand in batch(), the same slowdown and the same deopt pattern appear (2.2 ms against 1.8 ms on a fill-then-many-dependencies sequence). The cause is arrays on the shared batch write path, not the two names added to the method set, its Set lookup or code layout. handleBatchArrayWrite now serves arrays through a set trap of its own, picked by one Array.isArray per state() call, shared with hooksSlotFor. Interleaved A/B after the change, against 87c8823: many dependencies -0.5% on medians and -1.4% on totals, array fill -50.2%, no case slower beyond its A/A spread. The subscriber lookup stays inlined: calling findSubscribers instead costs 2.2% on medians and 2.4% on totals of array fill (three interleaved 10-cycle pairs).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
A reactive array handed out the native
fillandcopyWithin, so every element they wrote woke subscribers on its own, outside any batch. On trunk18f5a71an effect reading every index of[1, 2, 3]re-ran 3 times forfill(9)and 2 times forcopyWithin(0, 1), wherereverse()woke it once.Changes Made
fillandcopyWithinjoin the wrapped mutating method set, so a call wakes each subscriber at most once and a call that changes nothing wakes nobody (5b42960).many dependenciesran 10.6% slower on medians in an interleaved A/B against87c8823(2dbcee2).fillthrows in a sync top-level effect only when that effect read an index the call writes, andcopyWithinonly when its ranges overlap. The spec now states this.lengthnow re-runs once perfillcall where before it did not run at all, by the spec's existing "regardless of which properties the subscriber read" rule. It is the same class of wake-count change as fix(state): intercept array mutating methods on both kinds of state #186, so it ships with the next breaking release together with that one. No version bump here.AGENTS.mdandsrc/AGENTS.mdname nine methods.Performance Impact
array fill (hookless)median 79.53 ms before, 39.64 ms after; totals 1673.64 ms and 837.81 ms (default 3-cycle run, ticket 04 report).87c8823shows no existing case slower beyond its own A/A spread.many dependenciesreads -0.5% on medians and -1.4% on totals.findSubscribersinstead cost +2.2% onarray fill, about 0.4 points above that case's A/A spread. The margin is thin, and the performance tournament should revisit it.Test Coverage
npm test322 pass, coverage 100% lines, branches and functions,npm run checkclean on2dbcee2.tests/array-methods.test.ts,tests/property-array-mutations.test.ts,tests/infinite-loop.test.tsandtests/state-hooks.test.tscover wake counts, batches, bounds, frozen arrays, loop behaviour and hook observation on both kinds of state.fill(9)on a frozen[9, 9, 9]stays silent where native throws, because the set trap short-circuits unchanged writes..do/inbox/2026-10-25-same-value-writes-to-frozen-targets-do-not-throw.mdtracks it.Related Issues
The change came out of
.do/inbox/2026-10-25-fill-and-copywithin-not-batched.md; there is no GitHub issue. Follows #186.Checklist