delete state alignment mechanism (#46658)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46658

changelog: [internal]

We are shipping a better solution: Runtime Shadow Node Reference Syncing which is being rolled out.

Reviewed By: lenaic, rubennorte

Differential Revision: D63456344

fbshipit-source-id: e70b7dd4be7bf0670366e5200a910195b929a14d
This commit is contained in:
Samuel Susla
2024-09-30 04:59:49 -07:00
committed by Facebook GitHub Bot
parent d1f47f7ad5
commit 4e38493d71
28 changed files with 71 additions and 458 deletions
@@ -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<<70e68165b567734a2609b77656b2c183>>
* @generated SignedSource<<346573dc832f9de6a2e29a4cfde558c5>>
*/
/**
@@ -328,12 +328,6 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun useRuntimeShadowNodeReferenceUpdateOnLayout(): Boolean = accessor.useRuntimeShadowNodeReferenceUpdateOnLayout()
/**
* When enabled, it uses optimised state reconciliation algorithm.
*/
@JvmStatic
public fun useStateAlignmentMechanism(): Boolean = accessor.useStateAlignmentMechanism()
/**
* 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<<2f8da8585f1ac040bd26953263b08392>>
* @generated SignedSource<<71fd4a070f4eb1429f38221e34135062>>
*/
/**
@@ -70,7 +70,6 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
private var useRuntimeShadowNodeReferenceUpdateCache: Boolean? = null
private var useRuntimeShadowNodeReferenceUpdateOnLayoutCache: Boolean? = null
private var useStateAlignmentMechanismCache: Boolean? = null
private var useTurboModuleInteropCache: Boolean? = null
private var useTurboModulesCache: Boolean? = null
@@ -524,15 +523,6 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
return cached
}
override fun useStateAlignmentMechanism(): Boolean {
var cached = useStateAlignmentMechanismCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.useStateAlignmentMechanism()
useStateAlignmentMechanismCache = 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<<4055cd2fa9070549fba3eda675e8d6f6>>
* @generated SignedSource<<c35eeb0695671be65b1543ea0664468b>>
*/
/**
@@ -128,8 +128,6 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun useRuntimeShadowNodeReferenceUpdateOnLayout(): Boolean
@DoNotStrip @JvmStatic public external fun useStateAlignmentMechanism(): 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<<340a2b27d0a0281746e3f4b02be5e1bb>>
* @generated SignedSource<<e1352f772c20f824f636f1f73a541444>>
*/
/**
@@ -123,8 +123,6 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun useRuntimeShadowNodeReferenceUpdateOnLayout(): Boolean = false
override fun useStateAlignmentMechanism(): 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<<bb25a0d771f0f129ab3f224640053ac3>>
* @generated SignedSource<<0d8f65a06ed20f44e5de58b764635602>>
*/
/**
@@ -74,7 +74,6 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
private var useRuntimeShadowNodeReferenceUpdateCache: Boolean? = null
private var useRuntimeShadowNodeReferenceUpdateOnLayoutCache: Boolean? = null
private var useStateAlignmentMechanismCache: Boolean? = null
private var useTurboModuleInteropCache: Boolean? = null
private var useTurboModulesCache: Boolean? = null
@@ -578,16 +577,6 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun useStateAlignmentMechanism(): Boolean {
var cached = useStateAlignmentMechanismCache
if (cached == null) {
cached = currentProvider.useStateAlignmentMechanism()
accessedFeatureFlags.add("useStateAlignmentMechanism")
useStateAlignmentMechanismCache = 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<<509b61021c01bf47c54724bc9ba81c9c>>
* @generated SignedSource<<6d70976da58a1120c79704210899b893>>
*/
/**
@@ -123,8 +123,6 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun useRuntimeShadowNodeReferenceUpdateOnLayout(): Boolean
@DoNotStrip public fun useStateAlignmentMechanism(): 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<<8b39b99e91976d5bf63956e6501e180f>>
* @generated SignedSource<<1f027e4e66bce1536fb259a547bb6a00>>
*/
/**
@@ -339,12 +339,6 @@ class ReactNativeFeatureFlagsProviderHolder
return method(javaProvider_);
}
bool useStateAlignmentMechanism() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useStateAlignmentMechanism");
return method(javaProvider_);
}
bool useTurboModuleInterop() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useTurboModuleInterop");
@@ -611,11 +605,6 @@ bool JReactNativeFeatureFlagsCxxInterop::useRuntimeShadowNodeReferenceUpdateOnLa
return ReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdateOnLayout();
}
bool JReactNativeFeatureFlagsCxxInterop::useStateAlignmentMechanism(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useStateAlignmentMechanism();
}
bool JReactNativeFeatureFlagsCxxInterop::useTurboModuleInterop(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useTurboModuleInterop();
@@ -793,9 +782,6 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"useRuntimeShadowNodeReferenceUpdateOnLayout",
JReactNativeFeatureFlagsCxxInterop::useRuntimeShadowNodeReferenceUpdateOnLayout),
makeNativeMethod(
"useStateAlignmentMechanism",
JReactNativeFeatureFlagsCxxInterop::useStateAlignmentMechanism),
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<<896ca4611f7fa5de92279fbbf2095e3e>>
* @generated SignedSource<<b99b1f3a29b875f4c456f77a276f4db3>>
*/
/**
@@ -180,9 +180,6 @@ class JReactNativeFeatureFlagsCxxInterop
static bool useRuntimeShadowNodeReferenceUpdateOnLayout(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool useStateAlignmentMechanism(
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<<6e2b92ee7e4fb51de5286792004f649d>>
* @generated SignedSource<<4efee607ab3716723debee8a451604ef>>
*/
/**
@@ -221,10 +221,6 @@ bool ReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdateOnLayout() {
return getAccessor().useRuntimeShadowNodeReferenceUpdateOnLayout();
}
bool ReactNativeFeatureFlags::useStateAlignmentMechanism() {
return getAccessor().useStateAlignmentMechanism();
}
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<<646023269f07d6ae792b8aad5ef2d3c1>>
* @generated SignedSource<<d2afeedcfb81ca31bd7f0c19fd406ae6>>
*/
/**
@@ -287,11 +287,6 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool useRuntimeShadowNodeReferenceUpdateOnLayout();
/**
* When enabled, it uses optimised state reconciliation algorithm.
*/
RN_EXPORT static bool useStateAlignmentMechanism();
/**
* 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<<e35262c26447dec6e71e20f6a4fd8984>>
* @generated SignedSource<<1bbb59bff4ce77b34ee94d0a2095d71f>>
*/
/**
@@ -929,24 +929,6 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdateOnLayou
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useStateAlignmentMechanism() {
auto flagValue = useStateAlignmentMechanism_.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(50, "useStateAlignmentMechanism");
flagValue = currentProvider_->useStateAlignmentMechanism();
useStateAlignmentMechanism_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() {
auto flagValue = useTurboModuleInterop_.load();
@@ -956,7 +938,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(51, "useTurboModuleInterop");
markFlagAsAccessed(50, "useTurboModuleInterop");
flagValue = currentProvider_->useTurboModuleInterop();
useTurboModuleInterop_ = flagValue;
@@ -974,7 +956,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(52, "useTurboModules");
markFlagAsAccessed(51, "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<<261b4e8fae45ad2780f0bd2b17c86d5c>>
* @generated SignedSource<<5c64457d2fbe154d7e57b3e3e2e6a33a>>
*/
/**
@@ -81,7 +81,6 @@ class ReactNativeFeatureFlagsAccessor {
bool useOptimizedEventBatchingOnAndroid();
bool useRuntimeShadowNodeReferenceUpdate();
bool useRuntimeShadowNodeReferenceUpdateOnLayout();
bool useStateAlignmentMechanism();
bool useTurboModuleInterop();
bool useTurboModules();
@@ -94,7 +93,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
bool wasOverridden_;
std::array<std::atomic<const char*>, 53> accessedFeatureFlags_;
std::array<std::atomic<const char*>, 52> accessedFeatureFlags_;
std::atomic<std::optional<bool>> commonTestFlag_;
std::atomic<std::optional<bool>> allowRecursiveCommitsWithSynchronousMountOnAndroid_;
@@ -146,7 +145,6 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> useOptimizedEventBatchingOnAndroid_;
std::atomic<std::optional<bool>> useRuntimeShadowNodeReferenceUpdate_;
std::atomic<std::optional<bool>> useRuntimeShadowNodeReferenceUpdateOnLayout_;
std::atomic<std::optional<bool>> useStateAlignmentMechanism_;
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<<c08358e66009eff69107a684681feba9>>
* @generated SignedSource<<001d0b39664b190385442ca8661a08a8>>
*/
/**
@@ -227,10 +227,6 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool useStateAlignmentMechanism() 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<<eca1f4ea2447e754c5cfa8998c2e7903>>
* @generated SignedSource<<ddd6b6977c8ee0e40d710947b4a23c3c>>
*/
/**
@@ -75,7 +75,6 @@ class ReactNativeFeatureFlagsProvider {
virtual bool useOptimizedEventBatchingOnAndroid() = 0;
virtual bool useRuntimeShadowNodeReferenceUpdate() = 0;
virtual bool useRuntimeShadowNodeReferenceUpdateOnLayout() = 0;
virtual bool useStateAlignmentMechanism() = 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<<cfb1dc3fb447f3e4b014533a757e6c23>>
* @generated SignedSource<<74bbe88f1a61cca088a504bb2a1bc19c>>
*/
/**
@@ -287,11 +287,6 @@ bool NativeReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdateOnLayout(
return ReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdateOnLayout();
}
bool NativeReactNativeFeatureFlags::useStateAlignmentMechanism(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::useStateAlignmentMechanism();
}
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<<3f8b503d5402563b0eb94fe46ffa2814>>
* @generated SignedSource<<447e801fe9b49d2413d8bfbc106019a0>>
*/
/**
@@ -135,8 +135,6 @@ class NativeReactNativeFeatureFlags
bool useRuntimeShadowNodeReferenceUpdateOnLayout(jsi::Runtime& runtime);
bool useStateAlignmentMechanism(jsi::Runtime& runtime);
bool useTurboModuleInterop(jsi::Runtime& runtime);
bool useTurboModules(jsi::Runtime& runtime);
@@ -78,7 +78,6 @@ ShadowNode::ShadowNode(
react_native_assert(children_);
traits_.set(ShadowNodeTraits::Trait::ChildrenAreShared);
traits_.set(fragment.traits.get());
for (const auto& child : *children_) {
child->family_->setParent(family_);
@@ -110,9 +109,7 @@ ShadowNode::ShadowNode(
// State could have been progressed above by checking
// `sourceShadowNode.getMostRecentState()`.
traits_.unset(ShadowNodeTraits::Trait::ClonedByNativeStateUpdate);
traits_.set(ShadowNodeTraits::Trait::ChildrenAreShared);
traits_.set(fragment.traits.get());
if (fragment.children) {
for (const auto& child : *children_) {
@@ -136,8 +133,7 @@ ShadowNode::Unshared ShadowNode::clone(
*this,
{.props = props,
.children = fragment.children,
.state = fragment.state,
.traits = fragment.traits});
.state = fragment.state});
return clonedNode;
} else {
// TODO: We might need to merge fragment.priops with
@@ -293,23 +289,6 @@ bool ShadowNode::getHasBeenPromoted() const {
return hasBeenMounted_.load();
}
bool ShadowNode::progressStateIfNecessary() {
auto hasBeenPromoted = hasBeenMounted_.load();
if (!hasBeenPromoted && state_) {
ensureUnsealed();
auto mostRecentState = family_->getMostRecentStateIfObsolete(*state_);
if (mostRecentState) {
state_ = mostRecentState;
const auto& componentDescriptor = family_->componentDescriptor_;
// Must call ComponentDescriptor::adopt to trigger any side effect
// state may have. E.g. adjusting padding.
componentDescriptor.adopt(*this);
return true;
}
}
return false;
}
void ShadowNode::setRuntimeShadowNodeReference(
const std::shared_ptr<ShadowNodeWrapper>& runtimeShadowNodeReference)
const {
@@ -342,8 +321,7 @@ const ShadowNodeFamily& ShadowNode::getFamily() const {
ShadowNode::Unshared ShadowNode::cloneTree(
const ShadowNodeFamily& shadowNodeFamily,
const std::function<ShadowNode::Unshared(const ShadowNode& oldShadowNode)>&
callback,
ShadowNodeTraits traits) const {
callback) const {
auto ancestors = shadowNodeFamily.getAncestors(*this);
if (ancestors.empty()) {
@@ -371,8 +349,7 @@ ShadowNode::Unshared ShadowNode::cloneTree(
children[childIndex] = childNode;
childNode = parentNode.clone(
{.children = std::make_shared<ShadowNode::ListOfShared>(children),
.traits = traits});
{.children = std::make_shared<ShadowNode::ListOfShared>(children)});
}
return std::const_pointer_cast<ShadowNode>(childNode);
@@ -102,8 +102,8 @@ class ShadowNode : public Sealable,
*/
Unshared cloneTree(
const ShadowNodeFamily& shadowNodeFamily,
const std::function<Unshared(const ShadowNode& oldShadowNode)>& callback,
ShadowNodeTraits traits = {}) const;
const std::function<Unshared(const ShadowNode& oldShadowNode)>& callback)
const;
#pragma mark - Getters
@@ -178,17 +178,6 @@ class ShadowNode : public Sealable,
*/
bool getHasBeenPromoted() const;
/*
* Applies the most recent state to the ShadowNode if following conditions are
* met:
* - ShadowNode has a state.
* - ShadowNode has not been mounted before.
* - ShadowNode's current state is obsolete.
*
* Returns true if the state was applied, false otherwise.
*/
bool progressStateIfNecessary();
/*
* Bind the runtime reference to this `ShadowNode` with a weak pointer,
* allowing to update the reference to this `ShadowNode` when cloned.
@@ -26,7 +26,6 @@ struct ShadowNodeFragment {
const Props::Shared& props = propsPlaceholder();
const ShadowNode::SharedListOfShared& children = childrenPlaceholder();
const State::Shared& state = statePlaceholder();
const ShadowNodeTraits traits = {};
const bool runtimeShadowNodeReference = true;
/*
@@ -70,14 +70,11 @@ class ShadowNodeTraits {
// to be cloned before the first mutation.
ChildrenAreShared = 1 << 8,
// Indicates that the node was cloned because of native state update.
ClonedByNativeStateUpdate = 1 << 9,
// Indicates that direct children of the node should not be collapsed
ChildrenFormStackingContext = 1 << 10,
ChildrenFormStackingContext = 1 << 9,
// Inherits `YogaLayoutableShadowNode` and has a custom baseline function.
BaselineYogaNode = 1 << 11,
BaselineYogaNode = 1 << 10,
};
/*
@@ -244,26 +244,6 @@ TEST_P(ShadowNodeTest, handleCloneFunction) {
EXPECT_EQ(nodeAB_->getProps(), nodeABClone->getProps());
}
TEST_P(ShadowNodeTest, handleCloningWithTraits) {
auto clonedWithoutTraits = nodeAB_->clone({});
EXPECT_FALSE(clonedWithoutTraits->getTraits().check(
ShadowNodeTraits::Trait::ClonedByNativeStateUpdate));
auto newTraits = ShadowNodeTraits();
newTraits.set(ShadowNodeTraits::Trait::ClonedByNativeStateUpdate);
auto clonedWithTraits = clonedWithoutTraits->clone({.traits = newTraits});
EXPECT_TRUE(clonedWithTraits->getTraits().check(
ShadowNodeTraits::Trait::ClonedByNativeStateUpdate));
auto clonedAgain = clonedWithTraits->clone({});
EXPECT_FALSE(clonedAgain->getTraits().check(
ShadowNodeTraits::Trait::ClonedByNativeStateUpdate));
}
TEST_P(ShadowNodeTest, handleState) {
auto family = componentDescriptor_.createFamily(ShadowNodeFamilyFragment{
/* .tag = */ 9,
@@ -319,37 +299,6 @@ TEST_P(ShadowNodeTest, handleState) {
"Attempt to mutate a sealed object.");
}
TEST_P(ShadowNodeTest, testCloneTree) {
auto& family = nodeABA_->getFamily();
auto newTraits = ShadowNodeTraits();
newTraits.set(ShadowNodeTraits::Trait::ClonedByNativeStateUpdate);
auto rootNode = nodeA_->cloneTree(
family,
[newTraits](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.traits = newTraits});
},
newTraits);
EXPECT_TRUE(rootNode->getTraits().check(
ShadowNodeTraits::Trait::ClonedByNativeStateUpdate));
EXPECT_FALSE(rootNode->getChildren()[0]->getTraits().check(
ShadowNodeTraits::Trait::ClonedByNativeStateUpdate));
const auto& firstLevelChild = *rootNode->getChildren()[1];
EXPECT_TRUE(firstLevelChild.getTraits().check(
ShadowNodeTraits::Trait::ClonedByNativeStateUpdate));
EXPECT_FALSE(firstLevelChild.getChildren()[1]->getTraits().check(
ShadowNodeTraits::Trait::ClonedByNativeStateUpdate));
const auto& secondLevelchild = *firstLevelChild.getChildren()[0];
EXPECT_TRUE(secondLevelchild.getTraits().check(
ShadowNodeTraits::Trait::ClonedByNativeStateUpdate));
}
TEST_P(ShadowNodeTest, handleRuntimeReferenceTransferOnClone) {
auto nodeABRev1 = nodeAB_->clone({});
auto wrappedShadowNode = std::make_shared<ShadowNodeWrapper>(nodeABRev1);
@@ -298,6 +298,7 @@ ShadowViewNodePair::NonOwningList sliceChildShadowNodeViewPairs(
}
size_t startOfStaticIndex = 0;
sliceChildShadowNodeViewPairsRecursively(
pairList, startOfStaticIndex, scope, layoutOffset, shadowNode);
@@ -26,115 +26,6 @@ namespace facebook::react {
using CommitStatus = ShadowTree::CommitStatus;
using CommitMode = ShadowTree::CommitMode;
// --- State Alignment Mechanism algorithm ---
// Note: Ideally, we don't have to const_cast but our use of constness in
// C++ is overly restrictive. We do const_cast here but the only place where
// we change ShadowNode is by calling `ShadowNode::progressStateIfNecessary`
// where checks are in place to avoid manipulating a sealed ShadowNode.
static void progressStateIfNecessary(
ShadowNode& newShadowNode,
const ShadowNode& baseShadowNode);
/*
* Looks at the new parent, new child and base child node to determine how to
* reconcile the state.
*
* Only to be called when baseChildNode has trait `ClonedByNativeStateUpdate`.
*/
static void progressStateIfNecessary(
ShadowNode& newShadowNode,
const ShadowNode& newChildNode,
const ShadowNode& baseChildNode,
size_t suggestedIndex) {
auto& shadowNode = const_cast<ShadowNode&>(newChildNode);
if (shadowNode.progressStateIfNecessary()) {
// State was progressed without the need to clone.
// We are done with this node, but need to keep traversing.
progressStateIfNecessary(shadowNode, baseChildNode);
} else if (newChildNode.getHasBeenPromoted()) {
// `newShadowNode` was cloned from react and cloned from a native state
// update. This child node was cloned only from a native state update.
// This is branching and it is safe to promote the new branch from
// native state update.
auto clonedChildNode = baseChildNode.clone({});
newShadowNode.replaceChild(newChildNode, clonedChildNode, suggestedIndex);
} else {
// `newShadowNode` was cloned from react and cloned from a native state
// update. This child node was cloned also by react.
// we can't reason about this on this layer and need to keep traversing.
progressStateIfNecessary(shadowNode, baseChildNode);
}
}
static void progressStateIfNecessary(
ShadowNode& newShadowNode,
const ShadowNode& baseShadowNode) {
auto& newChildren = newShadowNode.getChildren();
auto& baseChildren = baseShadowNode.getChildren();
auto newChildrenSize = newChildren.size();
auto baseChildrenSize = baseChildren.size();
auto index = size_t{0};
for (index = 0; index < newChildrenSize && index < baseChildrenSize;
++index) {
const auto& newChildNode = *newChildren[index];
const auto& baseChildNode = *baseChildren[index];
if (&newChildNode == &baseChildNode) {
// Nodes are identical. They are shared between `newShadowNode` and
// `baseShadowNode` and it is safe to skipping.
continue;
}
if (!ShadowNode::sameFamily(newChildNode, baseChildNode)) {
// React has changed the structure of the tree. We will realign the
// structure below.
break;
}
if (!baseChildNode.getTraits().check(
ShadowNodeTraits::Trait::ClonedByNativeStateUpdate)) {
// was not cloned with a new state, we can continue.
continue;
}
progressStateIfNecessary(newShadowNode, newChildNode, baseChildNode, index);
}
// === Realigning the tree ===
auto unprocessedBaseChildren = baseChildren.begin();
std::advance(unprocessedBaseChildren, index);
for (; index < newChildrenSize; ++index) {
const auto& newChildNode = *newChildren[index];
auto baseChildNodeIterator = std::find_if(
unprocessedBaseChildren,
baseChildren.end(),
[&newChildNode](auto baseChildNode) {
return ShadowNode::sameFamily(newChildNode, *baseChildNode);
});
if (baseChildNodeIterator == baseChildren.end()) {
// This must never happen and there is a mismatch between the two trees.
// No way of recover from this, let's just continue.
continue;
}
const auto& baseChildNode = *(*baseChildNodeIterator);
if (!baseChildNode.getTraits().check(
ShadowNodeTraits::Trait::ClonedByNativeStateUpdate)) {
// was not cloned with a new state, we can continue.
continue;
}
progressStateIfNecessary(newShadowNode, newChildNode, baseChildNode, index);
}
}
// --- End of State Alignment Mechanism algorithm ---
/*
* Generates (possibly) a new tree where all nodes with non-obsolete `State`
* objects. If all `State` objects in the tree are not obsolete for the moment
@@ -393,15 +284,11 @@ CommitStatus ShadowTree::tryCommit(
}
if (commitOptions.enableStateReconciliation) {
if (ReactNativeFeatureFlags::useStateAlignmentMechanism()) {
progressStateIfNecessary(*newRootShadowNode, *oldRootShadowNode);
} else {
auto updatedNewRootShadowNode =
progressState(*newRootShadowNode, *oldRootShadowNode);
if (updatedNewRootShadowNode) {
newRootShadowNode =
std::static_pointer_cast<RootShadowNode>(updatedNewRootShadowNode);
}
auto updatedNewRootShadowNode =
progressState(*newRootShadowNode, *oldRootShadowNode);
if (updatedNewRootShadowNode) {
newRootShadowNode =
std::static_pointer_cast<RootShadowNode>(updatedNewRootShadowNode);
}
}
@@ -24,20 +24,6 @@
using namespace facebook::react;
class StateReconciliationTestFeatureFlags
: public ReactNativeFeatureFlagsDefaults {
public:
explicit StateReconciliationTestFeatureFlags(bool useStateAlignmentMechanism)
: useStateAlignmentMechanism_(useStateAlignmentMechanism) {}
bool useStateAlignmentMechanism() override {
return useStateAlignmentMechanism_;
}
private:
bool useStateAlignmentMechanism_;
};
class DummyShadowTreeDelegate : public ShadowTreeDelegate {
public:
RootShadowNode::Unshared shadowTreeWillCommit(
@@ -82,20 +68,10 @@ class StateReconciliationTest : public ::testing::TestWithParam<bool> {
public:
StateReconciliationTest() : builder_(simpleComponentBuilder()) {}
void SetUp() override {
ReactNativeFeatureFlags::dangerouslyReset();
ReactNativeFeatureFlags::override(
std::make_unique<StateReconciliationTestFeatureFlags>(GetParam()));
}
void TearDown() override {
ReactNativeFeatureFlags::dangerouslyReset();
}
ComponentBuilder builder_;
};
TEST_P(StateReconciliationTest, testStateReconciliation) {
TEST_F(StateReconciliationTest, testStateReconciliation) {
// ==== SETUP ====
/*
@@ -158,15 +134,10 @@ TEST_P(StateReconciliationTest, testStateReconciliation) {
auto state2 = scrollViewComponentDescriptor.createState(
scrollViewFamily, std::make_shared<const ScrollViewState>());
auto newTraits = ShadowNodeTraits();
newTraits.set(ShadowNodeTraits::Trait::ClonedByNativeStateUpdate);
auto rootShadowNodeState2 = initialRootShadowNode->cloneTree(
scrollViewFamily,
[&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = state2, .traits = newTraits});
},
newTraits);
scrollViewFamily, [&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = state2});
});
EXPECT_EQ(
findDescendantNode(*initialRootShadowNode, scrollViewFamily)->getState(),
@@ -190,11 +161,9 @@ TEST_P(StateReconciliationTest, testStateReconciliation) {
scrollViewFamily, std::make_shared<const ScrollViewState>());
auto rootShadowNodeState3 = rootShadowNodeState2->cloneTree(
scrollViewFamily,
[&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = state3, .traits = newTraits});
},
newTraits);
scrollViewFamily, [&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = state3});
});
EXPECT_EQ(
findDescendantNode(*rootShadowNodeState3, scrollViewFamily)->getState(),
@@ -232,7 +201,7 @@ TEST_P(StateReconciliationTest, testStateReconciliation) {
state3->getRevision());
}
TEST_P(StateReconciliationTest, testCloneslessStateReconciliationDoesntClone) {
TEST_F(StateReconciliationTest, testCloneslessStateReconciliationDoesntClone) {
// ==== SETUP ====
/*
<Root>
@@ -286,15 +255,10 @@ TEST_P(StateReconciliationTest, testCloneslessStateReconciliationDoesntClone) {
auto state2 = scrollViewComponentDescriptor.createState(
scrollViewFamily, std::make_shared<const ScrollViewState>());
auto newTraits = ShadowNodeTraits();
newTraits.set(ShadowNodeTraits::Trait::ClonedByNativeStateUpdate);
auto rootShadowNode2 = rootShadowNode1->cloneTree(
scrollViewFamily,
[&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = state2, .traits = newTraits});
},
newTraits);
scrollViewFamily, [&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = state2});
});
EXPECT_EQ(
findDescendantNode(*rootShadowNode2, scrollViewFamily)->getState(),
@@ -324,11 +288,9 @@ TEST_P(StateReconciliationTest, testCloneslessStateReconciliationDoesntClone) {
scrollViewFamily, std::make_shared<const ScrollViewState>());
auto rootShadowNodeClonedFromStateUpdate = rootShadowNode2->cloneTree(
scrollViewFamily,
[&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = state3, .traits = newTraits});
},
newTraits);
scrollViewFamily, [&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = state3});
});
// ==== State update ====
@@ -351,15 +313,9 @@ TEST_P(StateReconciliationTest, testCloneslessStateReconciliationDoesntClone) {
auto scrollViewShadowNode = findDescendantNode(shadowTree, scrollViewFamily);
EXPECT_EQ(scrollViewShadowNode->getState(), state3);
if (GetParam()) {
// Checking that newlyClonedShadowNode was not cloned unnecessarly by state
// progression. This fails with the old algorithm.
EXPECT_EQ(scrollViewShadowNode, newlyClonedShadowNode.get());
}
}
TEST_P(StateReconciliationTest, testStateReconciliationScrollViewChildUpdate) {
TEST_F(StateReconciliationTest, testStateReconciliationScrollViewChildUpdate) {
// ==== SETUP ====
/*
<Root>
@@ -431,15 +387,10 @@ TEST_P(StateReconciliationTest, testStateReconciliationScrollViewChildUpdate) {
auto state2 = scrollViewComponentDescriptor.createState(
scrollViewFamily, std::make_shared<const ScrollViewState>());
auto newTraits = ShadowNodeTraits();
newTraits.set(ShadowNodeTraits::Trait::ClonedByNativeStateUpdate);
auto rootShadowNode2 = initialRootShadowNode->cloneTree(
scrollViewFamily,
[&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = state2, .traits = newTraits});
},
newTraits);
scrollViewFamily, [&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = state2});
});
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
@@ -464,7 +415,7 @@ TEST_P(StateReconciliationTest, testStateReconciliationScrollViewChildUpdate) {
newlyClonedViewShadowNode.get());
}
TEST_P(StateReconciliationTest, testScrollViewWithChildrenDeletion) {
TEST_F(StateReconciliationTest, testScrollViewWithChildrenDeletion) {
// ==== SETUP ====
/*
@@ -539,18 +490,13 @@ TEST_P(StateReconciliationTest, testScrollViewWithChildrenDeletion) {
// ==== State update ====
auto newTraits = ShadowNodeTraits();
newTraits.set(ShadowNodeTraits::Trait::ClonedByNativeStateUpdate);
auto newState = scrollViewComponentDescriptor.createState(
childBFamily, std::make_shared<const ScrollViewState>());
auto rootShadowNodeClonedFromStateUpdate = rootNode->cloneTree(
childBFamily,
[&newState, newTraits](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = newState, .traits = newTraits});
},
newTraits);
childBFamily, [&newState](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = newState});
});
shadowTree.commit(
[&rootShadowNodeClonedFromStateUpdate](
@@ -578,7 +524,7 @@ TEST_P(StateReconciliationTest, testScrollViewWithChildrenDeletion) {
newState);
}
TEST_P(StateReconciliationTest, testScrollViewWithComplexChildrenReorder) {
TEST_F(StateReconciliationTest, testScrollViewWithComplexChildrenReorder) {
// ==== SETUP ====
/*
@@ -660,18 +606,13 @@ TEST_P(StateReconciliationTest, testScrollViewWithComplexChildrenReorder) {
// ==== State update ====
auto newTraits = ShadowNodeTraits();
newTraits.set(ShadowNodeTraits::Trait::ClonedByNativeStateUpdate);
auto newState = scrollViewComponentDescriptor.createState(
childAFamily, std::make_shared<const ScrollViewState>());
auto rootShadowNodeClonedFromStateUpdate = rootNode->cloneTree(
childAFamily,
[&newState, newTraits](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = newState, .traits = newTraits});
},
newTraits);
childAFamily, [&newState](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = newState});
});
shadowTree.commit(
[&rootShadowNodeClonedFromStateUpdate](
@@ -697,7 +638,7 @@ TEST_P(StateReconciliationTest, testScrollViewWithComplexChildrenReorder) {
EXPECT_EQ(findDescendantNode(shadowTree, childAFamily)->getState(), newState);
}
TEST_P(StateReconciliationTest, testScrollViewWithChildrenReorder) {
TEST_F(StateReconciliationTest, testScrollViewWithChildrenReorder) {
// ==== SETUP ====
/*
@@ -766,18 +707,13 @@ TEST_P(StateReconciliationTest, testScrollViewWithChildrenReorder) {
// ==== State update ====
auto newTraits = ShadowNodeTraits();
newTraits.set(ShadowNodeTraits::Trait::ClonedByNativeStateUpdate);
auto newState = scrollViewComponentDescriptor.createState(
childAFamily, std::make_shared<const ScrollViewState>());
auto rootShadowNodeClonedFromStateUpdate = rootNode->cloneTree(
childAFamily,
[&newState, newTraits](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = newState, .traits = newTraits});
},
newTraits);
childAFamily, [&newState](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = newState});
});
shadowTree.commit(
[&rootShadowNodeClonedFromStateUpdate](
@@ -803,7 +739,7 @@ TEST_P(StateReconciliationTest, testScrollViewWithChildrenReorder) {
EXPECT_EQ(findDescendantNode(shadowTree, childAFamily)->getState(), newState);
}
TEST_P(StateReconciliationTest, testScrollViewWithChildrenAddition) {
TEST_F(StateReconciliationTest, testScrollViewWithChildrenAddition) {
// ==== SETUP ====
/*
@@ -856,18 +792,13 @@ TEST_P(StateReconciliationTest, testScrollViewWithChildrenAddition) {
// ==== State update ====
auto newTraits = ShadowNodeTraits();
newTraits.set(ShadowNodeTraits::Trait::ClonedByNativeStateUpdate);
auto newState = scrollViewComponentDescriptor.createState(
scrollViewFamily, std::make_shared<const ScrollViewState>());
auto rootShadowNodeClonedFromStateUpdate = rootNode->cloneTree(
scrollViewFamily,
[&newState, newTraits](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = newState, .traits = newTraits});
},
newTraits);
scrollViewFamily, [&newState](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone({.state = newState});
});
// ==== Tree with new child ====
@@ -922,8 +853,3 @@ TEST_P(StateReconciliationTest, testScrollViewWithChildrenAddition) {
findDescendantNode(shadowTree, childB->getFamily())->getState(),
newState);
}
INSTANTIATE_TEST_SUITE_P(
StateReconciliationTestInstantiation,
StateReconciliationTest,
testing::Values(false, true));
@@ -361,9 +361,6 @@ void UIManager::updateState(const StateUpdate& stateUpdate) const {
auto& callback = stateUpdate.callback;
auto& family = stateUpdate.family;
auto& componentDescriptor = family->getComponentDescriptor();
auto clonedByNativeStateTraits = ShadowNodeTraits();
clonedByNativeStateTraits.set(
ShadowNodeTraits::Trait::ClonedByNativeStateUpdate);
shadowTreeRegistry_.visit(
family->getSurfaceId(), [&](const ShadowTree& shadowTree) {
@@ -372,8 +369,7 @@ void UIManager::updateState(const StateUpdate& stateUpdate) const {
auto isValid = true;
auto rootNode = oldRootShadowNode.cloneTree(
*family,
[&](const ShadowNode& oldShadowNode) {
*family, [&](const ShadowNode& oldShadowNode) {
auto newData =
callback(oldShadowNode.getState()->getDataPointer());
@@ -389,10 +385,8 @@ void UIManager::updateState(const StateUpdate& stateUpdate) const {
return oldShadowNode.clone(
{.props = ShadowNodeFragment::propsPlaceholder(),
.children = ShadowNodeFragment::childrenPlaceholder(),
.state = newState,
.traits = clonedByNativeStateTraits});
},
clonedByNativeStateTraits);
.state = newState});
});
return isValid
? std::static_pointer_cast<RootShadowNode>(rootNode)
@@ -467,15 +467,6 @@ const definitions: FeatureFlagDefinitions = {
purpose: 'experimentation',
},
},
useStateAlignmentMechanism: {
defaultValue: false,
metadata: {
dateAdded: '2024-04-12',
description:
'When enabled, it uses optimised state reconciliation algorithm.',
purpose: 'experimentation',
},
},
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<<1bf690e95da1924a4cae50ec639a95df>>
* @generated SignedSource<<e161b67cb919ad7e2e67ddcb3c501adb>>
* @flow strict
*/
@@ -99,7 +99,6 @@ export type ReactNativeFeatureFlags = {
useOptimizedEventBatchingOnAndroid: Getter<boolean>,
useRuntimeShadowNodeReferenceUpdate: Getter<boolean>,
useRuntimeShadowNodeReferenceUpdateOnLayout: Getter<boolean>,
useStateAlignmentMechanism: Getter<boolean>,
useTurboModuleInterop: Getter<boolean>,
useTurboModules: Getter<boolean>,
}
@@ -384,10 +383,6 @@ export const useRuntimeShadowNodeReferenceUpdate: Getter<boolean> = createNative
* When enabled, cloning shadow nodes during layout will update the reference held by the current JS fiber tree.
*/
export const useRuntimeShadowNodeReferenceUpdateOnLayout: Getter<boolean> = createNativeFlagGetter('useRuntimeShadowNodeReferenceUpdateOnLayout', false);
/**
* When enabled, it uses optimised state reconciliation algorithm.
*/
export const useStateAlignmentMechanism: Getter<boolean> = createNativeFlagGetter('useStateAlignmentMechanism', 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<<de64240cb68a53f5dc2f97b7ec967eeb>>
* @generated SignedSource<<cd4a2a047e3a6fdb4205efb518906462>>
* @flow strict
*/
@@ -73,7 +73,6 @@ export interface Spec extends TurboModule {
+useOptimizedEventBatchingOnAndroid?: () => boolean;
+useRuntimeShadowNodeReferenceUpdate?: () => boolean;
+useRuntimeShadowNodeReferenceUpdateOnLayout?: () => boolean;
+useStateAlignmentMechanism?: () => boolean;
+useTurboModuleInterop?: () => boolean;
+useTurboModules?: () => boolean;
}