diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index 5214e31adf8c..c0a1a9a5cd73 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<1eca66b21554b00725f2a9be894a0db9>> */ /** @@ -294,6 +294,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enablePropsUpdateReconciliationAndroid(): Boolean = accessor.enablePropsUpdateReconciliationAndroid() + /** + * Gates a defensive guard around Scheduler::uiManagerDidDispatchCommand and uiManagerDidFinishTransaction that prevents queued rendering-update lambdas from dereferencing the SchedulerDelegate after it has been destroyed (use-after-free). + */ + @JvmStatic + public fun enableSchedulerDelegateInvalidation(): Boolean = accessor.enableSchedulerDelegateInvalidation() + /** * When enabled, it will use SwiftUI for filter effects like blur on iOS. */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index 2509813ce330..a138abe7be3c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<76d977ea53cb2a37fc2ea8549e31cebd>> */ /** @@ -64,6 +64,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableNetworkEventReportingCache: Boolean? = null private var enablePreparedTextLayoutCache: Boolean? = null private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null + private var enableSchedulerDelegateInvalidationCache: Boolean? = null private var enableSwiftUIBasedFiltersCache: Boolean? = null private var enableViewCullingCache: Boolean? = null private var enableViewRecyclingCache: Boolean? = null @@ -508,6 +509,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableSchedulerDelegateInvalidation(): Boolean { + var cached = enableSchedulerDelegateInvalidationCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableSchedulerDelegateInvalidation() + enableSchedulerDelegateInvalidationCache = cached + } + return cached + } + override fun enableSwiftUIBasedFilters(): Boolean { var cached = enableSwiftUIBasedFiltersCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index 02e2fd2f8589..24abbbaa92ee 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -116,6 +116,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enablePropsUpdateReconciliationAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun enableSchedulerDelegateInvalidation(): Boolean + @DoNotStrip @JvmStatic public external fun enableSwiftUIBasedFilters(): Boolean @DoNotStrip @JvmStatic public external fun enableViewCulling(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index 998366d958af..4075e0ac58d1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<54a4f6d01a052e5ab19b15652d5ab1a5>> + * @generated SignedSource<<9e5b3192d1bec953c116d959ad63283d>> */ /** @@ -111,6 +111,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enablePropsUpdateReconciliationAndroid(): Boolean = false + override fun enableSchedulerDelegateInvalidation(): Boolean = false + override fun enableSwiftUIBasedFilters(): Boolean = false override fun enableViewCulling(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index 398b5df92b2c..1284abcf7a4c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<4d09d820304f454bc3850da89087caec>> + * @generated SignedSource<<378d6a8de497d26ebbbf55885be27a21>> */ /** @@ -68,6 +68,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableNetworkEventReportingCache: Boolean? = null private var enablePreparedTextLayoutCache: Boolean? = null private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null + private var enableSchedulerDelegateInvalidationCache: Boolean? = null private var enableSwiftUIBasedFiltersCache: Boolean? = null private var enableViewCullingCache: Boolean? = null private var enableViewRecyclingCache: Boolean? = null @@ -556,6 +557,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableSchedulerDelegateInvalidation(): Boolean { + var cached = enableSchedulerDelegateInvalidationCache + if (cached == null) { + cached = currentProvider.enableSchedulerDelegateInvalidation() + accessedFeatureFlags.add("enableSchedulerDelegateInvalidation") + enableSchedulerDelegateInvalidationCache = cached + } + return cached + } + override fun enableSwiftUIBasedFilters(): Boolean { var cached = enableSwiftUIBasedFiltersCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt index c806054c45fc..2e5fa22f4539 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<187ac62197545fbce9d537527b4aed3b>> + * @generated SignedSource<<6a5fd95dffcab319acb765aa25a33d8d>> */ /** @@ -27,6 +27,8 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android : override fun enableAccessibilityOrder(): Boolean = true + override fun enableSchedulerDelegateInvalidation(): Boolean = true + override fun enableSwiftUIBasedFilters(): Boolean = true override fun preventShadowTreeCommitExhaustion(): Boolean = true diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index 3113bd3ab653..13a3b26839b4 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<592acf445c4b4e56651cd6afb57e46fa>> + * @generated SignedSource<<42e555a40da280b24f84e3ee5b45051d>> */ /** @@ -111,6 +111,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enablePropsUpdateReconciliationAndroid(): Boolean + @DoNotStrip public fun enableSchedulerDelegateInvalidation(): Boolean + @DoNotStrip public fun enableSwiftUIBasedFilters(): Boolean @DoNotStrip public fun enableViewCulling(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index 7c27db4af32a..f586f2e3fcb5 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<55e4d6d6dc930b4ccf3e20cb77f7a042>> + * @generated SignedSource<> */ /** @@ -303,6 +303,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableSchedulerDelegateInvalidation() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableSchedulerDelegateInvalidation"); + return method(javaProvider_); + } + bool enableSwiftUIBasedFilters() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableSwiftUIBasedFilters"); @@ -809,6 +815,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enablePropsUpdateReconciliationAndroid( return ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid(); } +bool JReactNativeFeatureFlagsCxxInterop::enableSchedulerDelegateInvalidation( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableSwiftUIBasedFilters( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableSwiftUIBasedFilters(); @@ -1207,6 +1218,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enablePropsUpdateReconciliationAndroid", JReactNativeFeatureFlagsCxxInterop::enablePropsUpdateReconciliationAndroid), + makeNativeMethod( + "enableSchedulerDelegateInvalidation", + JReactNativeFeatureFlagsCxxInterop::enableSchedulerDelegateInvalidation), makeNativeMethod( "enableSwiftUIBasedFilters", JReactNativeFeatureFlagsCxxInterop::enableSwiftUIBasedFilters), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index e6d4455961ef..2b3741c71e80 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -162,6 +162,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enablePropsUpdateReconciliationAndroid( facebook::jni::alias_ref); + static bool enableSchedulerDelegateInvalidation( + facebook::jni::alias_ref); + static bool enableSwiftUIBasedFilters( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index e0a788e58f1c..a8bddab5889c 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<16024bed6914b4b74d4a50de142e872a>> + * @generated SignedSource<> */ /** @@ -202,6 +202,10 @@ bool ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid() { return getAccessor().enablePropsUpdateReconciliationAndroid(); } +bool ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation() { + return getAccessor().enableSchedulerDelegateInvalidation(); +} + bool ReactNativeFeatureFlags::enableSwiftUIBasedFilters() { return getAccessor().enableSwiftUIBasedFilters(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 08516f6d1d1c..b0c8f5bb492b 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<33ffe2572bbaeba64d2167dbac4bb0ab>> + * @generated SignedSource<<8b4288e3f5a8b26951150a3c75ad4356>> */ /** @@ -259,6 +259,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enablePropsUpdateReconciliationAndroid(); + /** + * Gates a defensive guard around Scheduler::uiManagerDidDispatchCommand and uiManagerDidFinishTransaction that prevents queued rendering-update lambdas from dereferencing the SchedulerDelegate after it has been destroyed (use-after-free). + */ + RN_EXPORT static bool enableSchedulerDelegateInvalidation(); + /** * When enabled, it will use SwiftUI for filter effects like blur on iOS. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index ba5f777abe14..0f1a8ac663c3 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<51dc335c5463b6c3f34c5330d69b6a00>> + * @generated SignedSource<> */ /** @@ -821,6 +821,24 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableSchedulerDelegateInvalidation() { + auto flagValue = enableSchedulerDelegateInvalidation_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(44, "enableSchedulerDelegateInvalidation"); + + flagValue = currentProvider_->enableSchedulerDelegateInvalidation(); + enableSchedulerDelegateInvalidation_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() { auto flagValue = enableSwiftUIBasedFilters_.load(); @@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "enableSwiftUIBasedFilters"); + markFlagAsAccessed(45, "enableSwiftUIBasedFilters"); flagValue = currentProvider_->enableSwiftUIBasedFilters(); enableSwiftUIBasedFilters_ = flagValue; @@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(45, "enableViewCulling"); + markFlagAsAccessed(46, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(46, "enableViewRecycling"); + markFlagAsAccessed(47, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForImage() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(47, "enableViewRecyclingForImage"); + markFlagAsAccessed(48, "enableViewRecyclingForImage"); flagValue = currentProvider_->enableViewRecyclingForImage(); enableViewRecyclingForImage_ = flagValue; @@ -902,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForScrollView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(48, "enableViewRecyclingForScrollView"); + markFlagAsAccessed(49, "enableViewRecyclingForScrollView"); flagValue = currentProvider_->enableViewRecyclingForScrollView(); enableViewRecyclingForScrollView_ = flagValue; @@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(49, "enableViewRecyclingForText"); + markFlagAsAccessed(50, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -938,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(50, "enableViewRecyclingForView"); + markFlagAsAccessed(51, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -956,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimenta // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(51, "enableVirtualViewContainerStateExperimental"); + markFlagAsAccessed(52, "enableVirtualViewContainerStateExperimental"); flagValue = currentProvider_->enableVirtualViewContainerStateExperimental(); enableVirtualViewContainerStateExperimental_ = flagValue; @@ -974,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewDebugFeatures() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(52, "enableVirtualViewDebugFeatures"); + markFlagAsAccessed(53, "enableVirtualViewDebugFeatures"); flagValue = currentProvider_->enableVirtualViewDebugFeatures(); enableVirtualViewDebugFeatures_ = flagValue; @@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::fixDifferentiatorParentTagForUnflattenCase // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(53, "fixDifferentiatorParentTagForUnflattenCase"); + markFlagAsAccessed(54, "fixDifferentiatorParentTagForUnflattenCase"); flagValue = currentProvider_->fixDifferentiatorParentTagForUnflattenCase(); fixDifferentiatorParentTagForUnflattenCase_ = flagValue; @@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::fixFindShadowNodeByTagRaceCondition() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(54, "fixFindShadowNodeByTagRaceCondition"); + markFlagAsAccessed(55, "fixFindShadowNodeByTagRaceCondition"); flagValue = currentProvider_->fixFindShadowNodeByTagRaceCondition(); fixFindShadowNodeByTagRaceCondition_ = flagValue; @@ -1028,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(55, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(56, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -1046,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::fixYogaFlexBasisFitContentInMainAxis() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(56, "fixYogaFlexBasisFitContentInMainAxis"); + markFlagAsAccessed(57, "fixYogaFlexBasisFitContentInMainAxis"); flagValue = currentProvider_->fixYogaFlexBasisFitContentInMainAxis(); fixYogaFlexBasisFitContentInMainAxis_ = flagValue; @@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxAssertSingleHostState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(57, "fuseboxAssertSingleHostState"); + markFlagAsAccessed(58, "fuseboxAssertSingleHostState"); flagValue = currentProvider_->fuseboxAssertSingleHostState(); fuseboxAssertSingleHostState_ = flagValue; @@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "fuseboxEnabledRelease"); + markFlagAsAccessed(59, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxFrameRecordingEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "fuseboxFrameRecordingEnabled"); + markFlagAsAccessed(60, "fuseboxFrameRecordingEnabled"); flagValue = currentProvider_->fuseboxFrameRecordingEnabled(); fuseboxFrameRecordingEnabled_ = flagValue; @@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(61, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxScreenshotCaptureEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "fuseboxScreenshotCaptureEnabled"); + markFlagAsAccessed(62, "fuseboxScreenshotCaptureEnabled"); flagValue = currentProvider_->fuseboxScreenshotCaptureEnabled(); fuseboxScreenshotCaptureEnabled_ = flagValue; @@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "hideOffscreenVirtualViewsOnIOS"); + markFlagAsAccessed(63, "hideOffscreenVirtualViewsOnIOS"); flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS(); hideOffscreenVirtualViewsOnIOS_ = flagValue; @@ -1172,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(63, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(64, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1190,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "perfIssuesEnabled"); + markFlagAsAccessed(65, "perfIssuesEnabled"); flagValue = currentProvider_->perfIssuesEnabled(); perfIssuesEnabled_ = flagValue; @@ -1208,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "perfMonitorV2Enabled"); + markFlagAsAccessed(66, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1226,7 +1244,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "preparedTextCacheSize"); + markFlagAsAccessed(67, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1244,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(68, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1262,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2Android() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "redBoxV2Android"); + markFlagAsAccessed(69, "redBoxV2Android"); flagValue = currentProvider_->redBoxV2Android(); redBoxV2Android_ = flagValue; @@ -1280,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2IOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "redBoxV2IOS"); + markFlagAsAccessed(70, "redBoxV2IOS"); flagValue = currentProvider_->redBoxV2IOS(); redBoxV2IOS_ = flagValue; @@ -1298,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(71, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "shouldTriggerResponderTransferOnScrollAndroid"); + markFlagAsAccessed(72, "shouldTriggerResponderTransferOnScrollAndroid"); flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid(); shouldTriggerResponderTransferOnScrollAndroid_ = flagValue; @@ -1334,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(73, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1352,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::syncAndroidClipBoundsWithOverflow() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(73, "syncAndroidClipBoundsWithOverflow"); + markFlagAsAccessed(74, "syncAndroidClipBoundsWithOverflow"); flagValue = currentProvider_->syncAndroidClipBoundsWithOverflow(); syncAndroidClipBoundsWithOverflow_ = flagValue; @@ -1370,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(74, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(75, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1388,7 +1406,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(75, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(76, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1406,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommitT // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(76, "updateRuntimeShadowNodeReferencesOnCommitThread"); + markFlagAsAccessed(77, "updateRuntimeShadowNodeReferencesOnCommitThread"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommitThread(); updateRuntimeShadowNodeReferencesOnCommitThread_ = flagValue; @@ -1424,7 +1442,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(77, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(78, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1442,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "useFabricInterop"); + markFlagAsAccessed(79, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1460,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useLISAlgorithmInDifferentiator() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "useLISAlgorithmInDifferentiator"); + markFlagAsAccessed(80, "useLISAlgorithmInDifferentiator"); flagValue = currentProvider_->useLISAlgorithmInDifferentiator(); useLISAlgorithmInDifferentiator_ = flagValue; @@ -1478,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(80, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(81, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1496,7 +1514,7 @@ bool ReactNativeFeatureFlagsAccessor::useNestedScrollViewAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "useNestedScrollViewAndroid"); + markFlagAsAccessed(82, "useNestedScrollViewAndroid"); flagValue = currentProvider_->useNestedScrollViewAndroid(); useNestedScrollViewAndroid_ = flagValue; @@ -1514,7 +1532,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedViewRegistryOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "useOptimizedViewRegistryOnAndroid"); + markFlagAsAccessed(83, "useOptimizedViewRegistryOnAndroid"); flagValue = currentProvider_->useOptimizedViewRegistryOnAndroid(); useOptimizedViewRegistryOnAndroid_ = flagValue; @@ -1532,7 +1550,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(83, "useSharedAnimatedBackend"); + markFlagAsAccessed(84, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1550,7 +1568,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(84, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(85, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1568,7 +1586,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(85, "useTurboModuleInterop"); + markFlagAsAccessed(86, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1586,7 +1604,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(86, "useTurboModules"); + markFlagAsAccessed(87, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1604,7 +1622,7 @@ bool ReactNativeFeatureFlagsAccessor::useUnorderedMapInDifferentiator() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(87, "useUnorderedMapInDifferentiator"); + markFlagAsAccessed(88, "useUnorderedMapInDifferentiator"); flagValue = currentProvider_->useUnorderedMapInDifferentiator(); useUnorderedMapInDifferentiator_ = flagValue; @@ -1622,7 +1640,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(88, "viewCullingOutsetRatio"); + markFlagAsAccessed(89, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1640,7 +1658,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(89, "viewTransitionEnabled"); + markFlagAsAccessed(90, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1658,7 +1676,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(90, "virtualViewPrerenderRatio"); + markFlagAsAccessed(91, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index bc9a1b7b1e27..67a5012fe00e 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<62bf5997f9ee2799bf7fd2cd6d5a6cd1>> + * @generated SignedSource<<17621e5f711352812633c125b7858537>> */ /** @@ -76,6 +76,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableNetworkEventReporting(); bool enablePreparedTextLayout(); bool enablePropsUpdateReconciliationAndroid(); + bool enableSchedulerDelegateInvalidation(); bool enableSwiftUIBasedFilters(); bool enableViewCulling(); bool enableViewRecycling(); @@ -134,7 +135,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 91> accessedFeatureFlags_; + std::array, 92> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -180,6 +181,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableNetworkEventReporting_; std::atomic> enablePreparedTextLayout_; std::atomic> enablePropsUpdateReconciliationAndroid_; + std::atomic> enableSchedulerDelegateInvalidation_; std::atomic> enableSwiftUIBasedFilters_; std::atomic> enableViewCulling_; std::atomic> enableViewRecycling_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index ac1e9c126022..ecc79d832fe4 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<0042e6f884ccf466dda224021ff7a86a>> + * @generated SignedSource<<3d1718afe5b2ed63a918ffe09a4a36b1>> */ /** @@ -203,6 +203,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableSchedulerDelegateInvalidation() override { + return false; + } + bool enableSwiftUIBasedFilters() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 4bbb20893888..2bc41176d59c 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<23e79788b0820f61ba95288412367247>> */ /** @@ -441,6 +441,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enablePropsUpdateReconciliationAndroid(); } + bool enableSchedulerDelegateInvalidation() override { + auto value = values_["enableSchedulerDelegateInvalidation"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableSchedulerDelegateInvalidation(); + } + bool enableSwiftUIBasedFilters() override { auto value = values_["enableSwiftUIBasedFilters"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h index e84785c6fd2c..8c52948d3c02 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<887a3ac5f8a93cc5ae5c8d01ec1d5e46>> */ /** @@ -35,6 +35,10 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur return true; } + bool enableSchedulerDelegateInvalidation() override { + return true; + } + bool enableSwiftUIBasedFilters() override { return true; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 7820c8f313e4..400e4bea814d 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<87316e34516ebbbb34ae384e5a61376b>> */ /** @@ -69,6 +69,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableNetworkEventReporting() = 0; virtual bool enablePreparedTextLayout() = 0; virtual bool enablePropsUpdateReconciliationAndroid() = 0; + virtual bool enableSchedulerDelegateInvalidation() = 0; virtual bool enableSwiftUIBasedFilters() = 0; virtual bool enableViewCulling() = 0; virtual bool enableViewRecycling() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index b6d699cd6e94..8c49f21652bd 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<80e1d4551c51800c138f16228cfae9f6>> */ /** @@ -264,6 +264,11 @@ bool NativeReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid( return ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid(); } +bool NativeReactNativeFeatureFlags::enableSchedulerDelegateInvalidation( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation(); +} + bool NativeReactNativeFeatureFlags::enableSwiftUIBasedFilters( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableSwiftUIBasedFilters(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index b3eb68b5f3a4..a3703ec36073 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<1df283aec4b56f36271a187341e7103d>> */ /** @@ -124,6 +124,8 @@ class NativeReactNativeFeatureFlags bool enablePropsUpdateReconciliationAndroid(jsi::Runtime& runtime); + bool enableSchedulerDelegateInvalidation(jsi::Runtime& runtime); + bool enableSwiftUIBasedFilters(jsi::Runtime& runtime); bool enableViewCulling(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 28750e02d16f..7ea5fc3c782f 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -29,7 +29,8 @@ Scheduler::Scheduler( const SchedulerToolbox& schedulerToolbox, UIManagerAnimationDelegate* animationDelegate, SchedulerDelegate* delegate) - : runtimeExecutor_(schedulerToolbox.runtimeExecutor), + : delegateInvalidated_(std::make_shared>(false)), + runtimeExecutor_(schedulerToolbox.runtimeExecutor), contextContainer_(schedulerToolbox.contextContainer) { // Creating a container for future `EventDispatcher` instance. eventDispatcher_ = std::make_shared>(); @@ -171,6 +172,15 @@ Scheduler::~Scheduler() { LOG(WARNING) << "Scheduler::~Scheduler() was called (address: " << this << ")."; + // Invalidate any lambdas already queued via scheduleRenderingUpdate that + // captured a raw delegate_ pointer; without this they'd dereference a + // dangling SchedulerDelegate after Scheduler teardown. (No replacement + // token is allocated here — Scheduler is going away.) + // Gated to allow controlled rollout / rollback. + if (ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation()) { + *delegateInvalidated_ = true; + } + auto weakRuntimeScheduler = contextContainer_->find>( RuntimeSchedulerKey); @@ -260,6 +270,21 @@ Scheduler::findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN( #pragma mark - Delegate void Scheduler::setDelegate(SchedulerDelegate* delegate) { + // Gated to allow controlled rollout / rollback. + if (ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation() && + delegate_ != delegate) { + // Mark the *current* token invalid: any rendering-update lambda already + // queued holds a shared_ptr to this atomic and will observe `true` on + // its next read, so it no-ops instead of calling into the previous + // delegate (which the caller is about to drop). + *delegateInvalidated_ = true; + // Then install a *fresh* token (a new atomic) so lambdas captured + // against the new delegate use their own non-invalidated flag. + // Reusing the previous atomic and flipping it back to `false` would + // re-arm the in-flight lambdas — exactly the use-after-free we're + // trying to prevent — because they share the same shared_ptr. + delegateInvalidated_ = std::make_shared>(false); + } delegate_ = delegate; } @@ -288,10 +313,21 @@ void Scheduler::uiManagerDidFinishTransaction( if (!mountSynchronously) { auto surfaceId = mountingCoordinator->getSurfaceId(); + // Capture the gating flag at queue time: the lambda's decision to + // honor the invalidation guard is fixed when we enqueue, not when it + // later runs. Avoids per-invocation feature-flag reads and keeps the + // contract for an in-flight lambda stable across flag flips. + auto guardEnabled = + ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation(); runtimeScheduler_->scheduleRenderingUpdate( surfaceId, [delegate = delegate_, + invalidated = delegateInvalidated_, + guardEnabled, mountingCoordinator = std::move(mountingCoordinator)]() { + if (guardEnabled && *invalidated) { + return; + } delegate->schedulerShouldRenderTransactions(mountingCoordinator); }); } else { @@ -314,12 +350,20 @@ void Scheduler::uiManagerDidDispatchCommand( "Scheduler::uiManagerDispatchCommand", "commandName", commandName); if (delegate_ != nullptr) { auto shadowView = ShadowView(*shadowNode); + // See comment in uiManagerDidFinishTransaction above for gating shape. + auto guardEnabled = + ReactNativeFeatureFlags::enableSchedulerDelegateInvalidation(); runtimeScheduler_->scheduleRenderingUpdate( shadowNode->getSurfaceId(), [delegate = delegate_, + invalidated = delegateInvalidated_, + guardEnabled, shadowView = std::move(shadowView), commandName, args]() { + if (guardEnabled && *invalidated) { + return; + } delegate->schedulerDidDispatchCommand(shadowView, commandName, args); }); } diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h index cc665dfde4b5..bd6de832e04d 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -122,6 +123,11 @@ class Scheduler final : public UIManagerDelegate { friend class SurfaceHandler; SchedulerDelegate *delegate_; + // Invalidation token captured by-value into lambdas deferred via + // runtimeScheduler_->scheduleRenderingUpdate. Set to true on delegate + // change or Scheduler destruction so a lambda that outlives its captured + // raw delegate pointer can no-op instead of dereferencing dangling memory. + std::shared_ptr> delegateInvalidated_; SharedComponentDescriptorRegistry componentDescriptorRegistry_; RuntimeExecutor runtimeExecutor_; std::shared_ptr uiManager_; diff --git a/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp b/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp index 60fde1385549..525a6e2e4514 100644 --- a/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp +++ b/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp @@ -320,6 +320,26 @@ void ReactHost::destroyReactInstance() { reactInstanceData_->messageQueueThread = nullptr; } +void ReactHost::unstable_recreateSchedulerDelegateForTesting() { + // Mirrors iOS RCTScheduler dealloc ordering (RCTScheduler.mm: setDelegate + // (nullptr) followed by destruction of the previous SchedulerDelegateProxy, + // while the RuntimeScheduler outlives both). The RuntimeScheduler may still + // hold queued rendering-update lambdas that captured the previous + // delegate's raw pointer by value. + // + // Allocate the replacement BEFORE destroying the previous one so the new + // SchedulerDelegate lands at a distinct address. Otherwise the allocator + // tends to reuse the just-freed slot, masking the use-after-free in + // queued lambdas behind a same-address re-allocation. + auto fresh = std::make_unique( + reactInstanceData_->mountingManager); + fresh->setUIManager(scheduler_->getUIManager()); + scheduler_->setDelegate(nullptr); + schedulerDelegate_ = nullptr; + scheduler_->setDelegate(fresh.get()); + schedulerDelegate_ = std::move(fresh); +} + void ReactHost::reloadReactInstance() { if (isReloadingReactInstance_.exchange(true)) { return; diff --git a/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.h b/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.h index 47645e4a1198..e44eca3b80d2 100644 --- a/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.h +++ b/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.h @@ -92,6 +92,16 @@ class ReactHost { void emitDeviceEvent(folly::dynamic &&args); + /* + * Test-only. Mirrors the iOS RCTScheduler dealloc lifecycle: detach the + * SchedulerDelegate from the Scheduler and destroy it, then install a + * fresh one — all while the RuntimeScheduler (and any queued rendering + * update lambdas) remains alive. This lets Fantom tests reproduce + * lifecycle bugs where a queued lambda outlived its captured raw + * SchedulerDelegate pointer. + */ + void unstable_recreateSchedulerDelegateForTesting(); + private: void createReactInstance(); void destroyReactInstance(); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index c09b656231a5..e930a4f00898 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -514,6 +514,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableSchedulerDelegateInvalidation: { + defaultValue: false, + metadata: { + dateAdded: '2026-05-04', + description: + 'Gates a defensive guard around Scheduler::uiManagerDidDispatchCommand and uiManagerDidFinishTransaction that prevents queued rendering-update lambdas from dereferencing the SchedulerDelegate after it has been destroyed (use-after-free).', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'experimental', + }, enableSwiftUIBasedFilters: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index fe9178d5b88f..9be1938a6448 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<404bdc9f1cd9ea3d65d1b31dccd1591e>> + * @generated SignedSource<<349263e08085c42598f13da74ffbf584>> * @flow strict * @noformat */ @@ -91,6 +91,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableNetworkEventReporting: Getter, enablePreparedTextLayout: Getter, enablePropsUpdateReconciliationAndroid: Getter, + enableSchedulerDelegateInvalidation: Getter, enableSwiftUIBasedFilters: Getter, enableViewCulling: Getter, enableViewRecycling: Getter, @@ -380,6 +381,10 @@ export const enablePreparedTextLayout: Getter = createNativeFlagGetter( * When enabled, Android will receive prop updates based on the differences between the last rendered shadow node and the last committed shadow node. */ export const enablePropsUpdateReconciliationAndroid: Getter = createNativeFlagGetter('enablePropsUpdateReconciliationAndroid', false); +/** + * Gates a defensive guard around Scheduler::uiManagerDidDispatchCommand and uiManagerDidFinishTransaction that prevents queued rendering-update lambdas from dereferencing the SchedulerDelegate after it has been destroyed (use-after-free). + */ +export const enableSchedulerDelegateInvalidation: Getter = createNativeFlagGetter('enableSchedulerDelegateInvalidation', false); /** * When enabled, it will use SwiftUI for filter effects like blur on iOS. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index a49f45388213..77b4f560fdf2 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<6e8201091111a8451911f4e72cdc0932>> + * @generated SignedSource<<1fe579457854f95d09dd24e4578dbc65>> * @flow strict * @noformat */ @@ -69,6 +69,7 @@ export interface Spec extends TurboModule { +enableNetworkEventReporting?: () => boolean; +enablePreparedTextLayout?: () => boolean; +enablePropsUpdateReconciliationAndroid?: () => boolean; + +enableSchedulerDelegateInvalidation?: () => boolean; +enableSwiftUIBasedFilters?: () => boolean; +enableViewCulling?: () => boolean; +enableViewRecycling?: () => boolean; diff --git a/packages/react-native/src/private/testing/fantom/specs/NativeFantom.js b/packages/react-native/src/private/testing/fantom/specs/NativeFantom.js index a47d97b9aacf..298419f3a42a 100644 --- a/packages/react-native/src/private/testing/fantom/specs/NativeFantom.js +++ b/packages/react-native/src/private/testing/fantom/specs/NativeFantom.js @@ -110,6 +110,13 @@ interface Spec extends TurboModule { flushMessageQueue: () => void; flushEventQueue: () => void; produceFramesForDuration: (miliseconds: number) => void; + /** + * Test-only. Mirrors the iOS RCTScheduler dealloc lifecycle by tearing + * down the host's current SchedulerDelegate and installing a fresh one, + * while leaving the RuntimeScheduler (and any queued rendering-update + * lambdas) alive. Used to reproduce SchedulerDelegate-lifecycle UAFs. + */ + unstable_recreateSchedulerDelegate: () => void; validateEmptyMessageQueue: () => void; getRenderedOutput: ( surfaceId: RootTag, diff --git a/private/react-native-fantom/src/index.js b/private/react-native-fantom/src/index.js index 73e40ba9d661..bea88defca0c 100644 --- a/private/react-native-fantom/src/index.js +++ b/private/react-native-fantom/src/index.js @@ -379,6 +379,27 @@ export function runWorkLoop(): void { } } +/** + * Test-only. Mirrors the iOS RCTScheduler dealloc lifecycle: detach the + * host's current SchedulerDelegate from the Scheduler and destroy it, then + * install a fresh one — all while the RuntimeScheduler (and any queued + * rendering-update lambdas) remains alive. Used to reproduce + * SchedulerDelegate-lifecycle bugs (e.g. queued lambdas that captured a + * raw delegate pointer by value). + * + * @example + * ``` + * dispatchCommand(element, 'someCommand', []); + * Fantom.unstable_recreateSchedulerDelegate(); + * Fantom.runWorkLoop(); // pending lambda drains against the now-recreated + * // delegate; if it dereferences the previous one, + * // the test process crashes. + * ``` + */ +export function unstable_recreateSchedulerDelegate(): void { + NativeFantom.unstable_recreateSchedulerDelegate(); +} + /** * Set this flag to `false` to let Fantom run tasks with LogBox installed * (necessary only if you are testing LogBox specifically). diff --git a/private/react-native-fantom/tester/src/NativeFantom.cpp b/private/react-native-fantom/tester/src/NativeFantom.cpp index 5b18de491450..d8bf3834a3ac 100644 --- a/private/react-native-fantom/tester/src/NativeFantom.cpp +++ b/private/react-native-fantom/tester/src/NativeFantom.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,11 @@ void NativeFantom::validateEmptyMessageQueue(jsi::Runtime& /*runtime*/) { } } +void NativeFantom::unstable_recreateSchedulerDelegate( + jsi::Runtime& /*runtime*/) { + appDelegate_.reactHost_->unstable_recreateSchedulerDelegateForTesting(); +} + std::vector NativeFantom::takeMountingManagerLogs( jsi::Runtime& /*runtime*/, SurfaceId surfaceId) { diff --git a/private/react-native-fantom/tester/src/NativeFantom.h b/private/react-native-fantom/tester/src/NativeFantom.h index 2c0a19a20fb7..b36a7bcc97f1 100644 --- a/private/react-native-fantom/tester/src/NativeFantom.h +++ b/private/react-native-fantom/tester/src/NativeFantom.h @@ -93,6 +93,8 @@ class NativeFantom : public NativeFantomCxxSpec { void flushEventQueue(jsi::Runtime &runtime); void validateEmptyMessageQueue(jsi::Runtime &runtime); + void unstable_recreateSchedulerDelegate(jsi::Runtime &runtime); + std::vector takeMountingManagerLogs(jsi::Runtime &runtime, SurfaceId surfaceId); std::string getRenderedOutput(