Fabric: ShadowNode::backtrackAncestors(...)

Summary:
We have to have a way to backtrack a pointer to a parent node and this is generalized version of that.
This is the first naive implementations of the algorithm. We will invest in optimizing this later.

Reviewed By: mdvacca

Differential Revision: D12920856

fbshipit-source-id: 9facb4e16a0b5608feb6747df3804952025ef093
This commit is contained in:
Valentin Shergin 2018-11-21 17:10:29 -08:00 committed by Facebook Github Bot
parent 1f32b5d1da
commit 3ecf4eaccb
3 changed files with 109 additions and 0 deletions

View File

@ -146,6 +146,25 @@ void ShadowNode::cloneChildrenIfShared() {
children_ = std::make_shared<SharedShadowNodeList>(*children_);
}
bool ShadowNode::constructAncestorPath(
const ShadowNode &ancestorShadowNode,
std::vector<std::reference_wrapper<const ShadowNode>> &ancestors) const {
// Note: We have a decent idea of how to make it reasonable performant.
// This is not implemented yet though. See T36620537 for more details.
if (this == &ancestorShadowNode) {
return true;
}
for (const auto &childShadowNode : *ancestorShadowNode.children_) {
if (constructAncestorPath(*childShadowNode, ancestors)) {
ancestors.push_back(std::ref(ancestorShadowNode));
return true;
}
}
return false;
}
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE

View File

@ -101,6 +101,21 @@ class ShadowNode : public virtual Sealable,
*/
void setLocalData(const SharedLocalData &localData);
/*
* Forms a list of all ancestors of the node relative to the given ancestor.
* The list starts from the parent node and ends with the given ancestor node.
* Returns `true` if successful, `false` otherwise.
* Thread-safe if the subtree is immutable.
* The theoretical complexity of this algorithm is `O(n)`. Use it wisely.
* The particular implementation can use some tricks to mitigate the
* complexity problem up to `0(ln(n))` but this is not guaranteed.
* Particular consumers should use appropriate cache techniques based on
* `childIndex` and `nodeId` tracking.
*/
bool constructAncestorPath(
const ShadowNode &rootShadowNode,
std::vector<std::reference_wrapper<const ShadowNode>> &ancestors) const;
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE

View File

@ -202,3 +202,78 @@ TEST(ShadowNodeTest, handleLocalData) {
secondNode->sealRecursive();
ASSERT_ANY_THROW(secondNode->setLocalData(localDataOver9000));
}
TEST(ShadowNodeTest, handleBacktracking) {
/*
* The structure:
* <A>
* <AA/>
* <AB>
* <ABA/>
* <ABB/>
* <ABC/>
* </AB>
* <AC/>
* </A>
*/
auto props = std::make_shared<const TestProps>();
auto nodeAA = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
.props = props,
.children = ShadowNode::emptySharedShadowNodeSharedList()},
nullptr);
auto nodeABA = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
.props = props,
.children = ShadowNode::emptySharedShadowNodeSharedList()},
nullptr);
auto nodeABB = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
.props = props,
.children = ShadowNode::emptySharedShadowNodeSharedList()},
nullptr);
auto nodeABC = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
.props = props,
.children = ShadowNode::emptySharedShadowNodeSharedList()},
nullptr);
auto nodeABChildren = std::make_shared<std::vector<SharedShadowNode>>(
std::vector<SharedShadowNode>{nodeABA, nodeABB, nodeABC});
auto nodeAB = std::make_shared<TestShadowNode>(
ShadowNodeFragment{.props = props, .children = nodeABChildren}, nullptr);
auto nodeAC = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
.props = props,
.children = ShadowNode::emptySharedShadowNodeSharedList()},
nullptr);
auto nodeAChildren = std::make_shared<std::vector<SharedShadowNode>>(
std::vector<SharedShadowNode>{nodeAA, nodeAB, nodeAC});
auto nodeA = std::make_shared<TestShadowNode>(
ShadowNodeFragment{.props = props, .children = nodeAChildren}, nullptr);
auto nodeZ = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
.props = props,
.children = ShadowNode::emptySharedShadowNodeSharedList()},
nullptr);
std::vector<std::reference_wrapper<const ShadowNode>> ancestors = {};
// Negative case:
auto success = nodeZ->constructAncestorPath(*nodeA, ancestors);
ASSERT_FALSE(success);
ASSERT_EQ(ancestors.size(), 0);
// Positive case:
success = nodeABC->constructAncestorPath(*nodeA, ancestors);
ASSERT_TRUE(success);
ASSERT_EQ(ancestors.size(), 2);
ASSERT_EQ(&ancestors[0].get(), nodeAB.get());
ASSERT_EQ(&ancestors[1].get(), nodeA.get());
}