Move ShadowNode::getAncestors to ShadowNodeFamily

Summary:
Changelog: [internal]

1. Moves `ShadowNode::getAncestors` to `ShadowNodeFamily`.
2. Exposes shadowNode's family through `ShadowNode::getFamily()`.

# Why?
This is a first step in order to merge `StateCoordinator` into `ShadowNodeFamily` and use it as target for state updates.

Reviewed By: shergin

Differential Revision: D19465188

fbshipit-source-id: b5a3625aa21c040301259de02beedbf97e11f20e
This commit is contained in:
Samuel Susla
2020-01-28 09:32:52 -08:00
committed by Facebook Github Bot
parent 8259a9d369
commit 01805636b9
12 changed files with 176 additions and 73 deletions
@@ -47,10 +47,10 @@ RootShadowNode::Unshared RootShadowNode::clone(
}
RootShadowNode::Unshared RootShadowNode::clone(
ShadowNode const &shadowNode,
ShadowNodeFamily const &shadowNodeFamily,
std::function<ShadowNode::Unshared(ShadowNode const &oldShadowNode)>
callback) const {
auto ancestors = shadowNode.getAncestors(*this);
auto ancestors = shadowNodeFamily.getAncestors(*this);
if (ancestors.size() == 0) {
return RootShadowNode::Unshared{nullptr};
@@ -59,7 +59,6 @@ RootShadowNode::Unshared RootShadowNode::clone(
auto &parent = ancestors.back();
auto &oldShadowNode = parent.first.get().getChildren().at(parent.second);
assert(ShadowNode::sameFamily(shadowNode, *oldShadowNode));
auto newShadowNode = callback(*oldShadowNode);
auto childNode = newShadowNode;
@@ -55,7 +55,7 @@ class RootShadowNode final
* Returns `nullptr` if the operation cannot be performed successfully.
*/
RootShadowNode::Unshared clone(
ShadowNode const &shadowNode,
ShadowNodeFamily const &shadowNodeFamily,
std::function<ShadowNode::Unshared(ShadowNode const &oldShadowNode)>
callback) const;
@@ -17,7 +17,6 @@
#include <react/core/ShadowNodeFragment.h>
#include <react/core/State.h>
#include <react/core/StateCoordinator.h>
#include <react/core/StateUpdate.h>
namespace facebook {
namespace react {
@@ -99,7 +99,7 @@ LayoutMetrics LayoutableShadowNode::getRelativeLayoutMetrics(
return layoutMetrics;
}
auto ancestors = shadowNode.getAncestors(ancestorShadowNode);
auto ancestors = shadowNode.getFamily().getAncestors(ancestorShadowNode);
if (ancestors.size() == 0) {
return EmptyLayoutMetrics;
@@ -17,8 +17,6 @@
namespace facebook {
namespace react {
using AncestorList = ShadowNode::AncestorList;
SharedShadowNodeSharedList ShadowNode::emptySharedShadowNodeSharedList() {
static const auto emptySharedShadowNodeSharedList =
std::make_shared<SharedShadowNodeList>();
@@ -215,44 +213,8 @@ void ShadowNode::setMounted(bool mounted) const {
family_->eventEmitter_->setEnabled(mounted);
}
AncestorList ShadowNode::getAncestors(
ShadowNode const &ancestorShadowNode) const {
auto families = better::small_vector<ShadowNodeFamily const *, 64>{};
auto ancestorFamily = ancestorShadowNode.family_.get();
auto family = family_.get();
while (family && family != ancestorFamily) {
families.push_back(family);
family = family->parent_.lock().get();
}
if (family != ancestorFamily) {
return {};
}
auto ancestors = AncestorList{};
auto parentNode = &ancestorShadowNode;
for (auto it = families.rbegin(); it != families.rend(); it++) {
auto childFamily = *it;
auto found = false;
auto childIndex = 0;
for (const auto &childNode : *parentNode->children_) {
if (childNode->family_.get() == childFamily) {
ancestors.push_back({*parentNode, childIndex});
parentNode = childNode.get();
found = true;
break;
}
childIndex++;
}
if (!found) {
ancestors.clear();
return ancestors;
}
}
return ancestors;
ShadowNodeFamily const &ShadowNode::getFamily() const {
return *family_;
}
#pragma mark - DebugStringConvertible
@@ -28,7 +28,6 @@ static constexpr const int kShadowNodeChildrenSmallVectorSize = 8;
class ComponentDescriptor;
struct ShadowNodeFragment;
class ShadowNode;
using SharedShadowNode = std::shared_ptr<const ShadowNode>;
@@ -127,6 +126,8 @@ class ShadowNode : public virtual Sealable,
void sealRecursive() const;
ShadowNodeFamily const &getFamily() const;
#pragma mark - Mutating Methods
void appendChild(ShadowNode::Shared const &child);
@@ -142,17 +143,6 @@ class ShadowNode : public virtual Sealable,
*/
void setMounted(bool mounted) const;
/*
* Returns a list of all ancestors of the node relative to the given ancestor.
* The list starts from the given ancestor node and ends with the parent node
* of `this` node. The elements of the list have a reference to some parent
* node and an index of the child of the parent node.
* Returns an empty array if there is no ancestor-descendant relationship.
* Can be called from any thread.
* The theoretical complexity of the algorithm is `O(ln(n))`. Use it wisely.
*/
AncestorList getAncestors(ShadowNode const &ancestorShadowNode) const;
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
@@ -175,6 +165,7 @@ class ShadowNode : public virtual Sealable,
State::Shared state_;
private:
friend ShadowNodeFamily;
/*
* Clones the list of children (and creates a new `shared_ptr` to it) if
* `childrenAreShared_` flag is `true`.
@@ -6,12 +6,15 @@
*/
#include "ShadowNodeFamily.h"
#include "ShadowNode.h"
#include <react/core/ComponentDescriptor.h>
namespace facebook {
namespace react {
using AncestorList = ShadowNode::AncestorList;
ShadowNodeFamily::ShadowNodeFamily(
ShadowNodeFamilyFragment const &fragment,
ComponentDescriptor const &componentDescriptor)
@@ -40,5 +43,45 @@ ComponentName ShadowNodeFamily::getComponentName() const {
return componentName_;
}
AncestorList ShadowNodeFamily::getAncestors(
ShadowNode const &ancestorShadowNode) const {
auto families = better::small_vector<ShadowNodeFamily const *, 64>{};
auto ancestorFamily = ancestorShadowNode.family_.get();
auto family = this;
while (family && family != ancestorFamily) {
families.push_back(family);
family = family->parent_.lock().get();
}
if (family != ancestorFamily) {
return {};
}
auto ancestors = AncestorList{};
auto parentNode = &ancestorShadowNode;
for (auto it = families.rbegin(); it != families.rend(); it++) {
auto childFamily = *it;
auto found = false;
auto childIndex = 0;
for (const auto &childNode : *parentNode->children_) {
if (childNode->family_.get() == childFamily) {
ancestors.push_back({*parentNode, childIndex});
parentNode = childNode.get();
found = true;
break;
}
childIndex++;
}
if (!found) {
ancestors.clear();
return ancestors;
}
}
return ancestors;
}
} // namespace react
} // namespace facebook
@@ -17,6 +17,7 @@ namespace facebook {
namespace react {
class ComponentDescriptor;
class ShadowNode;
/*
* Represents all things that shadow nodes from the same family have in common.
@@ -27,6 +28,12 @@ class ShadowNodeFamily {
using Shared = std::shared_ptr<ShadowNodeFamily const>;
using Weak = std::weak_ptr<ShadowNodeFamily const>;
using AncestorList = better::small_vector<
std::pair<
std::reference_wrapper<ShadowNode const> /* parentNode */,
int /* childIndex */>,
64>;
ShadowNodeFamily(
ShadowNodeFamilyFragment const &fragment,
ComponentDescriptor const &componentDescriptor);
@@ -44,6 +51,17 @@ class ShadowNodeFamily {
ComponentHandle getComponentHandle() const;
ComponentName getComponentName() const;
/*
* Returns a list of all ancestors of the node relative to the given ancestor.
* The list starts from the given ancestor node and ends with the parent node
* of `this` node. The elements of the list have a reference to some parent
* node and an index of the child of the parent node.
* Returns an empty array if there is no ancestor-descendant relationship.
* Can be called from any thread.
* The theoretical complexity of the algorithm is `O(ln(n))`. Use it wisely.
*/
AncestorList getAncestors(ShadowNode const &ancestorShadowNode) const;
private:
friend ShadowNode;
-1
View File
@@ -12,7 +12,6 @@
#include <react/core/ShadowNodeFragment.h>
#include <react/core/State.h>
#include <react/core/StateTarget.h>
#include <react/core/StateUpdate.h>
#ifdef ANDROID
#include <folly/dynamic.h>
@@ -0,0 +1,104 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <exception>
#include <gtest/gtest.h>
#include "TestComponent.h"
using namespace facebook::react;
TEST(ShadowNodeFamilyTest, sealObjectCorrectly) {
/*
* The structure:
* <A>
* <AA>
* <AAA/>
* </AA>
* </A>
*/
SurfaceId surfaceId = 1;
auto eventDispatcher = std::shared_ptr<EventDispatcher const>();
auto componentDescriptor = TestComponentDescriptor({eventDispatcher});
auto props = std::make_shared<const TestProps>();
auto familyAAA = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 12,
/* .surfaceId = */ surfaceId,
/* .eventEmitter = */ nullptr,
},
componentDescriptor);
auto nodeAAA = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyAAA,
ShadowNodeTraits{});
auto nodeAAChildren =
std::make_shared<SharedShadowNodeList>(SharedShadowNodeList{nodeAAA});
auto familyAA = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 11,
/* .surfaceId = */ surfaceId,
/* .eventEmitter = */ nullptr,
},
componentDescriptor);
auto nodeAA = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ nodeAAChildren,
},
familyAA,
ShadowNodeTraits{});
auto nodeAChildren =
std::make_shared<SharedShadowNodeList>(SharedShadowNodeList{nodeAA});
auto familyA = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 17,
/* .surfaceId = */ surfaceId,
/* .eventEmitter = */ nullptr,
},
componentDescriptor);
auto nodeA = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ nodeAChildren,
},
familyA,
ShadowNodeTraits{});
auto familyZ = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 18,
/* .surfaceId = */ surfaceId,
/* .eventEmitter = */ nullptr,
},
componentDescriptor);
auto nodeZ = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyZ,
ShadowNodeTraits{});
// Negative case:
auto ancestors1 = nodeZ->getFamily().getAncestors(*nodeA);
EXPECT_EQ(ancestors1.size(), 0);
// Positive case:
auto ancestors2 = nodeAAA->getFamily().getAncestors(*nodeA);
EXPECT_EQ(ancestors2.size(), 2);
EXPECT_EQ(&ancestors2[0].first.get(), nodeA.get());
EXPECT_EQ(&ancestors2[1].first.get(), nodeAA.get());
}
@@ -222,18 +222,6 @@ TEST_F(ShadowNodeTest, handleCloneFunction) {
EXPECT_EQ(nodeAB_->getProps(), nodeABClone->getProps());
}
TEST_F(ShadowNodeTest, handleBacktracking) {
// Negative case:
auto ancestors1 = nodeZ_->getAncestors(*nodeA_);
EXPECT_EQ(ancestors1.size(), 0);
// Positive case:
auto ancestors2 = nodeABB_->getAncestors(*nodeA_);
EXPECT_EQ(ancestors2.size(), 2);
EXPECT_EQ(&ancestors2[0].first.get(), nodeA_.get());
EXPECT_EQ(&ancestors2[1].first.get(), nodeAB_.get());
}
TEST_F(ShadowNodeTest, handleState) {
auto family = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
+2 -2
View File
@@ -153,7 +153,7 @@ void UIManager::setNativeProps(
shadowTree.tryCommit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
return oldRootShadowNode->clone(
shadowNode, [&](ShadowNode const &oldShadowNode) {
shadowNode.getFamily(), [&](ShadowNode const &oldShadowNode) {
return oldShadowNode.clone({
/* .props = */ props,
});
@@ -200,7 +200,7 @@ void UIManager::updateState(
shadowTree.tryCommit([&](RootShadowNode::Shared const
&oldRootShadowNode) {
return oldRootShadowNode->clone(
shadowNode, [&](ShadowNode const &oldShadowNode) {
shadowNode.getFamily(), [&](ShadowNode const &oldShadowNode) {
auto &componentDescriptor =
oldShadowNode.getComponentDescriptor();
auto state = componentDescriptor.createState(