Backport useShadowNodeStateOnClone and updateRuntimeShadowNodeReferencesOnCommit (and more)

* Add updateRuntimeShadowNodeReferencesOnCommit and useShadowNodeStateOnClone feature flags

* Use source shadow node state on clone

* Enable useShadowNodeStateOnClone by default on OSS

* Split shadow node reference setter and update functionality

* Move shadow node reference updates to tree commit

* Enable updateRuntimeShadowNodeReferencesOnCommit by default for OSS

---------

Co-authored-by: Nick Lefever <lefever@meta.com>
This commit is contained in:
Nick Lefever
2025-05-01 14:53:38 +01:00
committed by GitHub
co-authored by Nick Lefever
parent 994ab4a26e
commit 22a4e060d5
25 changed files with 302 additions and 34 deletions
@@ -314,6 +314,14 @@ class RCTAppDelegateBridgelessFeatureFlags : public ReactNativeFeatureFlagsDefau
{
return true;
}
bool updateRuntimeShadowNodeReferencesOnCommit() override
{
return true;
}
bool useShadowNodeStateOnClone() override
{
return true;
}
};
- (void)_setUpFeatureFlags
@@ -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<<5e27f8a6852293234595697743315ff8>>
* @generated SignedSource<<b263bdcbc1258f7d5c56e69732ba9076>>
*/
/**
@@ -238,6 +238,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean = accessor.traceTurboModulePromiseRejectionsOnAndroid()
/**
* When enabled, runtime shadow node references will be updated during the commit. This allows running RSNRU from any thread without corrupting the renderer state.
*/
@JvmStatic
public fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean = accessor.updateRuntimeShadowNodeReferencesOnCommit()
/**
* In Bridgeless mode, use the always available javascript error reporting pipeline.
*/
@@ -274,6 +280,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun useRawPropsJsiValue(): Boolean = accessor.useRawPropsJsiValue()
/**
* Use the state stored on the source shadow node when cloning it instead of reading in the most recent state on the shadow node family.
*/
@JvmStatic
public fun useShadowNodeStateOnClone(): Boolean = accessor.useShadowNodeStateOnClone()
/**
* In Bridgeless mode, should legacy NativeModules use the TurboModule system?
*/
@@ -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<<b59cecccbe734a126dcd20f76d0ce7c7>>
* @generated SignedSource<<0496ecf3d1e5d8a2e6d4d594aca806d0>>
*/
/**
@@ -55,12 +55,14 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var removeTurboModuleManagerDelegateMutexCache: Boolean? = null
private var throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOSCache: Boolean? = null
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
private var useEditTextStockAndroidFocusBehaviorCache: Boolean? = null
private var useFabricInteropCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
private var useRawPropsJsiValueCache: Boolean? = null
private var useShadowNodeStateOnCloneCache: Boolean? = null
private var useTurboModuleInteropCache: Boolean? = null
private var useTurboModulesCache: Boolean? = null
@@ -379,6 +381,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean {
var cached = updateRuntimeShadowNodeReferencesOnCommitCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.updateRuntimeShadowNodeReferencesOnCommit()
updateRuntimeShadowNodeReferencesOnCommitCache = cached
}
return cached
}
override fun useAlwaysAvailableJSErrorHandling(): Boolean {
var cached = useAlwaysAvailableJSErrorHandlingCache
if (cached == null) {
@@ -433,6 +444,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun useShadowNodeStateOnClone(): Boolean {
var cached = useShadowNodeStateOnCloneCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.useShadowNodeStateOnClone()
useShadowNodeStateOnCloneCache = cached
}
return cached
}
override fun useTurboModuleInterop(): Boolean {
var cached = useTurboModuleInteropCache
if (cached == null) {
@@ -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<<08b51adfc7c09f0f26f2b3c8351cda01>>
* @generated SignedSource<<c4f3b0cee8b9b4b9cebb589801e1dd15>>
*/
/**
@@ -98,6 +98,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean
@DoNotStrip @JvmStatic public external fun useAlwaysAvailableJSErrorHandling(): Boolean
@DoNotStrip @JvmStatic public external fun useEditTextStockAndroidFocusBehavior(): Boolean
@@ -110,6 +112,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun useRawPropsJsiValue(): Boolean
@DoNotStrip @JvmStatic public external fun useShadowNodeStateOnClone(): Boolean
@DoNotStrip @JvmStatic public external fun useTurboModuleInterop(): Boolean
@DoNotStrip @JvmStatic public external fun useTurboModules(): Boolean
@@ -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<<0041e37961e68474a6d092dc0f8a4903>>
* @generated SignedSource<<a8900217ae0385947b619c8fa0834942>>
*/
/**
@@ -93,6 +93,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean = false
override fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean = false
override fun useAlwaysAvailableJSErrorHandling(): Boolean = false
override fun useEditTextStockAndroidFocusBehavior(): Boolean = true
@@ -105,6 +107,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun useRawPropsJsiValue(): Boolean = false
override fun useShadowNodeStateOnClone(): Boolean = false
override fun useTurboModuleInterop(): Boolean = false
override fun useTurboModules(): Boolean = false
@@ -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<<e5c1e722f33148fe3aa36ccab62e2293>>
* @generated SignedSource<<8f5180a0ef154c083ac38d28e650ee11>>
*/
/**
@@ -59,12 +59,14 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var removeTurboModuleManagerDelegateMutexCache: Boolean? = null
private var throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOSCache: Boolean? = null
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
private var useEditTextStockAndroidFocusBehaviorCache: Boolean? = null
private var useFabricInteropCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
private var useRawPropsJsiValueCache: Boolean? = null
private var useShadowNodeStateOnCloneCache: Boolean? = null
private var useTurboModuleInteropCache: Boolean? = null
private var useTurboModulesCache: Boolean? = null
@@ -418,6 +420,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean {
var cached = updateRuntimeShadowNodeReferencesOnCommitCache
if (cached == null) {
cached = currentProvider.updateRuntimeShadowNodeReferencesOnCommit()
accessedFeatureFlags.add("updateRuntimeShadowNodeReferencesOnCommit")
updateRuntimeShadowNodeReferencesOnCommitCache = cached
}
return cached
}
override fun useAlwaysAvailableJSErrorHandling(): Boolean {
var cached = useAlwaysAvailableJSErrorHandlingCache
if (cached == null) {
@@ -478,6 +490,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun useShadowNodeStateOnClone(): Boolean {
var cached = useShadowNodeStateOnCloneCache
if (cached == null) {
cached = currentProvider.useShadowNodeStateOnClone()
accessedFeatureFlags.add("useShadowNodeStateOnClone")
useShadowNodeStateOnCloneCache = cached
}
return cached
}
override fun useTurboModuleInterop(): Boolean {
var cached = useTurboModuleInteropCache
if (cached == null) {
@@ -17,4 +17,8 @@ public class ReactNativeFeatureFlagsOverrides_RNOSS_Stable_Android(
override fun enableFabricRenderer(): Boolean = bridgelessEnabled || fabricEnabled
override fun useTurboModules(): Boolean = bridgelessEnabled || turboModulesEnabled
override fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean = true
override fun useShadowNodeStateOnClone(): Boolean = true
}
@@ -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<<9a223b4f79e4612eb193036256bfebc5>>
* @generated SignedSource<<33571f99b1f78fbc62cecfca5f8351fa>>
*/
/**
@@ -93,6 +93,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean
@DoNotStrip public fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean
@DoNotStrip public fun useAlwaysAvailableJSErrorHandling(): Boolean
@DoNotStrip public fun useEditTextStockAndroidFocusBehavior(): Boolean
@@ -105,6 +107,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun useRawPropsJsiValue(): Boolean
@DoNotStrip public fun useShadowNodeStateOnClone(): Boolean
@DoNotStrip public fun useTurboModuleInterop(): Boolean
@DoNotStrip public fun useTurboModules(): Boolean
@@ -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<<91c1a65790aa5946a354ab8a1966a5f4>>
* @generated SignedSource<<83b23039ed9fff5109ff3b532648baac>>
*/
/**
@@ -249,6 +249,12 @@ class ReactNativeFeatureFlagsProviderHolder
return method(javaProvider_);
}
bool updateRuntimeShadowNodeReferencesOnCommit() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("updateRuntimeShadowNodeReferencesOnCommit");
return method(javaProvider_);
}
bool useAlwaysAvailableJSErrorHandling() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useAlwaysAvailableJSErrorHandling");
@@ -285,6 +291,12 @@ class ReactNativeFeatureFlagsProviderHolder
return method(javaProvider_);
}
bool useShadowNodeStateOnClone() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useShadowNodeStateOnClone");
return method(javaProvider_);
}
bool useTurboModuleInterop() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useTurboModuleInterop");
@@ -476,6 +488,11 @@ bool JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndr
return ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid();
}
bool JReactNativeFeatureFlagsCxxInterop::updateRuntimeShadowNodeReferencesOnCommit(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit();
}
bool JReactNativeFeatureFlagsCxxInterop::useAlwaysAvailableJSErrorHandling(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling();
@@ -506,6 +523,11 @@ bool JReactNativeFeatureFlagsCxxInterop::useRawPropsJsiValue(
return ReactNativeFeatureFlags::useRawPropsJsiValue();
}
bool JReactNativeFeatureFlagsCxxInterop::useShadowNodeStateOnClone(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useShadowNodeStateOnClone();
}
bool JReactNativeFeatureFlagsCxxInterop::useTurboModuleInterop(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useTurboModuleInterop();
@@ -652,6 +674,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"traceTurboModulePromiseRejectionsOnAndroid",
JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndroid),
makeNativeMethod(
"updateRuntimeShadowNodeReferencesOnCommit",
JReactNativeFeatureFlagsCxxInterop::updateRuntimeShadowNodeReferencesOnCommit),
makeNativeMethod(
"useAlwaysAvailableJSErrorHandling",
JReactNativeFeatureFlagsCxxInterop::useAlwaysAvailableJSErrorHandling),
@@ -670,6 +695,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"useRawPropsJsiValue",
JReactNativeFeatureFlagsCxxInterop::useRawPropsJsiValue),
makeNativeMethod(
"useShadowNodeStateOnClone",
JReactNativeFeatureFlagsCxxInterop::useShadowNodeStateOnClone),
makeNativeMethod(
"useTurboModuleInterop",
JReactNativeFeatureFlagsCxxInterop::useTurboModuleInterop),
@@ -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<<610104213a5eba23b797e27377d71b52>>
* @generated SignedSource<<efb0288fd19fb35e4582522c835301b4>>
*/
/**
@@ -135,6 +135,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool traceTurboModulePromiseRejectionsOnAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool updateRuntimeShadowNodeReferencesOnCommit(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool useAlwaysAvailableJSErrorHandling(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -153,6 +156,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool useRawPropsJsiValue(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool useShadowNodeStateOnClone(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool useTurboModuleInterop(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -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<<3d4eefc7af1e9d594ade8339c261ca01>>
* @generated SignedSource<<661a4193f9ba7af0c963bc13751deb15>>
*/
/**
@@ -166,6 +166,10 @@ bool ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid() {
return getAccessor().traceTurboModulePromiseRejectionsOnAndroid();
}
bool ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit() {
return getAccessor().updateRuntimeShadowNodeReferencesOnCommit();
}
bool ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling() {
return getAccessor().useAlwaysAvailableJSErrorHandling();
}
@@ -190,6 +194,10 @@ bool ReactNativeFeatureFlags::useRawPropsJsiValue() {
return getAccessor().useRawPropsJsiValue();
}
bool ReactNativeFeatureFlags::useShadowNodeStateOnClone() {
return getAccessor().useShadowNodeStateOnClone();
}
bool ReactNativeFeatureFlags::useTurboModuleInterop() {
return getAccessor().useTurboModuleInterop();
}
@@ -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<<55a526204b386d34058c86183d5b97ac>>
* @generated SignedSource<<53e965423f2957b37e0470c607cc45a4>>
*/
/**
@@ -214,6 +214,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool traceTurboModulePromiseRejectionsOnAndroid();
/**
* When enabled, runtime shadow node references will be updated during the commit. This allows running RSNRU from any thread without corrupting the renderer state.
*/
RN_EXPORT static bool updateRuntimeShadowNodeReferencesOnCommit();
/**
* In Bridgeless mode, use the always available javascript error reporting pipeline.
*/
@@ -244,6 +249,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool useRawPropsJsiValue();
/**
* Use the state stored on the source shadow node when cloning it instead of reading in the most recent state on the shadow node family.
*/
RN_EXPORT static bool useShadowNodeStateOnClone();
/**
* In Bridgeless mode, should legacy NativeModules use the TurboModule system?
*/
@@ -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<<c30a4dea1d32e2838f6e5db6442a3f78>>
* @generated SignedSource<<5ff3ac57eb17d9b6642d98f2fc35bd9d>>
*/
/**
@@ -659,6 +659,24 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit() {
auto flagValue = updateRuntimeShadowNodeReferencesOnCommit_.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(35, "updateRuntimeShadowNodeReferencesOnCommit");
flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit();
updateRuntimeShadowNodeReferencesOnCommit_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() {
auto flagValue = useAlwaysAvailableJSErrorHandling_.load();
@@ -668,7 +686,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(35, "useAlwaysAvailableJSErrorHandling");
markFlagAsAccessed(36, "useAlwaysAvailableJSErrorHandling");
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
useAlwaysAvailableJSErrorHandling_ = flagValue;
@@ -686,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::useEditTextStockAndroidFocusBehavior() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(36, "useEditTextStockAndroidFocusBehavior");
markFlagAsAccessed(37, "useEditTextStockAndroidFocusBehavior");
flagValue = currentProvider_->useEditTextStockAndroidFocusBehavior();
useEditTextStockAndroidFocusBehavior_ = flagValue;
@@ -704,7 +722,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(37, "useFabricInterop");
markFlagAsAccessed(38, "useFabricInterop");
flagValue = currentProvider_->useFabricInterop();
useFabricInterop_ = flagValue;
@@ -722,7 +740,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(38, "useNativeViewConfigsInBridgelessMode");
markFlagAsAccessed(39, "useNativeViewConfigsInBridgelessMode");
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
@@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(39, "useOptimizedEventBatchingOnAndroid");
markFlagAsAccessed(40, "useOptimizedEventBatchingOnAndroid");
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
useOptimizedEventBatchingOnAndroid_ = flagValue;
@@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(40, "useRawPropsJsiValue");
markFlagAsAccessed(41, "useRawPropsJsiValue");
flagValue = currentProvider_->useRawPropsJsiValue();
useRawPropsJsiValue_ = flagValue;
@@ -767,6 +785,24 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() {
auto flagValue = useShadowNodeStateOnClone_.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(42, "useShadowNodeStateOnClone");
flagValue = currentProvider_->useShadowNodeStateOnClone();
useShadowNodeStateOnClone_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() {
auto flagValue = useTurboModuleInterop_.load();
@@ -776,7 +812,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(41, "useTurboModuleInterop");
markFlagAsAccessed(43, "useTurboModuleInterop");
flagValue = currentProvider_->useTurboModuleInterop();
useTurboModuleInterop_ = flagValue;
@@ -794,7 +830,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(42, "useTurboModules");
markFlagAsAccessed(44, "useTurboModules");
flagValue = currentProvider_->useTurboModules();
useTurboModules_ = flagValue;
@@ -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<<95aa4a8b8859f26b901112df1517b1f1>>
* @generated SignedSource<<267385c8df88a5d1eb7f1e2478209d39>>
*/
/**
@@ -67,12 +67,14 @@ class ReactNativeFeatureFlagsAccessor {
bool removeTurboModuleManagerDelegateMutex();
bool throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS();
bool traceTurboModulePromiseRejectionsOnAndroid();
bool updateRuntimeShadowNodeReferencesOnCommit();
bool useAlwaysAvailableJSErrorHandling();
bool useEditTextStockAndroidFocusBehavior();
bool useFabricInterop();
bool useNativeViewConfigsInBridgelessMode();
bool useOptimizedEventBatchingOnAndroid();
bool useRawPropsJsiValue();
bool useShadowNodeStateOnClone();
bool useTurboModuleInterop();
bool useTurboModules();
@@ -86,7 +88,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
bool wasOverridden_;
std::array<std::atomic<const char*>, 43> accessedFeatureFlags_;
std::array<std::atomic<const char*>, 45> accessedFeatureFlags_;
std::atomic<std::optional<bool>> commonTestFlag_;
std::atomic<std::optional<bool>> disableMountItemReorderingAndroid_;
@@ -123,12 +125,14 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> removeTurboModuleManagerDelegateMutex_;
std::atomic<std::optional<bool>> throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS_;
std::atomic<std::optional<bool>> traceTurboModulePromiseRejectionsOnAndroid_;
std::atomic<std::optional<bool>> updateRuntimeShadowNodeReferencesOnCommit_;
std::atomic<std::optional<bool>> useAlwaysAvailableJSErrorHandling_;
std::atomic<std::optional<bool>> useEditTextStockAndroidFocusBehavior_;
std::atomic<std::optional<bool>> useFabricInterop_;
std::atomic<std::optional<bool>> useNativeViewConfigsInBridgelessMode_;
std::atomic<std::optional<bool>> useOptimizedEventBatchingOnAndroid_;
std::atomic<std::optional<bool>> useRawPropsJsiValue_;
std::atomic<std::optional<bool>> useShadowNodeStateOnClone_;
std::atomic<std::optional<bool>> useTurboModuleInterop_;
std::atomic<std::optional<bool>> useTurboModules_;
};
@@ -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<<e80434127379886a001ffcbace6ba619>>
* @generated SignedSource<<48bb4ea50c42676aee161ea1594a6aec>>
*/
/**
@@ -167,6 +167,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool updateRuntimeShadowNodeReferencesOnCommit() override {
return false;
}
bool useAlwaysAvailableJSErrorHandling() override {
return false;
}
@@ -191,6 +195,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool useShadowNodeStateOnClone() override {
return false;
}
bool useTurboModuleInterop() override {
return false;
}
@@ -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<<21aeeeaa1a402f91b616180114887a58>>
* @generated SignedSource<<e92353bf22b82344bc6ab97f08ca724c>>
*/
/**
@@ -360,6 +360,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::traceTurboModulePromiseRejectionsOnAndroid();
}
bool updateRuntimeShadowNodeReferencesOnCommit() override {
auto value = values_["updateRuntimeShadowNodeReferencesOnCommit"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::updateRuntimeShadowNodeReferencesOnCommit();
}
bool useAlwaysAvailableJSErrorHandling() override {
auto value = values_["useAlwaysAvailableJSErrorHandling"];
if (!value.isNull()) {
@@ -414,6 +423,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::useRawPropsJsiValue();
}
bool useShadowNodeStateOnClone() override {
auto value = values_["useShadowNodeStateOnClone"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::useShadowNodeStateOnClone();
}
bool useTurboModuleInterop() override {
auto value = values_["useTurboModuleInterop"];
if (!value.isNull()) {
@@ -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<<b4ba05d9dee950f242db8f3a830a75da>>
* @generated SignedSource<<d8f7773c6f24c715a6c0e0ab703677fd>>
*/
/**
@@ -60,12 +60,14 @@ class ReactNativeFeatureFlagsProvider {
virtual bool removeTurboModuleManagerDelegateMutex() = 0;
virtual bool throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS() = 0;
virtual bool traceTurboModulePromiseRejectionsOnAndroid() = 0;
virtual bool updateRuntimeShadowNodeReferencesOnCommit() = 0;
virtual bool useAlwaysAvailableJSErrorHandling() = 0;
virtual bool useEditTextStockAndroidFocusBehavior() = 0;
virtual bool useFabricInterop() = 0;
virtual bool useNativeViewConfigsInBridgelessMode() = 0;
virtual bool useOptimizedEventBatchingOnAndroid() = 0;
virtual bool useRawPropsJsiValue() = 0;
virtual bool useShadowNodeStateOnClone() = 0;
virtual bool useTurboModuleInterop() = 0;
virtual bool useTurboModules() = 0;
};
@@ -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<<651dc228bd4959a90c82de905a8b6c40>>
* @generated SignedSource<<76f1f689940817c1df6ace2f8dd062e7>>
*/
/**
@@ -219,6 +219,11 @@ bool NativeReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid(
return ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid();
}
bool NativeReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit();
}
bool NativeReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling();
@@ -249,6 +254,11 @@ bool NativeReactNativeFeatureFlags::useRawPropsJsiValue(
return ReactNativeFeatureFlags::useRawPropsJsiValue();
}
bool NativeReactNativeFeatureFlags::useShadowNodeStateOnClone(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::useShadowNodeStateOnClone();
}
bool NativeReactNativeFeatureFlags::useTurboModuleInterop(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::useTurboModuleInterop();
@@ -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<<1a082675338773d1a6259fabfd2f652f>>
* @generated SignedSource<<af8c15575ff94feff9dadaaf8becdf18>>
*/
/**
@@ -107,6 +107,8 @@ class NativeReactNativeFeatureFlags
bool traceTurboModulePromiseRejectionsOnAndroid(jsi::Runtime& runtime);
bool updateRuntimeShadowNodeReferencesOnCommit(jsi::Runtime& runtime);
bool useAlwaysAvailableJSErrorHandling(jsi::Runtime& runtime);
bool useEditTextStockAndroidFocusBehavior(jsi::Runtime& runtime);
@@ -119,6 +121,8 @@ class NativeReactNativeFeatureFlags
bool useRawPropsJsiValue(jsi::Runtime& runtime);
bool useShadowNodeStateOnClone(jsi::Runtime& runtime);
bool useTurboModuleInterop(jsi::Runtime& runtime);
bool useTurboModules(jsi::Runtime& runtime);
@@ -112,7 +112,9 @@ ShadowNode::ShadowNode(
fragment.children ? fragment.children : sourceShadowNode.children_),
state_(
fragment.state ? fragment.state
: sourceShadowNode.getMostRecentState()),
: (ReactNativeFeatureFlags::useShadowNodeStateOnClone()
? sourceShadowNode.state_
: sourceShadowNode.getMostRecentState())),
orderIndex_(sourceShadowNode.orderIndex_),
family_(sourceShadowNode.family_),
traits_(sourceShadowNode.traits_) {
@@ -308,20 +310,28 @@ void ShadowNode::setRuntimeShadowNodeReference(
runtimeShadowNodeReference_ = runtimeShadowNodeReference;
}
void ShadowNode::transferRuntimeShadowNodeReference(
void ShadowNode::updateRuntimeShadowNodeReference(
const Shared& destinationShadowNode) const {
destinationShadowNode->runtimeShadowNodeReference_ =
runtimeShadowNodeReference_;
if (auto reference = runtimeShadowNodeReference_.lock()) {
reference->shadowNode = destinationShadowNode;
}
}
void ShadowNode::transferRuntimeShadowNodeReference(
const Shared& destinationShadowNode) const {
destinationShadowNode->runtimeShadowNodeReference_ =
runtimeShadowNodeReference_;
if (!ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit()) {
updateRuntimeShadowNodeReference(destinationShadowNode);
}
}
void ShadowNode::transferRuntimeShadowNodeReference(
const Shared& destinationShadowNode,
const ShadowNodeFragment& fragment) const {
if (useRuntimeShadowNodeReferenceUpdateOnThread &&
if ((ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit() ||
useRuntimeShadowNodeReferenceUpdateOnThread) &&
fragment.runtimeShadowNodeReference) {
transferRuntimeShadowNodeReference(destinationShadowNode);
}
@@ -187,6 +187,12 @@ class ShadowNode : public Sealable,
void setRuntimeShadowNodeReference(const std::shared_ptr<ShadowNodeWrapper>&
runtimeShadowNodeReference) const;
/*
* Update the runtime reference to point to the provided shadow node.
*/
void updateRuntimeShadowNodeReference(
const Shared& destinationShadowNode) const;
/*
* Transfer the runtime reference to this `ShadowNode` to a new instance,
* updating the reference to point to the new `ShadowNode` referencing it.
@@ -7,6 +7,8 @@
#include "updateMountedFlag.h"
#include <react/featureflags/ReactNativeFeatureFlags.h>
namespace facebook::react {
void updateMountedFlag(
const ShadowNode::ListOfShared& oldChildren,
@@ -47,6 +49,10 @@ void updateMountedFlag(
newChild->setMounted(true);
oldChild->setMounted(false);
if (ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit()) {
newChild->updateRuntimeShadowNodeReference(newChild);
}
updateMountedFlag(oldChild->getChildren(), newChild->getChildren());
}
@@ -419,6 +419,17 @@ const definitions: FeatureFlagDefinitions = {
},
ossReleaseStage: 'none',
},
updateRuntimeShadowNodeReferencesOnCommit: {
defaultValue: false,
metadata: {
dateAdded: '2025-04-15',
description:
'When enabled, runtime shadow node references will be updated during the commit. This allows running RSNRU from any thread without corrupting the renderer state.',
expectedReleaseValue: true,
purpose: 'experimentation',
},
ossReleaseStage: 'none',
},
useAlwaysAvailableJSErrorHandling: {
defaultValue: false,
metadata: {
@@ -482,6 +493,17 @@ const definitions: FeatureFlagDefinitions = {
},
ossReleaseStage: 'none',
},
useShadowNodeStateOnClone: {
defaultValue: false,
metadata: {
dateAdded: '2025-04-16',
description:
'Use the state stored on the source shadow node when cloning it instead of reading in the most recent state on the shadow node family.',
expectedReleaseValue: true,
purpose: 'experimentation',
},
ossReleaseStage: 'none',
},
useTurboModuleInterop: {
defaultValue: false,
metadata: {
@@ -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<<3e68d888bf4d8ac71b63571a639d265b>>
* @generated SignedSource<<5b1d3ba6712de7a927eeeb8602ee2a8f>>
* @flow strict
*/
@@ -84,12 +84,14 @@ export type ReactNativeFeatureFlags = $ReadOnly<{
removeTurboModuleManagerDelegateMutex: Getter<boolean>,
throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS: Getter<boolean>,
traceTurboModulePromiseRejectionsOnAndroid: Getter<boolean>,
updateRuntimeShadowNodeReferencesOnCommit: Getter<boolean>,
useAlwaysAvailableJSErrorHandling: Getter<boolean>,
useEditTextStockAndroidFocusBehavior: Getter<boolean>,
useFabricInterop: Getter<boolean>,
useNativeViewConfigsInBridgelessMode: Getter<boolean>,
useOptimizedEventBatchingOnAndroid: Getter<boolean>,
useRawPropsJsiValue: Getter<boolean>,
useShadowNodeStateOnClone: Getter<boolean>,
useTurboModuleInterop: Getter<boolean>,
useTurboModules: Getter<boolean>,
}>;
@@ -313,6 +315,10 @@ export const throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIO
* Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause.
*/
export const traceTurboModulePromiseRejectionsOnAndroid: Getter<boolean> = createNativeFlagGetter('traceTurboModulePromiseRejectionsOnAndroid', false);
/**
* When enabled, runtime shadow node references will be updated during the commit. This allows running RSNRU from any thread without corrupting the renderer state.
*/
export const updateRuntimeShadowNodeReferencesOnCommit: Getter<boolean> = createNativeFlagGetter('updateRuntimeShadowNodeReferencesOnCommit', false);
/**
* In Bridgeless mode, use the always available javascript error reporting pipeline.
*/
@@ -337,6 +343,10 @@ export const useOptimizedEventBatchingOnAndroid: Getter<boolean> = createNativeF
* Instead of using folly::dynamic as internal representation in RawProps and RawValue, use jsi::Value
*/
export const useRawPropsJsiValue: Getter<boolean> = createNativeFlagGetter('useRawPropsJsiValue', false);
/**
* Use the state stored on the source shadow node when cloning it instead of reading in the most recent state on the shadow node family.
*/
export const useShadowNodeStateOnClone: Getter<boolean> = createNativeFlagGetter('useShadowNodeStateOnClone', false);
/**
* In Bridgeless mode, should legacy NativeModules use the TurboModule system?
*/
@@ -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<<0d611c17d522627c59a68d083eb6b6dc>>
* @generated SignedSource<<f8da5261357616749d6319190ed0139d>>
* @flow strict
*/
@@ -59,12 +59,14 @@ export interface Spec extends TurboModule {
+removeTurboModuleManagerDelegateMutex?: () => boolean;
+throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS?: () => boolean;
+traceTurboModulePromiseRejectionsOnAndroid?: () => boolean;
+updateRuntimeShadowNodeReferencesOnCommit?: () => boolean;
+useAlwaysAvailableJSErrorHandling?: () => boolean;
+useEditTextStockAndroidFocusBehavior?: () => boolean;
+useFabricInterop?: () => boolean;
+useNativeViewConfigsInBridgelessMode?: () => boolean;
+useOptimizedEventBatchingOnAndroid?: () => boolean;
+useRawPropsJsiValue?: () => boolean;
+useShadowNodeStateOnClone?: () => boolean;
+useTurboModuleInterop?: () => boolean;
+useTurboModules?: () => boolean;
}