diff --git a/ReactCommon/fabric/mounting/BUCK b/ReactCommon/fabric/mounting/BUCK index 23cf786a2..268c698fb 100644 --- a/ReactCommon/fabric/mounting/BUCK +++ b/ReactCommon/fabric/mounting/BUCK @@ -27,6 +27,7 @@ rn_xplat_cxx_library( exported_headers = subdir_glob( [ ("", "*.h"), + ("stubs", "*.h"), ], prefix = "react/mounting", ), diff --git a/ReactCommon/fabric/mounting/ShadowTree.cpp b/ReactCommon/fabric/mounting/ShadowTree.cpp index 391471073..de95a18bf 100644 --- a/ReactCommon/fabric/mounting/ShadowTree.cpp +++ b/ReactCommon/fabric/mounting/ShadowTree.cpp @@ -5,6 +5,8 @@ #include "ShadowTree.h" +#include + #include #include #include @@ -194,6 +196,20 @@ bool ShadowTree::tryCommit( if (revision) { *revision = revision_; } + +#ifdef RN_SHADOW_TREE_INTROSPECTION + stubViewTree_.mutate(mutations); + auto stubViewTree = stubViewTreeFromShadowNode(*rootShadowNode_); + if (stubViewTree_ != stubViewTree) { + LOG(ERROR) << "Old tree:" + << "\n" + << oldRootShadowNode->getDebugDescription() << "\n"; + LOG(ERROR) << "New tree:" + << "\n" + << newRootShadowNode->getDebugDescription() << "\n"; + assert(false); + } +#endif } emitLayoutEvents(mutations); diff --git a/ReactCommon/fabric/mounting/ShadowTree.h b/ReactCommon/fabric/mounting/ShadowTree.h index 09ca34f35..5d5b83edb 100644 --- a/ReactCommon/fabric/mounting/ShadowTree.h +++ b/ReactCommon/fabric/mounting/ShadowTree.h @@ -5,6 +5,10 @@ #pragma once +#ifdef DEBUG +#define RN_SHADOW_TREE_INTROSPECTION +#endif + #include #include @@ -16,6 +20,10 @@ #include #include +#ifdef RN_SHADOW_TREE_INTROSPECTION +#include +#endif + namespace facebook { namespace react { @@ -87,6 +95,10 @@ class ShadowTree final { mutable SharedRootShadowNode rootShadowNode_; // Protected by `commitMutex_`. mutable int revision_{1}; // Protected by `commitMutex_`. ShadowTreeDelegate const *delegate_; + +#ifdef RN_SHADOW_TREE_INTROSPECTION + mutable StubViewTree stubViewTree_; // Protected by `commitMutex_`. +#endif }; } // namespace react diff --git a/ReactCommon/fabric/mounting/stubs/StubView.cpp b/ReactCommon/fabric/mounting/stubs/StubView.cpp new file mode 100644 index 000000000..5f0adaf01 --- /dev/null +++ b/ReactCommon/fabric/mounting/stubs/StubView.cpp @@ -0,0 +1,31 @@ +// 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 "StubView.h" + +namespace facebook { +namespace react { + +void StubView::update(ShadowView const &shadowView) { + componentName = shadowView.componentName; + componentHandle = shadowView.componentHandle; + tag = shadowView.tag; + props = shadowView.props; + eventEmitter = shadowView.eventEmitter; + layoutMetrics = shadowView.layoutMetrics; + state = shadowView.state; +} + +bool operator==(StubView const &lhs, StubView const &rhs) { + return std::tie(lhs.props, lhs.layoutMetrics) == + std::tie(rhs.props, rhs.layoutMetrics); +} + +bool operator!=(StubView const &lhs, StubView const &rhs) { + return !(lhs == rhs); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/mounting/stubs/StubView.h b/ReactCommon/fabric/mounting/stubs/StubView.h new file mode 100644 index 000000000..a69598251 --- /dev/null +++ b/ReactCommon/fabric/mounting/stubs/StubView.h @@ -0,0 +1,41 @@ +// 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. + +#pragma once + +#include +#include + +#include +#include +#include + +namespace facebook { +namespace react { + +class StubView final { + public: + using Shared = std::shared_ptr; + + StubView() = default; + StubView(StubView const &stubView) = default; + + void update(ShadowView const &shadowView); + + ComponentName componentName; + ComponentHandle componentHandle; + Tag tag; + SharedProps props; + SharedEventEmitter eventEmitter; + LayoutMetrics layoutMetrics; + State::Shared state; + std::vector children; +}; + +bool operator==(StubView const &lhs, StubView const &rhs); +bool operator!=(StubView const &lhs, StubView const &rhs); + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/mounting/stubs/StubViewTree.cpp b/ReactCommon/fabric/mounting/stubs/StubViewTree.cpp new file mode 100644 index 000000000..a6152c084 --- /dev/null +++ b/ReactCommon/fabric/mounting/stubs/StubViewTree.cpp @@ -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 "StubViewTree.h" + +namespace facebook { +namespace react { + +StubViewTree::StubViewTree(ShadowView const &shadowView) { + auto view = std::make_shared(); + view->update(shadowView); + registry[shadowView.tag] = view; +} + +void StubViewTree::mutate(ShadowViewMutationList const &mutations) { + for (auto const &mutation : mutations) { + switch (mutation.type) { + case ShadowViewMutation::Create: { + assert(mutation.parentShadowView == ShadowView{}); + assert(mutation.oldChildShadowView == ShadowView{}); + auto stubView = std::make_shared(); + auto tag = mutation.newChildShadowView.tag; + assert(registry.find(tag) == registry.end()); + registry[tag] = stubView; + break; + } + + case ShadowViewMutation::Delete: { + assert(mutation.parentShadowView == ShadowView{}); + assert(mutation.newChildShadowView == ShadowView{}); + auto tag = mutation.oldChildShadowView.tag; + assert(registry.find(tag) != registry.end()); + registry.erase(tag); + break; + } + + case ShadowViewMutation::Insert: { + assert(mutation.oldChildShadowView == ShadowView{}); + auto parentTag = mutation.parentShadowView.tag; + assert(registry.find(parentTag) != registry.end()); + auto parentStubView = registry[parentTag]; + auto childTag = mutation.newChildShadowView.tag; + assert(registry.find(childTag) != registry.end()); + auto childStubView = registry[childTag]; + childStubView->update(mutation.newChildShadowView); + parentStubView->children.insert( + parentStubView->children.begin() + mutation.index, childStubView); + break; + } + + case ShadowViewMutation::Remove: { + assert(mutation.newChildShadowView == ShadowView{}); + auto parentTag = mutation.parentShadowView.tag; + assert(registry.find(parentTag) != registry.end()); + auto parentStubView = registry[parentTag]; + auto childTag = mutation.oldChildShadowView.tag; + assert(registry.find(childTag) != registry.end()); + auto childStubView = registry[childTag]; + assert( + parentStubView->children[mutation.index]->tag == + childStubView->tag); + parentStubView->children.erase( + parentStubView->children.begin() + mutation.index); + break; + } + + case ShadowViewMutation::Update: { + assert( + mutation.newChildShadowView.tag == mutation.oldChildShadowView.tag); + assert( + registry.find(mutation.newChildShadowView.tag) != registry.end()); + auto stubView = registry[mutation.newChildShadowView.tag]; + stubView->update(mutation.newChildShadowView); + break; + } + } + } +} + +bool operator==(StubViewTree const &lhs, StubViewTree const &rhs) { + if (lhs.registry.size() != rhs.registry.size()) { + return false; + } + + for (auto const &pair : lhs.registry) { + auto &lhsStubView = *lhs.registry.at(pair.first); + auto &rhsStubView = *rhs.registry.at(pair.first); + + if (lhsStubView != rhsStubView) { + return false; + } + } + + return true; +} + +bool operator!=(StubViewTree const &lhs, StubViewTree const &rhs) { + return !(lhs == rhs); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/mounting/stubs/StubViewTree.h b/ReactCommon/fabric/mounting/stubs/StubViewTree.h new file mode 100644 index 000000000..59f112b89 --- /dev/null +++ b/ReactCommon/fabric/mounting/stubs/StubViewTree.h @@ -0,0 +1,31 @@ +// 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. + +#pragma once + +#include +#include + +#include +#include + +namespace facebook { +namespace react { + +class StubViewTree { + public: + StubViewTree() = default; + StubViewTree(ShadowView const &shadowView); + + void mutate(ShadowViewMutationList const &mutations); + + std::unordered_map registry{}; +}; + +bool operator==(StubViewTree const &lhs, StubViewTree const &rhs); +bool operator!=(StubViewTree const &lhs, StubViewTree const &rhs); + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/mounting/stubs/stubs.cpp b/ReactCommon/fabric/mounting/stubs/stubs.cpp new file mode 100644 index 000000000..48c609c32 --- /dev/null +++ b/ReactCommon/fabric/mounting/stubs/stubs.cpp @@ -0,0 +1,30 @@ +// 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 "stubs.h" + +#include +#include +#include + +namespace facebook { +namespace react { + +StubViewTree stubViewTreeFromShadowNode(ShadowNode const &rootShadowNode) { + auto emptyRootShadowNode = rootShadowNode.clone( + ShadowNodeFragment{ShadowNodeFragment::tagPlaceholder(), + ShadowNodeFragment::surfaceIdPlaceholder(), + ShadowNodeFragment::propsPlaceholder(), + ShadowNodeFragment::eventEmitterPlaceholder(), + ShadowNode::emptySharedShadowNodeSharedList()}); + + auto stubViewTree = StubViewTree(ShadowView(*emptyRootShadowNode)); + stubViewTree.mutate( + calculateShadowViewMutations(*emptyRootShadowNode, rootShadowNode)); + return stubViewTree; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/mounting/stubs/stubs.h b/ReactCommon/fabric/mounting/stubs/stubs.h new file mode 100644 index 000000000..f02c5a2e6 --- /dev/null +++ b/ReactCommon/fabric/mounting/stubs/stubs.h @@ -0,0 +1,18 @@ +// 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. + +#pragma once + +#include +#include "StubView.h" +#include "StubViewTree.h" + +namespace facebook { +namespace react { + +StubViewTree stubViewTreeFromShadowNode(ShadowNode const &rootShadowNode); + +} // namespace react +} // namespace facebook