feat(ReJest): record native updates from platform mutations on new arch#9653
Draft
piaskowyk wants to merge 1 commit into
Draft
feat(ReJest): record native updates from platform mutations on new arch#9653piaskowyk wants to merge 1 commit into
piaskowyk wants to merge 1 commit into
Conversation
ReJest's native-update recording broke on the new architecture. It read native prop values synchronously from the shadow node (getNewestCloneOfShadowNode), which deadlocks against in-flight Fabric commits and returns stale values once animations are applied via commits, the animation backend, or Core Animation. This adds a C++ NativeMutationsRegistry that records the ShadowViewMutations Reanimated sends to the platform - tapping the same pullTransaction funnel the layout-animations delegate owns - plus the Core Animation transition descriptors routed to the iOS CALayer. ReJest reads them back through new UI-runtime globals and reconstructs native snapshots from them, so toMatchNativeSnapshots works again without any per-frame native reads. All native code is gated behind the existing RUNTIME_TEST_FLAG static feature flag (zero cost in production builds). Also fixes the missing `tag` on `_updateProps` operations that prevented ReJest from grouping recorded updates per component. Verified on the iOS simulator (fabric-example): the new nativeMutationsRecording suite passes - native opacity mutations are recorded (1 -> 0) and match the JS-computed values. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Summary
ReJest's recording of native animation updates broke on the new
architecture. It read native prop values synchronously from the shadow node
(
_obtainProp→getNewestCloneOfShadowNode), which on the new archdeadlocks against in-flight Fabric commits and returns stale values once
animations are applied via commits / the animation backend / Core Animation.
This is why several
toMatchNativeSnapshots/getNativeSnapshotstests weredisabled with
// TODO: ... it hangsand// tag is not passed to _updateProps.This PR records native updates the way the user suggested — by listening to
the
ShadowViewMutations Reanimated sends to the platform, reusing the samepullTransactionfunnel the layout-animation delegate already owns — and feedsthem into the ReJest matchers.
How it works
JS channel (values the worklet computes) is still captured via the existing
_updateProps/_notifyAboutProgressinterception.Native channel (values sent to the platform) is now captured in C++:
NativeMutationsRegistry(Common/cpp/reanimated/Fabric/updates/) — athread-safe recorder of the
ShadowViewMutations sent to the platform, plusCore Animation transition descriptors (
from/to/duration) routed to theiOS
CALayer(which emit no per-frame mutation).record(filteredMutations)is called at the end of bothLayoutAnimationsProxy_Experimental::pullTransactionand_Legacy::pullTransaction,so it captures animated styles, layout animations and shared element
transitions through one chokepoint. CSS routed to Core Animation is captured
at the
CSSPlatformTransitionProxy::runboundary._startRecordingNativeMutations,_stopRecordingNativeMutations,_clearRecordedNativeMutations,_getRecordedNativeMutations,_obtainLatestRecordedProp).UpdatesContainer.getNativeSnapshotsnow reconstructs native snapshots fromthe recorded mutation stream instead of reading the shadow node per frame —
which is what removes the deadlock — while preserving the existing matcher /
frame-lag contract.
All native code is gated behind the existing
RUNTIME_TEST_FLAGstatic featureflag (
if constexpr+ a runtime atomic), so it is compiled out / a no-op inproduction builds.
Also fixes a latent bug:
_updatePropsoperations were missing theirtag, soReJest could not group recorded updates per component (the
// tag is not passedTODOs).
Verification
Built and run on the iOS simulator (
fabric-example, new arch, experimentalproxy +
IOS_CSS_CORE_ANIMATION). The newnative updatessuite passes:The C++ recorder captured the real per-frame native opacity values (1 → 0) and
toMatchNativeSnapshotsconfirmed they match the JS-computed values — thecross-check the framework lost on the new arch.
Still draft — follow-ups
recording call site (the experimental proxy's
filteredMutations); dedicatedtests are blocked by a pre-existing harness hang on entering/exiting
animations (the
entering and exiting/layout transitionssuites arealready disabled).
mutations (
totalRecorded == 0) and the CSS-animation component's unmounthangs the harness — needs investigation of the iOS CSS apply path.
CSSPlatformTransitionProxy::run; the iOS opacity transition did not surface adescriptor in this run — needs a closer look at the routing.
_obtainLatestRecordedPropis installed but currently unused by the JS side(kept as a usable API); could be removed.
🤖 Generated with Claude Code