From 4a6039c6359ba70f5cca57ceff26c06f2a0de76b Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Wed, 16 Sep 2020 11:46:47 -0700 Subject: [PATCH 001/241] Fix initial padding value for TextInput Summary: Changelog: [internal] # Problem Default padding for TextEdit is top: 26, bottom: 29, left: 10, right: 10 however the default values for LayoutMetrics.contentInsets is {0, 0, 0, 0}. If you try to construct TextEdit with padding 0, Fabric will drop padding update because padding is already 0, 0, 0, 0. # Fix To fix this, I added a special case to `Binding::createUpdatePaddingMountItem`, if the mutation is insert, proceed with updating padding. Reviewed By: JoshuaGross Differential Revision: D23731498 fbshipit-source-id: 294ab053e562c05aadf6e743fb6bf12285d50307 --- .../main/java/com/facebook/react/fabric/jni/Binding.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp index 9fb51e79fd7116..390166bfd7f716 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp @@ -428,11 +428,12 @@ local_ref createUpdatePaddingMountItem( auto newChildShadowView = mutation.newChildShadowView; if (oldChildShadowView.layoutMetrics.contentInsets == - newChildShadowView.layoutMetrics.contentInsets) { + newChildShadowView.layoutMetrics.contentInsets && + mutation.type != ShadowViewMutation::Type::Insert) { return nullptr; } - static auto updateLayoutInstruction = + static auto updatePaddingInstruction = jni::findClassStatic(Binding::UIManagerJavaDescriptor) ->getMethod(jint, jint, jint, jint, jint)>( "updatePaddingMountItem"); @@ -446,7 +447,7 @@ local_ref createUpdatePaddingMountItem( int right = round(contentInsets.right * pointScaleFactor); int bottom = round(contentInsets.bottom * pointScaleFactor); - return updateLayoutInstruction( + return updatePaddingInstruction( javaUIManager, newChildShadowView.tag, left, top, right, bottom); } From 047764482fcc09ae2b851f331f9db6e23b710c54 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Wed, 16 Sep 2020 23:52:42 -0700 Subject: [PATCH 002/241] Fabric: Simplifying ShadowTreeRevision implementation Summary: The implementation of this class is too complex for the purpose it serves. Making it simpler will make the code simpler and faster. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D23725688 fbshipit-source-id: 5e1ecddb0dd3c4c4f94786e2ba0af9b67e7426ce --- .../renderer/mounting/MountingCoordinator.cpp | 26 +++++++------- .../renderer/mounting/MountingCoordinator.h | 2 +- .../renderer/mounting/ShadowTreeRevision.cpp | 26 -------------- .../renderer/mounting/ShadowTreeRevision.h | 36 +++---------------- 4 files changed, 17 insertions(+), 73 deletions(-) diff --git a/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp b/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp index 7ad2b08e5e5700..1a925ed91db653 100644 --- a/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp +++ b/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp @@ -23,13 +23,13 @@ MountingCoordinator::MountingCoordinator( ShadowTreeRevision baseRevision, std::weak_ptr delegate, bool enableReparentingDetection) - : surfaceId_(baseRevision.getRootShadowNode().getSurfaceId()), + : surfaceId_(baseRevision.rootShadowNode->getSurfaceId()), baseRevision_(baseRevision), mountingOverrideDelegate_(delegate), telemetryController_(*this), enableReparentingDetection_(enableReparentingDetection) { #ifdef RN_SHADOW_TREE_INTROSPECTION - stubViewTree_ = stubViewTreeFromShadowNode(baseRevision_.getRootShadowNode()); + stubViewTree_ = stubViewTreeFromShadowNode(*baseRevision_.rootShadowNode); #endif } @@ -37,17 +37,15 @@ SurfaceId MountingCoordinator::getSurfaceId() const { return surfaceId_; } -void MountingCoordinator::push(ShadowTreeRevision &&revision) const { +void MountingCoordinator::push(ShadowTreeRevision const &revision) const { { std::lock_guard lock(mutex_); assert( - !lastRevision_.has_value() || - revision.getNumber() != lastRevision_->getNumber()); + !lastRevision_.has_value() || revision.number != lastRevision_->number); - if (!lastRevision_.has_value() || - lastRevision_->getNumber() < revision.getNumber()) { - lastRevision_ = std::move(revision); + if (!lastRevision_.has_value() || lastRevision_->number < revision.number) { + lastRevision_ = revision; } } @@ -60,7 +58,7 @@ void MountingCoordinator::revoke() const { // 1. We need to stop retaining `ShadowNode`s to not prolong their lifetime // to prevent them from overliving `ComponentDescriptor`s. // 2. A possible call to `pullTransaction()` should return empty optional. - baseRevision_.rootShadowNode_.reset(); + baseRevision_.rootShadowNode.reset(); lastRevision_.reset(); } @@ -90,12 +88,12 @@ better::optional MountingCoordinator::pullTransaction() if (lastRevision_.has_value()) { number_++; - auto telemetry = lastRevision_->getTelemetry(); + auto telemetry = lastRevision_->telemetry; telemetry.willDiff(); auto mutations = calculateShadowViewMutations( - baseRevision_.getRootShadowNode(), lastRevision_->getRootShadowNode()); + *baseRevision_.rootShadowNode, *lastRevision_->rootShadowNode); telemetry.didDiff(); @@ -145,11 +143,11 @@ better::optional MountingCoordinator::pullTransaction() auto line = std::string{}; auto stubViewTree = - stubViewTreeFromShadowNode(baseRevision_.getRootShadowNode()); + stubViewTreeFromShadowNode(*baseRevision_.rootShadowNode); if (stubViewTree_ != stubViewTree) { std::stringstream ssOldTree( - baseRevision_.getRootShadowNode().getDebugDescription()); + baseRevision_.rootShadowNode->getDebugDescription()); while (std::getline(ssOldTree, line, '\n')) { LOG(ERROR) << "Old tree:" << line; } @@ -160,7 +158,7 @@ better::optional MountingCoordinator::pullTransaction() } std::stringstream ssNewTree( - lastRevision_->getRootShadowNode().getDebugDescription()); + lastRevision_->rootShadowNode->getDebugDescription()); while (std::getline(ssNewTree, line, '\n')) { LOG(ERROR) << "New tree:" << line; } diff --git a/ReactCommon/react/renderer/mounting/MountingCoordinator.h b/ReactCommon/react/renderer/mounting/MountingCoordinator.h index 54bb0a3a63d4a7..4563dc7bd0aa5c 100644 --- a/ReactCommon/react/renderer/mounting/MountingCoordinator.h +++ b/ReactCommon/react/renderer/mounting/MountingCoordinator.h @@ -91,7 +91,7 @@ class MountingCoordinator final { private: friend class ShadowTree; - void push(ShadowTreeRevision &&revision) const; + void push(ShadowTreeRevision const &revision) const; /* * Revokes the last pushed `ShadowTreeRevision`. diff --git a/ReactCommon/react/renderer/mounting/ShadowTreeRevision.cpp b/ReactCommon/react/renderer/mounting/ShadowTreeRevision.cpp index 998464886ae021..f56d03ec2023e0 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTreeRevision.cpp +++ b/ReactCommon/react/renderer/mounting/ShadowTreeRevision.cpp @@ -6,29 +6,3 @@ */ #include "ShadowTreeRevision.h" - -namespace facebook { -namespace react { - -using Number = ShadowTreeRevision::Number; - -ShadowTreeRevision::ShadowTreeRevision( - RootShadowNode::Shared const &rootShadowNode, - Number number, - TransactionTelemetry telemetry) - : rootShadowNode_(rootShadowNode), number_(number), telemetry_(telemetry) {} - -TransactionTelemetry const &ShadowTreeRevision::getTelemetry() const { - return telemetry_; -} - -RootShadowNode const &ShadowTreeRevision::getRootShadowNode() { - return *rootShadowNode_; -} - -Number ShadowTreeRevision::getNumber() const { - return number_; -} - -} // namespace react -} // namespace facebook diff --git a/ReactCommon/react/renderer/mounting/ShadowTreeRevision.h b/ReactCommon/react/renderer/mounting/ShadowTreeRevision.h index d2a63a00aa487e..d58dbd643288d7 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTreeRevision.h +++ b/ReactCommon/react/renderer/mounting/ShadowTreeRevision.h @@ -30,40 +30,12 @@ class ShadowTreeRevision final { */ using Number = int64_t; - /* - * Creates the object with given root shadow node, revision number and - * telemetry. - */ - ShadowTreeRevision( - RootShadowNode::Shared const &rootShadowNode, - Number number, - TransactionTelemetry telemetry); - - /* - * Returns telemetry associated with this revision. - */ - TransactionTelemetry const &getTelemetry() const; - - /* - * Methods from this section are meant to be used by - * `MountingOverrideDelegate` only. - */ - public: - RootShadowNode const &getRootShadowNode(); - - /* - * Methods from this section are meant to be used by `MountingCoordinator` - * only. - */ - private: + friend class ShadowTree; friend class MountingCoordinator; - Number getNumber() const; - - private: - RootShadowNode::Shared rootShadowNode_; - Number number_; - TransactionTelemetry telemetry_; + RootShadowNode::Shared rootShadowNode; + Number number; + TransactionTelemetry telemetry; }; } // namespace react From bc5a15d2c8586bea94193d67779a875f5b779578 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Wed, 16 Sep 2020 23:52:42 -0700 Subject: [PATCH 003/241] Fabric: Using ShadowTreeRevision inside ShadowTree to store current commited tree Summary: Instead of storing tree separate instance variables, we now store a single object that contains them. We will need it to be able to postpone the mounting stage (we save all info we need for mounting inside the new instance variable). Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D23725690 fbshipit-source-id: 09dd4a0912c6f5b34d805636b62f73ca12287329 --- .../react/renderer/mounting/ShadowTree.cpp | 49 ++++++++++--------- .../react/renderer/mounting/ShadowTree.h | 5 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/ReactCommon/react/renderer/mounting/ShadowTree.cpp b/ReactCommon/react/renderer/mounting/ShadowTree.cpp index 59896ad7b1e257..87e95659607e5f 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTree.cpp +++ b/ReactCommon/react/renderer/mounting/ShadowTree.cpp @@ -237,17 +237,19 @@ ShadowTree::ShadowTree( auto family = rootComponentDescriptor.createFamily( ShadowNodeFamilyFragment{surfaceId, surfaceId, noopEventEmitter}, nullptr); - rootShadowNode_ = std::static_pointer_cast( + + auto rootShadowNode = std::static_pointer_cast( rootComponentDescriptor.createShadowNode( ShadowNodeFragment{ /* .props = */ props, }, family)); + currentRevision_ = ShadowTreeRevision{ + rootShadowNode, ShadowTreeRevision::Number{0}, TransactionTelemetry{}}; + mountingCoordinator_ = std::make_shared( - ShadowTreeRevision{rootShadowNode_, 0, {}}, - mountingOverrideDelegate, - enableReparentingDetection); + currentRevision_, mountingOverrideDelegate, enableReparentingDetection); } ShadowTree::~ShadowTree() { @@ -291,15 +293,16 @@ CommitStatus ShadowTree::tryCommit( auto telemetry = TransactionTelemetry{}; telemetry.willCommit(); - RootShadowNode::Shared oldRootShadowNode; + auto oldRevision = ShadowTreeRevision{}; + auto newRevision = ShadowTreeRevision{}; { - // Reading `rootShadowNode_` in shared manner. + // Reading `currentRevision_` in shared manner. std::shared_lock lock(commitMutex_); - oldRootShadowNode = rootShadowNode_; + oldRevision = currentRevision_; } - RootShadowNode::Unshared newRootShadowNode = transaction(oldRootShadowNode); + auto newRootShadowNode = transaction(oldRevision.rootShadowNode); if (!newRootShadowNode) { return CommitStatus::Cancelled; @@ -307,14 +310,14 @@ CommitStatus ShadowTree::tryCommit( if (enableStateReconciliation) { auto updatedNewRootShadowNode = - progressState(*newRootShadowNode, *oldRootShadowNode); + progressState(*newRootShadowNode, *oldRevision.rootShadowNode); if (updatedNewRootShadowNode) { newRootShadowNode = std::static_pointer_cast(updatedNewRootShadowNode); } } - // Layout nodes + // Layout nodes. std::vector affectedLayoutableNodes{}; affectedLayoutableNodes.reserve(1024); @@ -327,36 +330,36 @@ CommitStatus ShadowTree::tryCommit( // Seal the shadow node so it can no longer be mutated newRootShadowNode->sealRecursive(); - auto revisionNumber = ShadowTreeRevision::Number{}; - { - // Updating `rootShadowNode_` in unique manner if it hasn't changed. + // Updating `currentRevision_` in unique manner if it hasn't changed. std::unique_lock lock(commitMutex_); - if (rootShadowNode_ != oldRootShadowNode) { + if (currentRevision_.number != oldRevision.number) { return CommitStatus::Failed; } - rootShadowNode_ = newRootShadowNode; + auto newRevisionNumber = oldRevision.number + 1; { std::lock_guard dispatchLock(EventEmitter::DispatchMutex()); updateMountedFlag( - oldRootShadowNode->getChildren(), newRootShadowNode->getChildren()); + currentRevision_.rootShadowNode->getChildren(), + newRootShadowNode->getChildren()); } - revisionNumber_++; - revisionNumber = revisionNumber_; + telemetry.didCommit(); + telemetry.setRevisionNumber(newRevisionNumber); + + newRevision = + ShadowTreeRevision{newRootShadowNode, newRevisionNumber, telemetry}; + + currentRevision_ = newRevision; } emitLayoutEvents(affectedLayoutableNodes); - telemetry.didCommit(); - telemetry.setRevisionNumber(revisionNumber); - - mountingCoordinator_->push( - ShadowTreeRevision{newRootShadowNode, revisionNumber, telemetry}); + mountingCoordinator_->push(newRevision); notifyDelegatesOfUpdates(); diff --git a/ReactCommon/react/renderer/mounting/ShadowTree.h b/ReactCommon/react/renderer/mounting/ShadowTree.h index ac984fa8fb5b18..7a02cf9d370a9b 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTree.h +++ b/ReactCommon/react/renderer/mounting/ShadowTree.h @@ -101,10 +101,7 @@ class ShadowTree final { SurfaceId const surfaceId_; ShadowTreeDelegate const &delegate_; mutable better::shared_mutex commitMutex_; - mutable RootShadowNode::Shared - rootShadowNode_; // Protected by `commitMutex_`. - mutable ShadowTreeRevision::Number revisionNumber_{ - 0}; // Protected by `commitMutex_`. + mutable ShadowTreeRevision currentRevision_; // Protected by `commitMutex_`. MountingCoordinator::Shared mountingCoordinator_; bool enableReparentingDetection_{false}; }; From 0118cbf1d1d27995e2f1b1107217b0aad8fa070f Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Wed, 16 Sep 2020 23:52:42 -0700 Subject: [PATCH 004/241] Fabric: Introducing `ShadowTree::getCurrentRevision()` Summary: Previously, to get a current root shadow node for a shadow tree, we called `tryCommit` method and stole a pointer from this. That was not a very straightforward method to get things done, and most importantly we need to do this to change the shape of the ShadowTreeCommitTransaction signature (remove a shared pointer from the callback) to make it simpler, faster and allow future improvements (see the next diff). Changelog: [Internal] Fabric-specific internal change. Reviewed By: JoshuaGross, sammy-SC Differential Revision: D23725689 fbshipit-source-id: 51950b843a0e401828b6c6a38e5e2aaaf21ec166 --- .../react/renderer/mounting/ShadowTree.cpp | 5 +++++ .../react/renderer/mounting/ShadowTree.h | 6 ++++++ .../mounting/tests/StateReconciliationTest.cpp | 12 ++---------- .../react/renderer/scheduler/Scheduler.cpp | 17 +++++++---------- .../react/renderer/uimanager/UIManager.cpp | 17 ++++------------- 5 files changed, 24 insertions(+), 33 deletions(-) diff --git a/ReactCommon/react/renderer/mounting/ShadowTree.cpp b/ReactCommon/react/renderer/mounting/ShadowTree.cpp index 87e95659607e5f..a8870b68a8010b 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTree.cpp +++ b/ReactCommon/react/renderer/mounting/ShadowTree.cpp @@ -366,6 +366,11 @@ CommitStatus ShadowTree::tryCommit( return CommitStatus::Succeeded; } +ShadowTreeRevision ShadowTree::getCurrentRevision() const { + std::shared_lock lock(commitMutex_); + return currentRevision_; +} + void ShadowTree::commitEmptyTree() const { commit( [](RootShadowNode::Shared const &oldRootShadowNode) diff --git a/ReactCommon/react/renderer/mounting/ShadowTree.h b/ReactCommon/react/renderer/mounting/ShadowTree.h index 7a02cf9d370a9b..09e0b060a43abe 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTree.h +++ b/ReactCommon/react/renderer/mounting/ShadowTree.h @@ -72,6 +72,12 @@ class ShadowTree final { ShadowTreeCommitTransaction transaction, bool enableStateReconciliation = false) const; + /* + * Returns a `ShadowTreeRevision` representing the momentary state of + * the `ShadowTree`. + */ + ShadowTreeRevision getCurrentRevision() const; + /* * Commit an empty tree (a new `RootShadowNode` with no children). */ diff --git a/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp b/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp index c122047fa4f92e..04c29983e0e692 100644 --- a/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp +++ b/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp @@ -43,16 +43,8 @@ inline ShadowNode const *findDescendantNode( inline ShadowNode const *findDescendantNode( ShadowTree const &shadowTree, ShadowNodeFamily const &family) { - ShadowNode const *result = nullptr; - - shadowTree.tryCommit( - [&](RootShadowNode::Shared const &oldRootShadowNode) { - result = findDescendantNode(*oldRootShadowNode, family); - return nullptr; - }, - false); - - return result; + return findDescendantNode( + *shadowTree.getCurrentRevision().rootShadowNode, family); } TEST(StateReconciliationTest, testStateReconciliation) { diff --git a/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 37b724d0814753..516d36183f1583 100644 --- a/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -272,19 +272,16 @@ Size Scheduler::measureSurface( const LayoutContext &layoutContext) const { SystraceSection s("Scheduler::measureSurface"); - Size size; + auto currentRootShadowNode = RootShadowNode::Shared{}; uiManager_->getShadowTreeRegistry().visit( surfaceId, [&](const ShadowTree &shadowTree) { - shadowTree.tryCommit( - [&](RootShadowNode::Shared const &oldRootShadowNode) { - auto rootShadowNode = - oldRootShadowNode->clone(layoutConstraints, layoutContext); - rootShadowNode->layoutIfNeeded(); - size = rootShadowNode->getLayoutMetrics().frame.size; - return nullptr; - }); + currentRootShadowNode = shadowTree.getCurrentRevision().rootShadowNode; }); - return size; + + auto rootShadowNode = + currentRootShadowNode->clone(layoutConstraints, layoutContext); + rootShadowNode->layoutIfNeeded(); + return rootShadowNode->getLayoutMetrics().frame.size; } MountingCoordinator::Shared Scheduler::findMountingCoordinator( diff --git a/ReactCommon/react/renderer/uimanager/UIManager.cpp b/ReactCommon/react/renderer/uimanager/UIManager.cpp index 6c2e6253950b8f..8061c5e2f010d8 100644 --- a/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -139,12 +139,7 @@ ShadowNode::Shared UIManager::getNewestCloneOfShadowNode( auto ancestorShadowNode = ShadowNode::Shared{}; shadowTreeRegistry_.visit( shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) { - shadowTree.tryCommit( - [&](RootShadowNode::Shared const &oldRootShadowNode) { - ancestorShadowNode = oldRootShadowNode; - return nullptr; - }, - true); + ancestorShadowNode = shadowTree.getCurrentRevision().rootShadowNode; }); if (!ancestorShadowNode) { @@ -205,13 +200,9 @@ LayoutMetrics UIManager::getRelativeLayoutMetrics( if (!ancestorShadowNode) { shadowTreeRegistry_.visit( shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) { - shadowTree.tryCommit( - [&](RootShadowNode::Shared const &oldRootShadowNode) { - owningAncestorShadowNode = oldRootShadowNode; - ancestorShadowNode = oldRootShadowNode.get(); - return nullptr; - }, - true); + owningAncestorShadowNode = + shadowTree.getCurrentRevision().rootShadowNode; + ancestorShadowNode = owningAncestorShadowNode.get(); }); } else { // It is possible for JavaScript (or other callers) to have a reference From 13bc3c87efdc17d7f5a57231b23c4fbd82cfb553 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Wed, 16 Sep 2020 23:52:42 -0700 Subject: [PATCH 005/241] Fabric: Removing shared_ptr from ShadowTreeCommitTransaction's argument Summary: We don't need a shared_ptr here and without it the code will be faster and simpler. This change is aligned with any clone-line callbacks we have in the Core which accepts a `const &` and return `shared_ptr<>`. Changelog: [Internal] Fabric-specific internal change. Reviewed By: JoshuaGross, sammy-SC Differential Revision: D23725687 fbshipit-source-id: 1cd959f4273913175d342302e2f12752f0114768 --- .../react/renderer/mounting/ShadowTree.cpp | 7 +++---- ReactCommon/react/renderer/mounting/ShadowTree.h | 2 +- .../mounting/tests/StateReconciliationTest.cpp | 8 ++++---- .../react/renderer/scheduler/Scheduler.cpp | 8 ++++---- .../react/renderer/uimanager/UIManager.cpp | 16 ++++++++-------- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/ReactCommon/react/renderer/mounting/ShadowTree.cpp b/ReactCommon/react/renderer/mounting/ShadowTree.cpp index a8870b68a8010b..d7d3a8461542bd 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTree.cpp +++ b/ReactCommon/react/renderer/mounting/ShadowTree.cpp @@ -302,7 +302,7 @@ CommitStatus ShadowTree::tryCommit( oldRevision = currentRevision_; } - auto newRootShadowNode = transaction(oldRevision.rootShadowNode); + auto newRootShadowNode = transaction(*oldRevision.rootShadowNode); if (!newRootShadowNode) { return CommitStatus::Cancelled; @@ -373,10 +373,9 @@ ShadowTreeRevision ShadowTree::getCurrentRevision() const { void ShadowTree::commitEmptyTree() const { commit( - [](RootShadowNode::Shared const &oldRootShadowNode) - -> RootShadowNode::Unshared { + [](RootShadowNode const &oldRootShadowNode) -> RootShadowNode::Unshared { return std::make_shared( - *oldRootShadowNode, + oldRootShadowNode, ShadowNodeFragment{ /* .props = */ ShadowNodeFragment::propsPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), diff --git a/ReactCommon/react/renderer/mounting/ShadowTree.h b/ReactCommon/react/renderer/mounting/ShadowTree.h index 09e0b060a43abe..1a2851d57345f4 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTree.h +++ b/ReactCommon/react/renderer/mounting/ShadowTree.h @@ -24,7 +24,7 @@ namespace facebook { namespace react { using ShadowTreeCommitTransaction = std::function; + RootShadowNode const &oldRootShadowNode)>; /* * Represents the shadow tree and its lifecycle. diff --git a/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp b/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp index 04c29983e0e692..99b86ca357b1b7 100644 --- a/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp +++ b/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp @@ -99,7 +99,7 @@ TEST(StateReconciliationTest, testStateReconciliation) { {}}; shadowTree.commit( - [&](RootShadowNode::Shared const &oldRootShadowNode) { + [&](RootShadowNode const &oldRootShadowNode) { return std::static_pointer_cast(rootShadowNodeState1); }, true); @@ -123,7 +123,7 @@ TEST(StateReconciliationTest, testStateReconciliation) { findDescendantNode(*rootShadowNodeState2, family)->getState(), state2); shadowTree.commit( - [&](RootShadowNode::Shared const &oldRootShadowNode) { + [&](RootShadowNode const &oldRootShadowNode) { return std::static_pointer_cast(rootShadowNodeState2); }, true); @@ -145,7 +145,7 @@ TEST(StateReconciliationTest, testStateReconciliation) { findDescendantNode(*rootShadowNodeState3, family)->getState(), state3); shadowTree.commit( - [&](RootShadowNode::Shared const &oldRootShadowNode) { + [&](RootShadowNode const &oldRootShadowNode) { return std::static_pointer_cast(rootShadowNodeState3); }, true); @@ -160,7 +160,7 @@ TEST(StateReconciliationTest, testStateReconciliation) { // Here we commit the old tree but we expect that the state associated with // the node will stay the same (newer that the old tree has). shadowTree.commit( - [&](RootShadowNode::Shared const &oldRootShadowNode) { + [&](RootShadowNode const &oldRootShadowNode) { return std::static_pointer_cast(rootShadowNodeState2); }, true); diff --git a/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 516d36183f1583..a2384dfb8ecce0 100644 --- a/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -220,9 +220,9 @@ void Scheduler::renderTemplateToSurface( uiManager_->getShadowTreeRegistry().visit( surfaceId, [=](const ShadowTree &shadowTree) { return shadowTree.tryCommit( - [&](RootShadowNode::Shared const &oldRootShadowNode) { + [&](RootShadowNode const &oldRootShadowNode) { return std::make_shared( - *oldRootShadowNode, + oldRootShadowNode, ShadowNodeFragment{ /* .props = */ ShadowNodeFragment::propsPlaceholder(), /* .children = */ @@ -302,8 +302,8 @@ void Scheduler::constraintSurfaceLayout( uiManager_->getShadowTreeRegistry().visit( surfaceId, [&](ShadowTree const &shadowTree) { - shadowTree.commit([&](RootShadowNode::Shared const &oldRootShadowNode) { - return oldRootShadowNode->clone(layoutConstraints, layoutContext); + shadowTree.commit([&](RootShadowNode const &oldRootShadowNode) { + return oldRootShadowNode.clone(layoutConstraints, layoutContext); }); }); } diff --git a/ReactCommon/react/renderer/uimanager/UIManager.cpp b/ReactCommon/react/renderer/uimanager/UIManager.cpp index 8061c5e2f010d8..8f6c4bb0aa7ca4 100644 --- a/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -97,9 +97,9 @@ void UIManager::completeSurface( shadowTreeRegistry_.visit(surfaceId, [&](ShadowTree const &shadowTree) { shadowTree.commit( - [&](RootShadowNode::Shared const &oldRootShadowNode) { + [&](RootShadowNode const &oldRootShadowNode) { return std::make_shared( - *oldRootShadowNode, + oldRootShadowNode, ShadowNodeFragment{ /* .props = */ ShadowNodeFragment::propsPlaceholder(), /* .children = */ rootChildren, @@ -173,9 +173,9 @@ void UIManager::setNativeProps( shadowTreeRegistry_.visit( shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) { shadowTree.tryCommit( - [&](RootShadowNode::Shared const &oldRootShadowNode) { + [&](RootShadowNode const &oldRootShadowNode) { return std::static_pointer_cast( - oldRootShadowNode->cloneTree( + oldRootShadowNode.cloneTree( shadowNode.getFamily(), [&](ShadowNode const &oldShadowNode) { return oldShadowNode.clone({ @@ -231,10 +231,10 @@ void UIManager::updateStateWithAutorepeat( shadowTreeRegistry_.visit( family->getSurfaceId(), [&](ShadowTree const &shadowTree) { - shadowTree.commit([&](RootShadowNode::Shared const &oldRootShadowNode) { + shadowTree.commit([&](RootShadowNode const &oldRootShadowNode) { auto isValid = true; - auto rootNode = oldRootShadowNode->cloneTree( + auto rootNode = oldRootShadowNode.cloneTree( *family, [&](ShadowNode const &oldShadowNode) { auto newData = callback(oldShadowNode.getState()->getDataPointer()); @@ -274,10 +274,10 @@ void UIManager::updateState(StateUpdate const &stateUpdate) const { shadowTreeRegistry_.visit( family->getSurfaceId(), [&](ShadowTree const &shadowTree) { - auto status = shadowTree.tryCommit([&](RootShadowNode::Shared const + auto status = shadowTree.tryCommit([&](RootShadowNode const &oldRootShadowNode) { return std::static_pointer_cast( - oldRootShadowNode->cloneTree( + oldRootShadowNode.cloneTree( *family, [&](ShadowNode const &oldShadowNode) { auto newData = callback(oldShadowNode.getState()->getDataPointer()); From 04c874bd9c6b15274fd87acf10cb3533b2eabc0d Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Thu, 17 Sep 2020 08:55:20 -0700 Subject: [PATCH 006/241] Fabric: Using `thread_local` keyword instead on own implementation in TransactionTelemetry Summary: Apparently, there is C++ keyword for this. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D23754284 fbshipit-source-id: 5f9bbcc72d9c586173624869d614f12d2319fb7b --- .../react/renderer/mounting/TransactionTelemetry.cpp | 8 ++++---- .../react/renderer/mounting/TransactionTelemetry.h | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ReactCommon/react/renderer/mounting/TransactionTelemetry.cpp b/ReactCommon/react/renderer/mounting/TransactionTelemetry.cpp index f1ac3f659feaa7..d81ed7c6bf44b1 100644 --- a/ReactCommon/react/renderer/mounting/TransactionTelemetry.cpp +++ b/ReactCommon/react/renderer/mounting/TransactionTelemetry.cpp @@ -12,18 +12,18 @@ namespace facebook { namespace react { -using ThreadLocalTransactionTelemetry = ThreadStorage; +thread_local TransactionTelemetry *threadLocalTransactionTelemetry = nullptr; TransactionTelemetry *TransactionTelemetry::threadLocalTelemetry() { - return ThreadLocalTransactionTelemetry::getInstance().get().value_or(nullptr); + return threadLocalTransactionTelemetry; } void TransactionTelemetry::setAsThreadLocal() { - ThreadLocalTransactionTelemetry::getInstance().set(this); + threadLocalTransactionTelemetry = this; } void TransactionTelemetry::unsetAsThreadLocal() { - ThreadLocalTransactionTelemetry::getInstance().set(nullptr); + threadLocalTransactionTelemetry = nullptr; } void TransactionTelemetry::willCommit() { diff --git a/ReactCommon/react/renderer/mounting/TransactionTelemetry.h b/ReactCommon/react/renderer/mounting/TransactionTelemetry.h index b71e10bae33e78..88ef2a23b0e3c4 100644 --- a/ReactCommon/react/renderer/mounting/TransactionTelemetry.h +++ b/ReactCommon/react/renderer/mounting/TransactionTelemetry.h @@ -11,7 +11,6 @@ #include #include -#include namespace facebook { namespace react { From 18ffe12203d03b4e960d61d7bb50cd02bba94663 Mon Sep 17 00:00:00 2001 From: Jason Safaiyeh Date: Thu, 17 Sep 2020 09:25:56 -0700 Subject: [PATCH 007/241] Add an explicit NDK version to Android template (#29403) Summary: Added an explicit NDK version to the Android template. This allows tooling to detect which NDK version to install automatically. ## Changelog [Android] [Added] - Add an explicit NDK version to Android template Pull Request resolved: https://github.com/facebook/react-native/pull/29403 Test Plan: Template builds as it should. NDK version gets installed with initial Android set up. Reviewed By: passy Differential Revision: D23752007 Pulled By: fkgozali fbshipit-source-id: 31c33f275f94a4a62338a61e79b31c4b996969bf --- template/android/app/build.gradle | 2 ++ template/android/build.gradle | 1 + 2 files changed, 3 insertions(+) diff --git a/template/android/app/build.gradle b/template/android/app/build.gradle index 1215ed78d324f2..a5a0ea4e8bc90c 100644 --- a/template/android/app/build.gradle +++ b/template/android/app/build.gradle @@ -121,6 +121,8 @@ def jscFlavor = 'org.webkit:android-jsc:+' def enableHermes = project.ext.react.get("enableHermes", false); android { + ndkVersion rootProject.ext.ndkVersion + compileSdkVersion rootProject.ext.compileSdkVersion compileOptions { diff --git a/template/android/build.gradle b/template/android/build.gradle index 86351cce876abf..2bafa1f491c53c 100644 --- a/template/android/build.gradle +++ b/template/android/build.gradle @@ -6,6 +6,7 @@ buildscript { minSdkVersion = 19 compileSdkVersion = 29 targetSdkVersion = 29 + ndkVersion = "20.1.5948944" } repositories { google() From f2ca6f568e1caa2050032089c8378b4f3e2bc074 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Thu, 17 Sep 2020 11:09:42 -0700 Subject: [PATCH 008/241] Fabric: Removing `RCTCGColorRefUnretainedFromSharedColor()` Summary: This diff removes `RCTCGColorRefUnretainedFromSharedColor` function because it's not compatible with future changes we plan to make. Converting SharedColor to unretained CGColorRef is actually quite dangerous because... it's an unowned pointer. Reviewed By: JoshuaGross Differential Revision: D23753509 fbshipit-source-id: df030ade69b63cbb872d6bdbde51575eedc6a4a6 --- .../View/RCTViewComponentView.mm | 32 +++++++++++++------ React/Fabric/RCTConversions.h | 9 ++---- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 35b7858f01e177..5e3b33d9a00ba5 100644 --- a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -7,10 +7,12 @@ #import "RCTViewComponentView.h" +#import +#import + #import #import #import -#import #import #import #import @@ -118,7 +120,7 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & // `shadowColor` if (oldViewProps.shadowColor != newViewProps.shadowColor) { - CGColorRef shadowColor = RCTCGColorRefFromSharedColor(newViewProps.shadowColor); + CGColorRef shadowColor = RCTCreateCGColorRefFromSharedColor(newViewProps.shadowColor); self.layer.shadowColor = shadowColor; CGColorRelease(shadowColor); needsInvalidateLayer = YES; @@ -344,12 +346,20 @@ static RCTCornerRadii RCTCornerRadiiFromBorderRadii(BorderRadii borderRadii) .bottomRight = (CGFloat)borderRadii.bottomRight}; } -static RCTBorderColors RCTBorderColorsFromBorderColors(BorderColors borderColors) +static RCTBorderColors RCTCreateRCTBorderColorsFromBorderColors(BorderColors borderColors) +{ + return RCTBorderColors{.top = RCTCreateCGColorRefFromSharedColor(borderColors.top), + .left = RCTCreateCGColorRefFromSharedColor(borderColors.left), + .bottom = RCTCreateCGColorRefFromSharedColor(borderColors.bottom), + .right = RCTCreateCGColorRefFromSharedColor(borderColors.right)}; +} + +static void RCTReleaseRCTBorderColors(RCTBorderColors borderColors) { - return RCTBorderColors{.top = RCTCGColorRefUnretainedFromSharedColor(borderColors.top), - .left = RCTCGColorRefUnretainedFromSharedColor(borderColors.left), - .bottom = RCTCGColorRefUnretainedFromSharedColor(borderColors.bottom), - .right = RCTCGColorRefUnretainedFromSharedColor(borderColors.right)}; + CGColorRelease(borderColors.top); + CGColorRelease(borderColors.left); + CGColorRelease(borderColors.bottom); + CGColorRelease(borderColors.right); } static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle) @@ -412,7 +422,7 @@ - (void)invalidateLayer } layer.borderWidth = (CGFloat)borderMetrics.borderWidths.left; - CGColorRef borderColor = RCTCGColorRefFromSharedColor(borderMetrics.borderColors.left); + CGColorRef borderColor = RCTCreateCGColorRefFromSharedColor(borderMetrics.borderColors.left); layer.borderColor = borderColor; CGColorRelease(borderColor); layer.cornerRadius = (CGFloat)borderMetrics.borderRadii.topLeft; @@ -431,15 +441,19 @@ - (void)invalidateLayer layer.borderColor = nil; layer.cornerRadius = 0; + RCTBorderColors borderColors = RCTCreateRCTBorderColorsFromBorderColors(borderMetrics.borderColors); + UIImage *image = RCTGetBorderImage( RCTBorderStyleFromBorderStyle(borderMetrics.borderStyles.left), layer.bounds.size, RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii), RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths), - RCTBorderColorsFromBorderColors(borderMetrics.borderColors), + borderColors, _backgroundColor.CGColor, self.clipsToBounds); + RCTReleaseRCTBorderColors(borderColors); + if (image == nil) { _borderLayer.contents = nil; } else { diff --git a/React/Fabric/RCTConversions.h b/React/Fabric/RCTConversions.h index f1dc7ee2910f17..5caafef9567210 100644 --- a/React/Fabric/RCTConversions.h +++ b/React/Fabric/RCTConversions.h @@ -40,13 +40,8 @@ inline UIColor *_Nullable RCTUIColorFromSharedColor(const facebook::react::Share return sharedColor ? [UIColor colorWithCGColor:sharedColor.get()] : nil; } -inline CF_RETURNS_NOT_RETAINED CGColorRef -RCTCGColorRefUnretainedFromSharedColor(const facebook::react::SharedColor &sharedColor) -{ - return sharedColor ? sharedColor.get() : nil; -} - -inline CF_RETURNS_RETAINED CGColorRef RCTCGColorRefFromSharedColor(const facebook::react::SharedColor &sharedColor) +inline CF_RETURNS_RETAINED CGColorRef +RCTCreateCGColorRefFromSharedColor(const facebook::react::SharedColor &sharedColor) { return sharedColor ? CGColorCreateCopy(sharedColor.get()) : nil; } From d3bb76b8b6ab93755822d0d5c4235a2f0048478e Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Thu, 17 Sep 2020 11:09:42 -0700 Subject: [PATCH 009/241] Fabric: Using RCTUIColorFromSharedColor everywhere Summary: Even though we have wonderful helper functions converting SharedColor to UIColor and CGColorRef, in many places we still use some hardcoded things. This diff fixes that; it will be important for the next diff. Changelog: [Internal] Fabric-specific internal change. Reviewed By: JoshuaGross Differential Revision: D23753508 fbshipit-source-id: 09d280b132266252753526c2735ab3e41b96c8d5 --- .../RCTActivityIndicatorViewComponentView.mm | 8 +++++--- .../ComponentViews/Image/RCTImageComponentView.mm | 2 +- .../ComponentViews/Slider/RCTSliderComponentView.mm | 8 +++++--- .../ComponentViews/Switch/RCTSwitchComponentView.mm | 8 +++++--- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/React/Fabric/Mounting/ComponentViews/ActivityIndicator/RCTActivityIndicatorViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ActivityIndicator/RCTActivityIndicatorViewComponentView.mm index dd79102526d9e5..7b5a56999ed8be 100644 --- a/React/Fabric/Mounting/ComponentViews/ActivityIndicator/RCTActivityIndicatorViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/ActivityIndicator/RCTActivityIndicatorViewComponentView.mm @@ -7,6 +7,8 @@ #import "RCTActivityIndicatorViewComponentView.h" +#import + #import #import #import @@ -50,7 +52,7 @@ - (instancetype)initWithFrame:(CGRect)frame } else { [_activityIndicatorView stopAnimating]; } - _activityIndicatorView.color = [UIColor colorWithCGColor:defaultProps->color.get()]; + _activityIndicatorView.color = RCTUIColorFromSharedColor(defaultProps->color); _activityIndicatorView.hidesWhenStopped = defaultProps->hidesWhenStopped; _activityIndicatorView.activityIndicatorViewStyle = convertActivityIndicatorViewStyle(defaultProps->size); @@ -73,8 +75,8 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & } } - if (oldViewProps.color.get() != newViewProps.color.get()) { - _activityIndicatorView.color = [UIColor colorWithCGColor:newViewProps.color.get()]; + if (oldViewProps.color != newViewProps.color) { + _activityIndicatorView.color = RCTUIColorFromSharedColor(newViewProps.color); } // TODO: This prop should be deprecated. diff --git a/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm index a80c9914ea75ad..ce8e55c0229fb6 100644 --- a/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm @@ -74,7 +74,7 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & // `tintColor` if (oldImageProps.tintColor != newImageProps.tintColor) { - _imageView.tintColor = [UIColor colorWithCGColor:newImageProps.tintColor.get()]; + _imageView.tintColor = RCTUIColorFromSharedColor(newImageProps.tintColor); } [super updateProps:props oldProps:oldProps]; diff --git a/React/Fabric/Mounting/ComponentViews/Slider/RCTSliderComponentView.mm b/React/Fabric/Mounting/ComponentViews/Slider/RCTSliderComponentView.mm index e28a7eb5da9f3b..abc29605cf4582 100644 --- a/React/Fabric/Mounting/ComponentViews/Slider/RCTSliderComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Slider/RCTSliderComponentView.mm @@ -7,8 +7,10 @@ #import "RCTSliderComponentView.h" +#import #import #import + #import #import #import @@ -131,17 +133,17 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & // `thumbTintColor` if (oldSliderProps.thumbTintColor != newSliderProps.thumbTintColor) { - _sliderView.thumbTintColor = [UIColor colorWithCGColor:newSliderProps.thumbTintColor.get()]; + _sliderView.thumbTintColor = RCTUIColorFromSharedColor(newSliderProps.thumbTintColor); } // `minimumTrackTintColor` if (oldSliderProps.minimumTrackTintColor != newSliderProps.minimumTrackTintColor) { - _sliderView.minimumTrackTintColor = [UIColor colorWithCGColor:newSliderProps.minimumTrackTintColor.get()]; + _sliderView.minimumTrackTintColor = RCTUIColorFromSharedColor(newSliderProps.minimumTrackTintColor); } // `maximumTrackTintColor` if (oldSliderProps.maximumTrackTintColor != newSliderProps.maximumTrackTintColor) { - _sliderView.maximumTrackTintColor = [UIColor colorWithCGColor:newSliderProps.maximumTrackTintColor.get()]; + _sliderView.maximumTrackTintColor = RCTUIColorFromSharedColor(newSliderProps.maximumTrackTintColor); } [super updateProps:props oldProps:oldProps]; diff --git a/React/Fabric/Mounting/ComponentViews/Switch/RCTSwitchComponentView.mm b/React/Fabric/Mounting/ComponentViews/Switch/RCTSwitchComponentView.mm index 2538f040476c78..5c89f7cbc0bab0 100644 --- a/React/Fabric/Mounting/ComponentViews/Switch/RCTSwitchComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Switch/RCTSwitchComponentView.mm @@ -7,6 +7,8 @@ #import "RCTSwitchComponentView.h" +#import + #import #import #import @@ -75,17 +77,17 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & // `tintColor` if (oldSwitchProps.tintColor != newSwitchProps.tintColor) { - _switchView.tintColor = [UIColor colorWithCGColor:newSwitchProps.tintColor.get()]; + _switchView.tintColor = RCTUIColorFromSharedColor(newSwitchProps.tintColor); } // `onTintColor if (oldSwitchProps.onTintColor != newSwitchProps.onTintColor) { - _switchView.onTintColor = [UIColor colorWithCGColor:newSwitchProps.onTintColor.get()]; + _switchView.onTintColor = RCTUIColorFromSharedColor(newSwitchProps.onTintColor); } // `thumbTintColor` if (oldSwitchProps.thumbTintColor != newSwitchProps.thumbTintColor) { - _switchView.thumbTintColor = [UIColor colorWithCGColor:newSwitchProps.thumbTintColor.get()]; + _switchView.thumbTintColor = RCTUIColorFromSharedColor(newSwitchProps.thumbTintColor); } [super updateProps:props oldProps:oldProps]; From 241795cbc4741d2de41d31f1281f38ce4b00f811 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Thu, 17 Sep 2020 11:09:42 -0700 Subject: [PATCH 010/241] Fabric: Color conversion functions now do not depend on internal implementations of SharedColor Summary: This diff changes the implementation of `RCTCreateCGColorRefFromSharedColor` and `RCTUIColorFromSharedColor` in such a way that they don't rely on the fact that SharedColor is actually a `shared_ptr`. Instead, the methods just extract color components from SharedColor and create UIColor and CGColorRef objects on demand. This allows us to change the implementation of SharedColor without worrying much about the rest of the system, which will do in the next diff. Changelog: [Internal] Fabric-specific internal change. Reviewed By: JoshuaGross Differential Revision: D23753510 fbshipit-source-id: 340127527888776ebd5d241ed60c7e5220564013 --- React/Fabric/RCTConversions.h | 9 +++++++-- .../platform/ios/RCTTextPrimitivesConversions.h | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/React/Fabric/RCTConversions.h b/React/Fabric/RCTConversions.h index 5caafef9567210..73e6127bae6774 100644 --- a/React/Fabric/RCTConversions.h +++ b/React/Fabric/RCTConversions.h @@ -37,13 +37,18 @@ inline std::string RCTStringFromNSString(NSString *string) inline UIColor *_Nullable RCTUIColorFromSharedColor(const facebook::react::SharedColor &sharedColor) { - return sharedColor ? [UIColor colorWithCGColor:sharedColor.get()] : nil; + if (!sharedColor) { + return nil; + } + + auto components = facebook::react::colorComponentsFromColor(sharedColor); + return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha]; } inline CF_RETURNS_RETAINED CGColorRef RCTCreateCGColorRefFromSharedColor(const facebook::react::SharedColor &sharedColor) { - return sharedColor ? CGColorCreateCopy(sharedColor.get()) : nil; + return CGColorRetain(RCTUIColorFromSharedColor(sharedColor).CGColor); } inline CGPoint RCTCGPointFromPoint(const facebook::react::Point &point) diff --git a/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextPrimitivesConversions.h b/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextPrimitivesConversions.h index fdfb6e2ce61c68..b5bbbdb1572d96 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextPrimitivesConversions.h +++ b/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextPrimitivesConversions.h @@ -96,7 +96,12 @@ inline static NSUnderlineStyle RCTNSUnderlineStyleFromStyleAndPattern( return style; } -inline static UIColor *RCTUIColorFromSharedColor(const SharedColor &color) +inline static UIColor *RCTUIColorFromSharedColor(const SharedColor &sharedColor) { - return color ? [UIColor colorWithCGColor:color.get()] : nil; + if (!sharedColor) { + return nil; + } + + auto components = colorComponentsFromColor(sharedColor); + return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha]; } From e4877ed9859f6d556d8ec1ce288a3ebaaab12ff2 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Thu, 17 Sep 2020 11:09:42 -0700 Subject: [PATCH 011/241] Fabric: Using `optional` instead of `CGColorRef` on iOS Summary: Finally, this diff changes the internal implementation of SharedColor to be `optional`. Initially, when we started working on the new renderer, it seemed like a good idea to allocated CGColor objects ahead of time and store them with Props. Now, this idea does not look so good, especially because: * Having `SharedColor` as a `shared_ptr` is a quite big perf overhead for copying this thing. And the size of the object is not small. * Having `SharedColor` as a `shared_ptr` creates huge interconnectedness among pieces of the core and rendering. E.g. improper releasing a pointer in some component view can cause a crash somewhere in the core (because both places might use the same shared `blackColor`. On Android, we already use simple `int` as a representation of a color, and this works great. And this diff implements something very similar to Android, but a bit differently: here we use `optional` instead of custom class with a single `int` field and some magic value that represents "empty value". This approach should fix T75836417 because now we don't have allocation and deallocation when we simply assign color values. If this solution works fine on iOS, I will do unify all implementations among all platforms. Changelog: [Internal] Fabric-specific internal change. Reviewed By: JoshuaGross Differential Revision: D23753507 fbshipit-source-id: 42fd6cee6bf7b39c92c88536da06ba9e8cf4d4db --- ReactCommon/react/renderer/graphics/BUCK | 2 ++ .../renderer/graphics/platform/ios/Color.cpp | 33 ++++++++----------- .../renderer/graphics/platform/ios/Color.h | 8 ++--- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/ReactCommon/react/renderer/graphics/BUCK b/ReactCommon/react/renderer/graphics/BUCK index 9b7fd05c2d0b46..87064ce89c0436 100644 --- a/ReactCommon/react/renderer/graphics/BUCK +++ b/ReactCommon/react/renderer/graphics/BUCK @@ -7,6 +7,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", ) @@ -95,6 +96,7 @@ rn_xplat_cxx_library( tests = [":tests"], visibility = ["PUBLIC"], deps = [ + react_native_xplat_target("better:better"), "//third-party/glog:glog", "//xplat/fbsystrace:fbsystrace", "//xplat/folly:headers_only", diff --git a/ReactCommon/react/renderer/graphics/platform/ios/Color.cpp b/ReactCommon/react/renderer/graphics/platform/ios/Color.cpp index 8cbdeee7fdc60b..0b114759cb1aa0 100644 --- a/ReactCommon/react/renderer/graphics/platform/ios/Color.cpp +++ b/ReactCommon/react/renderer/graphics/platform/ios/Color.cpp @@ -12,28 +12,21 @@ namespace facebook { namespace react { SharedColor colorFromComponents(ColorComponents components) { - const CGFloat componentsArray[] = { - components.red, components.green, components.blue, components.alpha}; - - auto color = CGColorCreate(CGColorSpaceCreateDeviceRGB(), componentsArray); - - return SharedColor(color, CGColorRelease); + float ratio = 255.9999; + return SharedColor( + ((int)(components.alpha * ratio) & 0xff) << 24 | + ((int)(components.red * ratio) & 0xff) << 16 | + ((int)(components.green * ratio) & 0xff) << 8 | + ((int)(components.blue * ratio) & 0xff)); } -ColorComponents colorComponentsFromColor(SharedColor color) { - if (!color) { - // Empty color object can be considered as `clear` (black, fully - // transparent) color. - return ColorComponents{0, 0, 0, 0}; - } - - auto numberOfComponents __unused = CGColorGetNumberOfComponents(color.get()); - assert(numberOfComponents == 4); - const CGFloat *components = CGColorGetComponents(color.get()); - return ColorComponents{(float)components[0], - (float)components[1], - (float)components[2], - (float)components[3]}; +ColorComponents colorComponentsFromColor(SharedColor sharedColor) { + float ratio = 256; + Color color = *sharedColor; + return ColorComponents{(float)((color >> 16) & 0xff) / ratio, + (float)((color >> 8) & 0xff) / ratio, + (float)((color >> 0) & 0xff) / ratio, + (float)((color >> 24) & 0xff) / ratio}; } SharedColor clearColor() { diff --git a/ReactCommon/react/renderer/graphics/platform/ios/Color.h b/ReactCommon/react/renderer/graphics/platform/ios/Color.h index abaf98f4245e56..0b8ab406b40f50 100644 --- a/ReactCommon/react/renderer/graphics/platform/ios/Color.h +++ b/ReactCommon/react/renderer/graphics/platform/ios/Color.h @@ -7,17 +7,17 @@ #pragma once -#include +#include -#include #include #include namespace facebook { namespace react { -using Color = CGColor; -using SharedColor = std::shared_ptr; +using Color = int32_t; + +using SharedColor = better::optional; SharedColor colorFromComponents(ColorComponents components); ColorComponents colorComponentsFromColor(SharedColor color); From 5b93c49e76498cfe1ecaf8a95abcb044946ce197 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Thu, 17 Sep 2020 11:09:42 -0700 Subject: [PATCH 012/241] Fabric: Using pre-cached UIColor objects for black, white, and clear colors Summary: This change maps the three most used colors (black, white, clear) to corresponding predefined values in UIColor. This should meaningfully reduce the overall amount of allocated UIColor/CGColor objects. In my non-scientific measures, it reduces the number of CGColor objects from ~1500 to ~1000. And... it no much at least in terms of kilobytes. However, I still think it's a good idea to implement this because I hope that can remove some work from memory allocation infra and maybe enable some optimizations that UIKit hopefully does for black and white colors. (I tend to believe that this optimization exists because UIKit even has a classes called UIDeviceWhiteColor and UICachedDeviceWhiteColor.) Changelog: [Internal] Fabric-specific internal change. Reviewed By: JoshuaGross Differential Revision: D23753506 fbshipit-source-id: 46e58dc7e6b0dcab3c83d29c7257c90ffbd95246 --- React/Fabric/RCTConversions.h | 14 +++++++++++++- .../platform/ios/RCTTextPrimitivesConversions.h | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/React/Fabric/RCTConversions.h b/React/Fabric/RCTConversions.h index 73e6127bae6774..222e9ba85005b2 100644 --- a/React/Fabric/RCTConversions.h +++ b/React/Fabric/RCTConversions.h @@ -35,12 +35,24 @@ inline std::string RCTStringFromNSString(NSString *string) return std::string{string.UTF8String ?: ""}; } -inline UIColor *_Nullable RCTUIColorFromSharedColor(const facebook::react::SharedColor &sharedColor) +inline UIColor *_Nullable RCTUIColorFromSharedColor(facebook::react::SharedColor const &sharedColor) { if (!sharedColor) { return nil; } + if (*facebook::react::clearColor() == *sharedColor) { + return [UIColor clearColor]; + } + + if (*facebook::react::blackColor() == *sharedColor) { + return [UIColor blackColor]; + } + + if (*facebook::react::whiteColor() == *sharedColor) { + return [UIColor whiteColor]; + } + auto components = facebook::react::colorComponentsFromColor(sharedColor); return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha]; } diff --git a/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextPrimitivesConversions.h b/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextPrimitivesConversions.h index b5bbbdb1572d96..c78daec7b7a3d5 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextPrimitivesConversions.h +++ b/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextPrimitivesConversions.h @@ -102,6 +102,18 @@ inline static UIColor *RCTUIColorFromSharedColor(const SharedColor &sharedColor) return nil; } - auto components = colorComponentsFromColor(sharedColor); + if (*facebook::react::clearColor() == *sharedColor) { + return [UIColor clearColor]; + } + + if (*facebook::react::blackColor() == *sharedColor) { + return [UIColor blackColor]; + } + + if (*facebook::react::whiteColor() == *sharedColor) { + return [UIColor whiteColor]; + } + + auto components = facebook::react::colorComponentsFromColor(sharedColor); return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha]; } From e7dbd845e6682a2e46114039ef6e1886482d9ecd Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Thu, 17 Sep 2020 11:20:38 -0700 Subject: [PATCH 013/241] Coalesce touchMove events Summary: Changelog: [Internal] To align more closely with Paper, Fabric should coalesce touchMove events. on iOS it happens: https://www.internalfb.com/intern/diffusion/FBS/browsefile/master/xplat/js/react-native-github/React/Base/RCTTouchEvent.m?lines=43 Reviewed By: JoshuaGross Differential Revision: D23734212 fbshipit-source-id: a9d324a6481884905d7be6637fcafe4e71f2bd9f --- .../react/renderer/components/view/TouchEventEmitter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp b/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp index 4b582c6ba47bb3..738dec8d6467c7 100644 --- a/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp +++ b/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp @@ -68,7 +68,9 @@ void TouchEventEmitter::onTouchStart(TouchEvent const &event) const { } void TouchEventEmitter::onTouchMove(TouchEvent const &event) const { - dispatchTouchEvent("touchMove", event, EventPriority::AsynchronousBatched); + dispatchUniqueEvent("touchMove", [event](jsi::Runtime &runtime) { + return touchEventPayload(runtime, event); + }); } void TouchEventEmitter::onTouchEnd(TouchEvent const &event) const { From 012ac09fa17e5b437c68a46200333e203895b9b1 Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Thu, 17 Sep 2020 12:25:46 -0700 Subject: [PATCH 014/241] Fix LayoutAnimations assertion on `adjustDelayedMutationIndicesForMutation` Summary: `adjustDelayedMutationIndicesForMutation` asserts that the mutation is either Remove or Insert. At one callsite, we weren't checking the mutation type before calling `adjustDelayedMutationIndicesForMutation`. Changelog: [Internal] Reviewed By: shergin Differential Revision: D23746617 fbshipit-source-id: 6046fec2eb4821094937b1b16f40347bbc55c20e --- .../renderer/animations/LayoutAnimationKeyFrameManager.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp index 66bcab91d50292..a6f552186fefbc 100644 --- a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp +++ b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp @@ -1253,7 +1253,10 @@ LayoutAnimationKeyFrameManager::pullTransaction( << "Adjust delayed mutations based on finalConflictingMutations"; #endif for (auto &mutation : finalConflictingMutations) { - adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + if (mutation.type == ShadowViewMutation::Remove || + mutation.type == ShadowViewMutation::Insert) { + adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + } } // Adjust keyframes based on already-delayed, existing animations, before From ad400f3cf6ea59010dbbd6fd864364b2a9fb3e68 Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Thu, 17 Sep 2020 12:25:46 -0700 Subject: [PATCH 015/241] Fix MountingCoordinator override mode Summary: In MountingCoordinator override mode (used in LayoutAnimations) we must set the start and end `diff` time when no real diff happens, otherwise we will hit an assert in telemetry later. I also ensure that the TransactionNumber is incremented in that case. Changelog: [Internal] Reviewed By: shergin Differential Revision: D23746684 fbshipit-source-id: b1fe3864e453fdba89d43cc827bd37434abf7a4d --- ReactCommon/react/renderer/mounting/MountingCoordinator.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp b/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp index 1a925ed91db653..e457f011514552 100644 --- a/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp +++ b/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp @@ -117,10 +117,13 @@ better::optional MountingCoordinator::pullTransaction() mutations = transaction->getMutations(); telemetry = transaction->getTelemetry(); } else { + number_++; telemetry.willLayout(); telemetry.didLayout(); telemetry.willCommit(); telemetry.didCommit(); + telemetry.willDiff(); + telemetry.didDiff(); } transaction = mountingOverrideDelegate->pullTransaction( From b64a6618d679c3a2922d3d68983352b387c87fff Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Thu, 17 Sep 2020 12:25:46 -0700 Subject: [PATCH 016/241] Fix MountingCoordinator RN_SHADOW_TREE_INTROSPECTION + LayoutAnimations Summary: Currently, MountingCoordinator's RN_SHADOW_TREE_INTROSPECTION code will crash often because it assumes there is always a "new" tree to compare the old tree to. In the LayoutAnimations context this is not always the case - in fact, the majority of the time, LayoutAnimations is producing mutations for animation without a "new" tree. Just check that the tree exists before trying to print it. Changelog: [Internal] Reviewed By: shergin Differential Revision: D23747289 fbshipit-source-id: a1ba22aeae32ed8915a53bc33cdc199e8ce5128a --- .../renderer/mounting/MountingCoordinator.cpp | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp b/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp index e457f011514552..d98ed0a4a740d9 100644 --- a/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp +++ b/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp @@ -97,9 +97,6 @@ better::optional MountingCoordinator::pullTransaction() telemetry.didDiff(); - baseRevision_ = std::move(*lastRevision_); - lastRevision_.reset(); - transaction = MountingTransaction{ surfaceId_, number_, std::move(mutations), telemetry}; } @@ -142,13 +139,15 @@ better::optional MountingCoordinator::pullTransaction() // If the transaction was overridden, we don't have a model of the shadow // tree therefore we cannot validate the validity of the mutation // instructions. - if (!shouldOverridePullTransaction) { - auto line = std::string{}; - + if (!shouldOverridePullTransaction && lastRevision_.has_value()) { auto stubViewTree = - stubViewTreeFromShadowNode(*baseRevision_.rootShadowNode); + stubViewTreeFromShadowNode(*lastRevision_->rootShadowNode); + + bool treesEqual = stubViewTree_ == stubViewTree; - if (stubViewTree_ != stubViewTree) { + if (!treesEqual) { + // Display debug info + auto line = std::string{}; std::stringstream ssOldTree( baseRevision_.rootShadowNode->getDebugDescription()); while (std::getline(ssOldTree, line, '\n')) { @@ -167,13 +166,15 @@ better::optional MountingCoordinator::pullTransaction() } } - assert( - (stubViewTree_ == stubViewTree) && - "Incorrect set of mutations detected."); + assert((treesEqual) && "Incorrect set of mutations detected."); } } #endif + if (lastRevision_.has_value()) { + baseRevision_ = std::move(*lastRevision_); + lastRevision_.reset(); + } return transaction; } From 3585bb422f97390e59e728f2d0a5cd936e4beb16 Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Thu, 17 Sep 2020 12:25:46 -0700 Subject: [PATCH 017/241] Fix compilation for LayoutAnimations debug mode Summary: iOS needs this function to be marked as static. Changelog: [internal] Reviewed By: shergin Differential Revision: D23749613 fbshipit-source-id: a8c160646853450fc7d849448bdbb45e02beb964 --- .../renderer/animations/LayoutAnimationKeyFrameManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp index a6f552186fefbc..7599e85de2d62e 100644 --- a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp +++ b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp @@ -30,7 +30,8 @@ namespace facebook { namespace react { #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING -std::string GetMutationInstructionString(ShadowViewMutation const &mutation) { +static std::string GetMutationInstructionString( + ShadowViewMutation const &mutation) { bool mutationIsRemove = mutation.type == ShadowViewMutation::Type::Remove; bool mutationIsInsert = mutation.type == ShadowViewMutation::Type::Insert; bool mutationIsDelete = mutation.type == ShadowViewMutation::Type::Delete; From 0599742db83bce1ae14e7e924af1b345ca3ef62c Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Thu, 17 Sep 2020 12:25:46 -0700 Subject: [PATCH 018/241] LayoutAnimations: at the end of every animation, issue an update mutation Summary: LayoutAnimations: at the end of every animation, issue an update mutation - this is so that the props data on the Mounting layer/StubViewTree layer is pointer-identical to the props data on the ShadowTree. This unblocks iOS debug mode crashes. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D23753606 fbshipit-source-id: 407e0c2746a65e6d3ee29c1cce891cd7c1013593 --- .../animations/LayoutAnimationDriver.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp b/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp index 583176fefac4dd..c9f5b13ab0c200 100644 --- a/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp +++ b/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp @@ -191,8 +191,10 @@ void LayoutAnimationDriver::animationMutationsForFrame( // Queue up "final" mutations for all keyframes in the completed animation for (auto const &keyframe : animation.keyFrames) { - if (!keyframe.invalidated && - keyframe.finalMutationForKeyFrame.hasValue()) { + if (keyframe.invalidated) { + continue; + } + if (keyframe.finalMutationForKeyFrame.hasValue()) { auto const &finalMutationForKeyFrame = *keyframe.finalMutationForKeyFrame; PrintMutationInstruction( @@ -207,6 +209,17 @@ void LayoutAnimationDriver::animationMutationsForFrame( finalMutationForKeyFrame.oldChildShadowView, finalMutationForKeyFrame.newChildShadowView, finalMutationForKeyFrame.index}); + } else { + // Issue a final UPDATE so that the final props object sent to the + // mounting layer is the same as the one on the ShadowTree. This is + // mostly to make the MountingCoordinator StubViewTree assertions + // pass. + mutationsList.push_back( + ShadowViewMutation{ShadowViewMutation::Type::Update, + keyframe.parentView, + keyframe.viewStart, + keyframe.viewEnd, + -1}); } } From ee38751975babd8db5d21d1c0940966adccce5fe Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Thu, 17 Sep 2020 13:17:48 -0700 Subject: [PATCH 019/241] Layout Events: throttle layout events sent to same node repeatedly Summary: Under Fabric only, we can enter an infinite layout loop where the emitted layout event oscillates between two height values that are off by a very small amount. The cause is, in part, components that use layoutEvents to determine their dimensions: for example, using onLayout event "height" parameters to determine the height of a child. If the onLayout height changes rapidly, the child's height will change, causing another layout, ad infinitum. This might seem like an extreme case but there are product use-cases where this is done in non-Fabric and layout stabilizes quickly. In Fabric, currently it may never stabilize. Part of this is due to a longstanding issue that exists in Fabric and non-Fabric, that we cannot immediately fix: If in a single frame, C++ emits 100 layout events to ReactJS, ReactJS may only process 50 before committing the root. That will trigger more layout events, even though product code has only partially processed the layout events. At the next frame, the next 50 events will be processed which may again change the layout, emitting more events... etc. In most cases the tree will converge and layout values will stabilize, but in extreme cases in Fabric, it might not. Part of this is because Fabric does not drop *stale* layout events. If 10 layout events are dispatched to the same node, it will process all 10 events in older. Non-Fabric does not have this behavior, so we're changing Fabric to drop stale events when they queue up. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D23719494 fbshipit-source-id: e44a3b3e40585b59680299db3a4efdc63cdf0de8 --- .../components/view/ViewEventEmitter.cpp | 35 +++++++++++++------ .../components/view/ViewEventEmitter.h | 1 + .../renderer/uimanager/UIManagerBinding.cpp | 5 +++ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp b/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp index fe2a83f5f43d8d..e26f7c143a6dfc 100644 --- a/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp +++ b/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp @@ -46,16 +46,31 @@ void ViewEventEmitter::onLayout(const LayoutMetrics &layoutMetrics) const { lastLayoutMetrics_ = layoutMetrics; } - dispatchEvent("layout", [frame = layoutMetrics.frame](jsi::Runtime &runtime) { - auto layout = jsi::Object(runtime); - layout.setProperty(runtime, "x", frame.origin.x); - layout.setProperty(runtime, "y", frame.origin.y); - layout.setProperty(runtime, "width", frame.size.width); - layout.setProperty(runtime, "height", frame.size.height); - auto payload = jsi::Object(runtime); - payload.setProperty(runtime, "layout", std::move(layout)); - return payload; - }); + std::atomic_uint_fast8_t *eventCounter = &eventCounter_; + uint_fast8_t expectedEventCount = ++*eventCounter; + + // dispatchUniqueEvent only drops consecutive onLayout events to the same + // node. We want to drop *any* unprocessed onLayout events when there's a + // newer one. + dispatchEvent( + "layout", + [frame = layoutMetrics.frame, expectedEventCount, eventCounter]( + jsi::Runtime &runtime) { + uint_fast8_t actualEventCount = eventCounter->load(); + if (expectedEventCount != actualEventCount) { + // Drop stale events + return jsi::Value::null(); + } + + auto layout = jsi::Object(runtime); + layout.setProperty(runtime, "x", frame.origin.x); + layout.setProperty(runtime, "y", frame.origin.y); + layout.setProperty(runtime, "width", frame.size.width); + layout.setProperty(runtime, "height", frame.size.height); + auto payload = jsi::Object(runtime); + payload.setProperty(runtime, "layout", std::move(layout)); + return jsi::Value(std::move(payload)); + }); } } // namespace react diff --git a/ReactCommon/react/renderer/components/view/ViewEventEmitter.h b/ReactCommon/react/renderer/components/view/ViewEventEmitter.h index 0aa1f5e588eba2..237c9a53b1038b 100644 --- a/ReactCommon/react/renderer/components/view/ViewEventEmitter.h +++ b/ReactCommon/react/renderer/components/view/ViewEventEmitter.h @@ -40,6 +40,7 @@ class ViewEventEmitter : public TouchEventEmitter { private: mutable std::mutex layoutMetricsMutex_; mutable LayoutMetrics lastLayoutMetrics_; + mutable std::atomic_uint_fast8_t eventCounter_{0}; }; } // namespace react diff --git a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index 10a9dce3cdaa86..5c2765ca3996f9 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -135,6 +135,11 @@ void UIManagerBinding::dispatchEvent( auto payload = payloadFactory(runtime); + // If a payload is null, the factory has decided to cancel the event + if (payload.isNull()) { + return; + } + auto instanceHandle = eventTarget ? [&]() { auto instanceHandle = eventTarget->getInstanceHandle(runtime); From 9b76e217bb16935069f0ea5b60f4c4d9b73f86d6 Mon Sep 17 00:00:00 2001 From: Ramanpreet Nara Date: Thu, 17 Sep 2020 13:52:44 -0700 Subject: [PATCH 020/241] Copy all blocks generated by TurboModules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: The Apple documentation states: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Blocks/Articles/bxUsing.html#//apple_ref/doc/uid/TP40007502-CH5-SW1 > Typically, you shouldn’t need to copy (or retain) a block. You only need to make a copy when you expect the block to be used after destruction of the scope within which it was declared. Copying moves a block to the heap. All blocks generated in the TurboModule infra, for callbacks and promise resolve/reject handlers, can be used after the destruction of the scope within which they were declared. Therefore, let's copy them in the hopes that they mitigate T75876134. **Note:** We copy blocks before pushing them into the `retainedObjects` array in the legacy Infra as well. Context: D2559997 (https://github.com/facebook/react-native/commit/71da2917e577b7ec659083408cff7f9981d6600f), D5589246 (https://github.com/facebook/react-native/commit/2a6965df9063c795b3d3098c4db76e5f595ba44f) Changelog: [Internal] Reviewed By: fkgozali Differential Revision: D23764329 fbshipit-source-id: e71360977bdff74dc570bd40f0139792860f811f --- .../core/platform/ios/RCTTurboModule.mm | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm index 4e5cdc8a4ee3c6..c8950c5718fe6a 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm @@ -171,7 +171,7 @@ static int32_t getUniqueId() { auto weakWrapper = CallbackWrapper::createWeak(value.getFunction(runtime), runtime, jsInvoker); BOOL __block wrapperWasCalled = NO; - return ^(NSArray *responses) { + RCTResponseSenderBlock callback = ^(NSArray *responses) { if (wrapperWasCalled) { throw std::runtime_error("callback arg cannot be called more than once"); } @@ -194,6 +194,8 @@ static int32_t getUniqueId() wrapperWasCalled = YES; }; + + return [callback copy]; } namespace facebook { @@ -331,12 +333,11 @@ static int32_t getUniqueId() bool wasMethodSync = isMethodSync(returnType); void (^block)() = ^{ - if (!weakModule) { + id strongModule = weakModule; + if (!strongModule) { return; } - id strongModule = weakModule; - if (wasMethodSync) { TurboModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodNameStr.c_str()); } else { @@ -635,10 +636,13 @@ static int32_t getUniqueId() runtime, jsInvoker_, ^(RCTPromiseResolveBlock resolveBlock, RCTPromiseRejectBlock rejectBlock) { - [inv setArgument:(void *)&resolveBlock atIndex:count + 2]; - [inv setArgument:(void *)&rejectBlock atIndex:count + 3]; - [retainedObjectsForInvocation addObject:resolveBlock]; - [retainedObjectsForInvocation addObject:rejectBlock]; + RCTPromiseResolveBlock resolveCopy = [resolveBlock copy]; + RCTPromiseRejectBlock rejectCopy = [rejectBlock copy]; + + [inv setArgument:(void *)&resolveCopy atIndex:count + 2]; + [inv setArgument:(void *)&rejectCopy atIndex:count + 3]; + [retainedObjectsForInvocation addObject:resolveCopy]; + [retainedObjectsForInvocation addObject:rejectCopy]; // The return type becomes void in the ObjC side. performMethodInvocation(runtime, VoidKind, methodName, inv, retainedObjectsForInvocation); }) From bb003816a389b8655c53fa34444417c14516459c Mon Sep 17 00:00:00 2001 From: Xuan Huang Date: Thu, 17 Sep 2020 14:15:58 -0700 Subject: [PATCH 021/241] Upgrade Hermes dependency to 0.7.0 Summary: Use the latest published release of hermes-engine. Changelog: [Android] [Changed] - Upgraded to Hermes 0.7.0 allow-large-files Reviewed By: cpojer Differential Revision: D23725129 fbshipit-source-id: 50938433147100e97699b717d77a687fbbfe892b --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 34260b816a895d..dc045489e56b55 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "base64-js": "^1.1.2", "event-target-shim": "^5.0.1", "fbjs-scripts": "^1.1.0", - "hermes-engine": "~0.6.0", + "hermes-engine": "~0.7.0", "invariant": "^2.2.4", "jsc-android": "^245459.0.0", "metro-babel-register": "0.63.0", diff --git a/yarn.lock b/yarn.lock index 0d013d0483564c..05196e9070286f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3604,10 +3604,10 @@ has@^1.0.1, has@^1.0.3: dependencies: function-bind "^1.1.1" -hermes-engine@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.6.0.tgz#82c0738b89afb3253dcc4b322888a8bf0f52930b" - integrity sha512-WrKfVJ8zsaTz31GHuoX2rJl7AV85Y9bFQkWhqalbObwPusanSsvU+viByDXccUU3khs9CjLBTm0O6DAH3Yls8g== +hermes-engine@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.7.0.tgz#c4a13e09811d7bc975a0662bd2ca7120003d6ef8" + integrity sha512-lU9OenFWXXOzYldqn15QvbiD0kDc+uw2arhCOkR+9D+PhrLFcbEqnaXFESgchN77JYEf77KkqXncTZA8aoXw2A== hosted-git-info@^2.1.4: version "2.8.8" From c453dbc4cc9c7e009910a6fbea68e099a244e9b8 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Thu, 17 Sep 2020 14:39:38 -0700 Subject: [PATCH 022/241] Fabric: Enabling state auto-repeating for all state updates (gated) Summary: This enables a new state auto repeating mechanism built-in mechanism for all state updates which we already use for CK interop. This experiment is supposed to help with T74769670 and co. This change is gated with MC. Changelog: [Internal] Fabric-specific internal change. Reviewed By: JoshuaGross Differential Revision: D23762508 fbshipit-source-id: f535513c724ace9ede570177281324eb507329c5 --- ReactCommon/react/renderer/scheduler/Scheduler.cpp | 6 ++++++ ReactCommon/react/renderer/uimanager/UIManager.cpp | 2 +- ReactCommon/react/renderer/uimanager/UIManager.h | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/ReactCommon/react/renderer/scheduler/Scheduler.cpp index a2384dfb8ecce0..1f0c617ef8d58a 100644 --- a/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -110,11 +110,17 @@ Scheduler::Scheduler( "react_fabric:enable_reparenting_detection_android"); removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool( "react_fabric:remove_outstanding_surfaces_on_destruction_android"); + uiManager_->experimentEnableStateUpdateWithAutorepeat = + reactNativeConfig_->getBool( + "react_fabric:enable_state_update_with_autorepeat_android"); #else enableReparentingDetection_ = reactNativeConfig_->getBool( "react_fabric:enable_reparenting_detection_ios"); removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool( "react_fabric:remove_outstanding_surfaces_on_destruction_ios"); + uiManager_->experimentEnableStateUpdateWithAutorepeat = + reactNativeConfig_->getBool( + "react_fabric:enable_state_update_with_autorepeat_ios"); #endif } diff --git a/ReactCommon/react/renderer/uimanager/UIManager.cpp b/ReactCommon/react/renderer/uimanager/UIManager.cpp index 8f6c4bb0aa7ca4..e4c4cdb8f5b461 100644 --- a/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -263,7 +263,7 @@ void UIManager::updateStateWithAutorepeat( } void UIManager::updateState(StateUpdate const &stateUpdate) const { - if (stateUpdate.autorepeat) { + if (stateUpdate.autorepeat || experimentEnableStateUpdateWithAutorepeat) { updateStateWithAutorepeat(stateUpdate); return; } diff --git a/ReactCommon/react/renderer/uimanager/UIManager.h b/ReactCommon/react/renderer/uimanager/UIManager.h index e2ba6f41630551..4e53389615d04f 100644 --- a/ReactCommon/react/renderer/uimanager/UIManager.h +++ b/ReactCommon/react/renderer/uimanager/UIManager.h @@ -73,6 +73,11 @@ class UIManager final : public ShadowTreeDelegate { ShadowTree const &shadowTree, MountingCoordinator::Shared const &mountingCoordinator) const override; + /* + * Temporary flags. + */ + bool experimentEnableStateUpdateWithAutorepeat{false}; + private: friend class UIManagerBinding; friend class Scheduler; From 9b094ee77a5f82b1e5ebfe6384afb3f660f03a3d Mon Sep 17 00:00:00 2001 From: Ramanpreet Nara Date: Thu, 17 Sep 2020 16:04:40 -0700 Subject: [PATCH 023/241] Stop accessing JVM in ~JavaTurboModule Summary: Inside JavaTurboModule, the native `CallInvoker` is used to schedule work on the NativeModules thread. So, in ~JavaTurboModule(), I scheduled some work on the NativeModules thread. This work holds a copy of the JNI global reference to the Java NativeModule object, and when it's executed, it resets this global reference to the Java NativeModule object. This should ensure that the we don't access the JVM in ~JavaTurboModule, which could crash the program. I also removed the redundant `quitSynchronous()` in `~CatalystInstanceImpl()`, to prevent the NativeModules thread from being deleted before we delete the `jsi::Runtime`. This shouldn't cause an issue, because we delete the NativeModules thread when we call [ReactQueueConfigurationImpl.destroy()](https://fburl.com/codesearch/p7aurwn3). Changelog: [Internal] Reviewed By: ejanzer Differential Revision: D23744777 fbshipit-source-id: a5c8d3f2ac4287dfef9a4b4404a04b335aa0963d --- .../jni/react/jni/CatalystInstanceImpl.cpp | 6 ------ .../main/jni/react/jni/CatalystInstanceImpl.h | 1 - .../core/platform/android/JavaTurboModule.cpp | 20 +++++++++++++++++++ .../core/platform/android/JavaTurboModule.h | 1 + 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp b/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp index 7d5b1acf483f88..659c8235a64bbb 100644 --- a/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp +++ b/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp @@ -92,12 +92,6 @@ CatalystInstanceImpl::initHybrid(jni::alias_ref) { CatalystInstanceImpl::CatalystInstanceImpl() : instance_(std::make_unique()) {} -CatalystInstanceImpl::~CatalystInstanceImpl() { - if (moduleMessageQueue_ != NULL) { - moduleMessageQueue_->quitSynchronous(); - } -} - void CatalystInstanceImpl::registerNatives() { registerHybrid({ makeNativeMethod("initHybrid", CatalystInstanceImpl::initHybrid), diff --git a/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h b/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h index dd9990ef090a00..c40a691232c6a2 100644 --- a/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h +++ b/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h @@ -37,7 +37,6 @@ class CatalystInstanceImpl : public jni::HybridClass { "Lcom/facebook/react/bridge/CatalystInstanceImpl;"; static jni::local_ref initHybrid(jni::alias_ref); - ~CatalystInstanceImpl() override; static void registerNatives(); diff --git a/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.cpp b/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.cpp index 4b5d8d6e9538b6..82fbc928fb6c1a 100644 --- a/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.cpp +++ b/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.cpp @@ -31,6 +31,26 @@ JavaTurboModule::JavaTurboModule(const InitParams ¶ms) instance_(jni::make_global(params.instance)), nativeInvoker_(params.nativeInvoker) {} +JavaTurboModule::~JavaTurboModule() { + /** + * TODO(T75896241): In E2E tests, instance_ is null. Investigate why. Can we + * get rid of this null check? + */ + if (!instance_) { + return; + } + + nativeInvoker_->invokeAsync([instance = std::move(instance_)]() mutable { + /** + * Reset the global NativeModule ref on the NativeModules thread. Why: + * - ~JavaTurboModule() can be called on a non-JVM thread. If we reset the + * global ref in ~JavaTurboModule(), we might access the JVM from a + * non-JVM thread, which will crash the app. + */ + instance.reset(); + }); +} + bool JavaTurboModule::isPromiseAsyncDispatchEnabled_ = false; void JavaTurboModule::enablePromiseAsyncDispatch(bool enable) { isPromiseAsyncDispatchEnabled_ = enable; diff --git a/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.h b/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.h index 642cacb194a85b..93acda48fcff56 100644 --- a/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.h +++ b/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.h @@ -41,6 +41,7 @@ class JSI_EXPORT JavaTurboModule : public TurboModule { }; JavaTurboModule(const InitParams ¶ms); + virtual ~JavaTurboModule(); jsi::Value invokeJavaMethod( jsi::Runtime &runtime, TurboModuleMethodValueKind valueKind, From 2788ee8ba9b714d5cc6e9f215223ba0586024444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 18 Sep 2020 07:11:17 -0700 Subject: [PATCH 024/241] Improve performance logger definition and type safety Summary: The way the performance logger is defined now is very unsafe regarding type safety, as all accesses to its properties is untyped (`any`) and it uses several `mixed` types in cases that could be more refined. This migrates the creation of performance loggers to instances of a class to improve its type safety. If there's an impact in performance, it's expected to be positive. Changelog: [Internal][Changed] - Replaced object literals with class instances to create performance loggers Reviewed By: lunaleaps Differential Revision: D23758609 fbshipit-source-id: 0734742eb97d92a4a53f7b66a8ca45a2ae90946c --- .../__tests__/PerformanceLogger-test.js | 1 + .../Utilities/createPerformanceLogger.js | 378 +++++++++--------- 2 files changed, 189 insertions(+), 190 deletions(-) diff --git a/Libraries/Utilities/__tests__/PerformanceLogger-test.js b/Libraries/Utilities/__tests__/PerformanceLogger-test.js index 9af931852397ab..d39d30a818e424 100644 --- a/Libraries/Utilities/__tests__/PerformanceLogger-test.js +++ b/Libraries/Utilities/__tests__/PerformanceLogger-test.js @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. * * @format + * @flow strict-local */ 'use strict'; diff --git a/Libraries/Utilities/createPerformanceLogger.js b/Libraries/Utilities/createPerformanceLogger.js index c44d649651df85..05829e51021586 100644 --- a/Libraries/Utilities/createPerformanceLogger.js +++ b/Libraries/Utilities/createPerformanceLogger.js @@ -13,7 +13,7 @@ const Systrace = require('../Performance/Systrace'); const infoLog = require('./infoLog'); -const performanceNow = +const performanceNow: () => number = global.nativeQPLTimestamp ?? global.performance.now.bind(global.performance); type Timespan = { @@ -21,9 +21,11 @@ type Timespan = { totalTime?: number, startTime?: number, endTime?: number, - ... }; +// Extra values should be serializable primitives +type ExtraValue = number | string | boolean; + export interface IPerformanceLogger { addTimeAnnotation( key: string, @@ -43,9 +45,9 @@ export interface IPerformanceLogger { currentTimestamp(): number; getTimespans(): {[key: string]: Timespan, ...}; hasTimespan(key: string): boolean; - setExtra(key: string, value: mixed): void; - getExtras(): {[key: string]: mixed, ...}; - removeExtra(key: string): ?mixed; + setExtra(key: string, value: ExtraValue): void; + getExtras(): {[key: string]: ExtraValue, ...}; + removeExtra(key: string): ExtraValue | void; markPoint(key: string, timestamp?: number): void; getPoints(): {[key: string]: number, ...}; logEverything(): void; @@ -55,211 +57,207 @@ const _cookies: {[key: string]: number, ...} = {}; const PRINT_TO_CONSOLE: false = false; // Type as false to prevent accidentally committing `true`; -/** - * This function creates performance loggers that can be used to collect and log - * various performance data such as timespans, points and extras. - * The loggers need to have minimal overhead since they're used in production. - */ -function createPerformanceLogger(): IPerformanceLogger { - const result: IPerformanceLogger & { - _timespans: {[key: string]: Timespan, ...}, - _extras: {[key: string]: mixed, ...}, - _points: {[key: string]: number, ...}, - ... - } = { - _timespans: {}, - _extras: {}, - _points: {}, - - addTimeAnnotation(key: string, durationInMs: number, description?: string) { - if (this._timespans[key]) { - if (PRINT_TO_CONSOLE && __DEV__) { - infoLog( - 'PerformanceLogger: Attempting to add a timespan that already exists ', - key, - ); - } - return; - } +class PerformanceLogger implements IPerformanceLogger { + _timespans: {[key: string]: Timespan} = {}; + _extras: {[key: string]: ExtraValue} = {}; + _points: {[key: string]: number} = {}; - this._timespans[key] = { - description: description, - totalTime: durationInMs, - }; - }, - - addTimespan( - key: string, - startTime: number, - endTime: number, - description?: string, - ) { - if (this._timespans[key]) { - if (PRINT_TO_CONSOLE && __DEV__) { - infoLog( - 'PerformanceLogger: Attempting to add a timespan that already exists ', - key, - ); - } - return; + addTimeAnnotation(key: string, durationInMs: number, description?: string) { + if (this._timespans[key]) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog( + 'PerformanceLogger: Attempting to add a timespan that already exists ', + key, + ); } + return; + } - this._timespans[key] = { - description, - startTime, - endTime, - totalTime: endTime - (startTime || 0), - }; - }, - - startTimespan(key: string, description?: string) { - if (this._timespans[key]) { - if (PRINT_TO_CONSOLE && __DEV__) { - infoLog( - 'PerformanceLogger: Attempting to start a timespan that already exists ', - key, - ); - } - return; + this._timespans[key] = { + description: description, + totalTime: durationInMs, + }; + } + + addTimespan( + key: string, + startTime: number, + endTime: number, + description?: string, + ) { + if (this._timespans[key]) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog( + 'PerformanceLogger: Attempting to add a timespan that already exists ', + key, + ); } + return; + } + + this._timespans[key] = { + description, + startTime, + endTime, + totalTime: endTime - (startTime || 0), + }; + } - this._timespans[key] = { - description: description, - startTime: performanceNow(), - }; - _cookies[key] = Systrace.beginAsyncEvent(key); - if (PRINT_TO_CONSOLE) { - infoLog('PerformanceLogger.js', 'start: ' + key); + startTimespan(key: string, description?: string) { + if (this._timespans[key]) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog( + 'PerformanceLogger: Attempting to start a timespan that already exists ', + key, + ); } - }, - - stopTimespan(key: string, options?: {update?: boolean}) { - const timespan = this._timespans[key]; - if (!timespan || !timespan.startTime) { - if (PRINT_TO_CONSOLE && __DEV__) { - infoLog( - 'PerformanceLogger: Attempting to end a timespan that has not started ', - key, - ); - } - return; + return; + } + + this._timespans[key] = { + description: description, + startTime: performanceNow(), + }; + _cookies[key] = Systrace.beginAsyncEvent(key); + if (PRINT_TO_CONSOLE) { + infoLog('PerformanceLogger.js', 'start: ' + key); + } + } + + stopTimespan(key: string, options?: {update?: boolean}) { + const timespan = this._timespans[key]; + if (!timespan || timespan.startTime == null) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog( + 'PerformanceLogger: Attempting to end a timespan that has not started ', + key, + ); } - if (timespan.endTime && !options?.update) { - if (PRINT_TO_CONSOLE && __DEV__) { - infoLog( - 'PerformanceLogger: Attempting to end a timespan that has already ended ', - key, - ); - } - return; + return; + } + if (timespan.endTime != null && !options?.update) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog( + 'PerformanceLogger: Attempting to end a timespan that has already ended ', + key, + ); } + return; + } + + timespan.endTime = performanceNow(); + timespan.totalTime = timespan.endTime - (timespan.startTime || 0); + if (PRINT_TO_CONSOLE) { + infoLog('PerformanceLogger.js', 'end: ' + key); + } + + if (_cookies[key] != null) { + Systrace.endAsyncEvent(key, _cookies[key]); + delete _cookies[key]; + } + } - timespan.endTime = performanceNow(); - timespan.totalTime = timespan.endTime - (timespan.startTime || 0); - if (PRINT_TO_CONSOLE) { - infoLog('PerformanceLogger.js', 'end: ' + key); + clear() { + this._timespans = {}; + this._extras = {}; + this._points = {}; + if (PRINT_TO_CONSOLE) { + infoLog('PerformanceLogger.js', 'clear'); + } + } + + clearCompleted() { + for (const key in this._timespans) { + if (this._timespans[key].totalTime != null) { + delete this._timespans[key]; } + } + this._extras = {}; + this._points = {}; + if (PRINT_TO_CONSOLE) { + infoLog('PerformanceLogger.js', 'clearCompleted'); + } + } + + currentTimestamp() { + return performanceNow(); + } + + getTimespans() { + return this._timespans; + } - if (_cookies[key] != null) { - Systrace.endAsyncEvent(key, _cookies[key]); - delete _cookies[key]; + hasTimespan(key: string) { + return !!this._timespans[key]; + } + + setExtra(key: string, value: ExtraValue) { + if (this._extras.hasOwnProperty(key)) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog( + 'PerformanceLogger: Attempting to set an extra that already exists ', + {key, currentValue: this._extras[key], attemptedValue: value}, + ); } - }, - - clear() { - this._timespans = {}; - this._extras = {}; - this._points = {}; - if (PRINT_TO_CONSOLE) { - infoLog('PerformanceLogger.js', 'clear'); + return; + } + this._extras[key] = value; + } + + getExtras() { + return this._extras; + } + + removeExtra(key: string): ExtraValue | void { + const value = this._extras[key]; + delete this._extras[key]; + return value; + } + + markPoint(key: string, timestamp?: number) { + if (this._points[key]) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog( + 'PerformanceLogger: Attempting to mark a point that has been already logged ', + key, + ); } - }, + return; + } + this._points[key] = timestamp ?? performanceNow(); + } + + getPoints() { + return this._points; + } - clearCompleted() { + logEverything() { + if (PRINT_TO_CONSOLE) { + // log timespans for (const key in this._timespans) { - if (this._timespans[key].totalTime) { - delete this._timespans[key]; - } - } - this._extras = {}; - this._points = {}; - if (PRINT_TO_CONSOLE) { - infoLog('PerformanceLogger.js', 'clearCompleted'); - } - }, - - currentTimestamp() { - return performanceNow(); - }, - - getTimespans() { - return this._timespans; - }, - - hasTimespan(key: string) { - return !!this._timespans[key]; - }, - - setExtra(key: string, value: mixed) { - if (this._extras[key]) { - if (PRINT_TO_CONSOLE && __DEV__) { - infoLog( - 'PerformanceLogger: Attempting to set an extra that already exists ', - {key, currentValue: this._extras[key], attemptedValue: value}, - ); + if (this._timespans[key].totalTime != null) { + infoLog(key + ': ' + this._timespans[key].totalTime + 'ms'); } - return; } - this._extras[key] = value; - }, - - getExtras() { - return this._extras; - }, - - removeExtra(key: string): ?mixed { - const value = this._extras[key]; - delete this._extras[key]; - return value; - }, - - markPoint(key: string, timestamp?: number) { - if (this._points[key]) { - if (PRINT_TO_CONSOLE && __DEV__) { - infoLog( - 'PerformanceLogger: Attempting to mark a point that has been already logged ', - key, - ); - } - return; - } - this._points[key] = timestamp ?? performanceNow(); - }, - - getPoints() { - return this._points; - }, - - logEverything() { - if (PRINT_TO_CONSOLE) { - // log timespans - for (const key in this._timespans) { - if (this._timespans[key].totalTime) { - infoLog(key + ': ' + this._timespans[key].totalTime + 'ms'); - } - } - // log extras - infoLog(this._extras); + // log extras + infoLog(this._extras); - // log points - for (const key in this._points) { - infoLog(key + ': ' + this._points[key] + 'ms'); - } + // log points + for (const key in this._points) { + infoLog(key + ': ' + this._points[key] + 'ms'); } - }, - }; - return result; + } + } +} + +/** + * This function creates performance loggers that can be used to collect and log + * various performance data such as timespans, points and extras. + * The loggers need to have minimal overhead since they're used in production. + */ +function createPerformanceLogger(): IPerformanceLogger { + return new PerformanceLogger(); } module.exports = createPerformanceLogger; From 4d842963fa1ec17884355738fd7edca0f8c77997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 18 Sep 2020 07:11:17 -0700 Subject: [PATCH 025/241] Remove unused timespan descriptions from performance loggers Summary: The `description` parameter is never used so we can simplify the API. Changelog: [Internal][Changed] Removed `description` option from performance logger timespans Reviewed By: lunaleaps Differential Revision: D23758829 fbshipit-source-id: 10900f86effc3e1f54a408cf8f9fbc9b3b52f569 --- .../__tests__/PerformanceLogger-test.js | 6 ++-- .../Utilities/createPerformanceLogger.js | 30 ++++--------------- 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/Libraries/Utilities/__tests__/PerformanceLogger-test.js b/Libraries/Utilities/__tests__/PerformanceLogger-test.js index d39d30a818e424..651c6f0f47ae6b 100644 --- a/Libraries/Utilities/__tests__/PerformanceLogger-test.js +++ b/Libraries/Utilities/__tests__/PerformanceLogger-test.js @@ -55,10 +55,8 @@ describe('PerformanceLogger', () => { let perfLogger = createPerformanceLogger(); const startTime = 0; const endTime = 100; - const description = 'description'; - perfLogger.addTimespan(TIMESPAN_1, startTime, endTime, description); + perfLogger.addTimespan(TIMESPAN_1, startTime, endTime); expect(perfLogger.getTimespans()[TIMESPAN_1]).toEqual({ - description, startTime, endTime, totalTime: endTime - startTime, @@ -70,7 +68,7 @@ describe('PerformanceLogger', () => { perfLogger.startTimespan(TIMESPAN_1); perfLogger.stopTimespan(TIMESPAN_1); const existing = perfLogger.getTimespans()[TIMESPAN_1]; - perfLogger.addTimespan(TIMESPAN_1, 0, 100, 'overriding'); + perfLogger.addTimespan(TIMESPAN_1, 0, 100); expect(perfLogger.getTimespans()[TIMESPAN_1]).toEqual(existing); }); diff --git a/Libraries/Utilities/createPerformanceLogger.js b/Libraries/Utilities/createPerformanceLogger.js index 05829e51021586..643493b936fc67 100644 --- a/Libraries/Utilities/createPerformanceLogger.js +++ b/Libraries/Utilities/createPerformanceLogger.js @@ -17,7 +17,6 @@ const performanceNow: () => number = global.nativeQPLTimestamp ?? global.performance.now.bind(global.performance); type Timespan = { - description?: string, totalTime?: number, startTime?: number, endTime?: number, @@ -27,18 +26,9 @@ type Timespan = { type ExtraValue = number | string | boolean; export interface IPerformanceLogger { - addTimeAnnotation( - key: string, - durationInMs: number, - description?: string, - ): void; - addTimespan( - key: string, - startTime: number, - endTime: number, - description?: string, - ): void; - startTimespan(key: string, description?: string): void; + addTimeAnnotation(key: string, durationInMs: number): void; + addTimespan(key: string, startTime: number, endTime: number): void; + startTimespan(key: string): void; stopTimespan(key: string, options?: {update?: boolean}): void; clear(): void; clearCompleted(): void; @@ -62,7 +52,7 @@ class PerformanceLogger implements IPerformanceLogger { _extras: {[key: string]: ExtraValue} = {}; _points: {[key: string]: number} = {}; - addTimeAnnotation(key: string, durationInMs: number, description?: string) { + addTimeAnnotation(key: string, durationInMs: number) { if (this._timespans[key]) { if (PRINT_TO_CONSOLE && __DEV__) { infoLog( @@ -74,17 +64,11 @@ class PerformanceLogger implements IPerformanceLogger { } this._timespans[key] = { - description: description, totalTime: durationInMs, }; } - addTimespan( - key: string, - startTime: number, - endTime: number, - description?: string, - ) { + addTimespan(key: string, startTime: number, endTime: number) { if (this._timespans[key]) { if (PRINT_TO_CONSOLE && __DEV__) { infoLog( @@ -96,14 +80,13 @@ class PerformanceLogger implements IPerformanceLogger { } this._timespans[key] = { - description, startTime, endTime, totalTime: endTime - (startTime || 0), }; } - startTimespan(key: string, description?: string) { + startTimespan(key: string) { if (this._timespans[key]) { if (PRINT_TO_CONSOLE && __DEV__) { infoLog( @@ -115,7 +98,6 @@ class PerformanceLogger implements IPerformanceLogger { } this._timespans[key] = { - description: description, startTime: performanceNow(), }; _cookies[key] = Systrace.beginAsyncEvent(key); From fc4f667cdeb024cf5b251964166d0f642dcd1153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 18 Sep 2020 07:11:17 -0700 Subject: [PATCH 026/241] Remove unnecessary addTimeAnnotation method from performance logger Summary: Changelog: [Internal][Removed] Removed `addTimeAnnotation` method from performance loggers Reviewed By: lunaleaps Differential Revision: D23758816 fbshipit-source-id: 98e0abae25266f3dcc5953f25f20cde8e3dac190 --- .../__tests__/PerformanceLogger-test.js | 16 ++++++-------- .../Utilities/createPerformanceLogger.js | 21 ++----------------- 2 files changed, 8 insertions(+), 29 deletions(-) diff --git a/Libraries/Utilities/__tests__/PerformanceLogger-test.js b/Libraries/Utilities/__tests__/PerformanceLogger-test.js index 651c6f0f47ae6b..ea940f10947785 100644 --- a/Libraries/Utilities/__tests__/PerformanceLogger-test.js +++ b/Libraries/Utilities/__tests__/PerformanceLogger-test.js @@ -15,8 +15,6 @@ import createPerformanceLogger from '../createPerformanceLogger'; import type {IPerformanceLogger} from '../createPerformanceLogger'; const TIMESPAN_1 = ''; -const TIMESPAN_2 = ''; -const TIMESPAN_2_DURATION = 123; const EXTRA_KEY = ''; const EXTRA_VALUE = ''; const EXTRA_VALUE_2 = ''; @@ -29,16 +27,16 @@ describe('PerformanceLogger', () => { GlobalPerformanceLogger.clear(); }); - it('starts & stops and adds a timespan', () => { + it('starts & stops a timespan', () => { let perfLogger = createPerformanceLogger(); perfLogger.startTimespan(TIMESPAN_1); perfLogger.stopTimespan(TIMESPAN_1); - perfLogger.addTimeAnnotation(TIMESPAN_2, TIMESPAN_2_DURATION); expect(perfLogger.hasTimespan(TIMESPAN_1)).toBe(true); - expect(perfLogger.hasTimespan(TIMESPAN_2)).toBe(true); - expect(perfLogger.getTimespans()[TIMESPAN_2].totalTime).toBe( - TIMESPAN_2_DURATION, - ); + expect(perfLogger.getTimespans()[TIMESPAN_1]).toEqual({ + startTime: expect.any(Number), + endTime: expect.any(Number), + totalTime: expect.any(Number), + }); }); it('does not override a timespan', () => { @@ -47,8 +45,6 @@ describe('PerformanceLogger', () => { let old = perfLogger.getTimespans()[TIMESPAN_1]; perfLogger.startTimespan(TIMESPAN_1); expect(perfLogger.getTimespans()[TIMESPAN_1]).toBe(old); - perfLogger.addTimeAnnotation(TIMESPAN_1, 1); - expect(perfLogger.getTimespans()[TIMESPAN_1]).toBe(old); }); it('adds a timespan with start and end timestamps', () => { diff --git a/Libraries/Utilities/createPerformanceLogger.js b/Libraries/Utilities/createPerformanceLogger.js index 643493b936fc67..e98b516be35dd6 100644 --- a/Libraries/Utilities/createPerformanceLogger.js +++ b/Libraries/Utilities/createPerformanceLogger.js @@ -17,16 +17,15 @@ const performanceNow: () => number = global.nativeQPLTimestamp ?? global.performance.now.bind(global.performance); type Timespan = { - totalTime?: number, - startTime?: number, + startTime: number, endTime?: number, + totalTime?: number, }; // Extra values should be serializable primitives type ExtraValue = number | string | boolean; export interface IPerformanceLogger { - addTimeAnnotation(key: string, durationInMs: number): void; addTimespan(key: string, startTime: number, endTime: number): void; startTimespan(key: string): void; stopTimespan(key: string, options?: {update?: boolean}): void; @@ -52,22 +51,6 @@ class PerformanceLogger implements IPerformanceLogger { _extras: {[key: string]: ExtraValue} = {}; _points: {[key: string]: number} = {}; - addTimeAnnotation(key: string, durationInMs: number) { - if (this._timespans[key]) { - if (PRINT_TO_CONSOLE && __DEV__) { - infoLog( - 'PerformanceLogger: Attempting to add a timespan that already exists ', - key, - ); - } - return; - } - - this._timespans[key] = { - totalTime: durationInMs, - }; - } - addTimespan(key: string, startTime: number, endTime: number) { if (this._timespans[key]) { if (PRINT_TO_CONSOLE && __DEV__) { From f1b84ddf2c21c619b962430eebe2d213bc15da38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 18 Sep 2020 07:11:17 -0700 Subject: [PATCH 027/241] Remove update option from stopTimestamp method in performance loggers Summary: Changelog: [Internal][Changed] Removed `update` option from `stopTimestamp` method in performance loggers Reviewed By: lunaleaps Differential Revision: D23759138 fbshipit-source-id: bb83b6f5ff2f640733c2e508779b3bc52800e4f6 --- Libraries/Utilities/createPerformanceLogger.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Libraries/Utilities/createPerformanceLogger.js b/Libraries/Utilities/createPerformanceLogger.js index e98b516be35dd6..c9baf0cd07aa90 100644 --- a/Libraries/Utilities/createPerformanceLogger.js +++ b/Libraries/Utilities/createPerformanceLogger.js @@ -28,7 +28,7 @@ type ExtraValue = number | string | boolean; export interface IPerformanceLogger { addTimespan(key: string, startTime: number, endTime: number): void; startTimespan(key: string): void; - stopTimespan(key: string, options?: {update?: boolean}): void; + stopTimespan(key: string): void; clear(): void; clearCompleted(): void; currentTimestamp(): number; @@ -89,7 +89,7 @@ class PerformanceLogger implements IPerformanceLogger { } } - stopTimespan(key: string, options?: {update?: boolean}) { + stopTimespan(key: string) { const timespan = this._timespans[key]; if (!timespan || timespan.startTime == null) { if (PRINT_TO_CONSOLE && __DEV__) { @@ -100,7 +100,7 @@ class PerformanceLogger implements IPerformanceLogger { } return; } - if (timespan.endTime != null && !options?.update) { + if (timespan.endTime != null) { if (PRINT_TO_CONSOLE && __DEV__) { infoLog( 'PerformanceLogger: Attempting to end a timespan that has already ended ', From d1b695d343988b036137b50d4ceef8c0d0f8ba66 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Fri, 18 Sep 2020 07:42:23 -0700 Subject: [PATCH 028/241] Propagate nativeID in createAnimatedComponent Summary: Changelog: [internal] https://our.intern.facebook.com/intern/diffusion/FBS/browse/master/xplat/js/react-native-github/Libraries/Animated/createAnimatedComponent.js?commit=1b6ce6c3a69a&lines=82-112 `_isFabric` in `createAnimatedComponent` returns false for Fabric component, that's why nativeID was not being assigned and view got flattened. To fix this, props.nativeID is propagated. SnackBar already has nativeID https://our.intern.facebook.com/intern/diffusion/FBS/browse/master/xplat/js/RKJSModules/Libraries/FDS/FDSLightweightFeedback/DEPRECATED_FDSSnackBar.js?commit=1b6ce6c3a69a&lines=277 Reviewed By: PeteTheHeat Differential Revision: D23757304 fbshipit-source-id: 9e4b4599c95b8af8767793bc8cdce717a347a273 --- Libraries/Animated/createAnimatedComponent.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Libraries/Animated/createAnimatedComponent.js b/Libraries/Animated/createAnimatedComponent.js index d5dc89b7f81f8d..ccc789c8037b46 100644 --- a/Libraries/Animated/createAnimatedComponent.js +++ b/Libraries/Animated/createAnimatedComponent.js @@ -224,7 +224,8 @@ function createAnimatedComponent( style={mergedStyle} ref={this._setComponentRef} nativeID={ - this._isFabric() ? 'animatedComponent' : undefined + props.nativeID ?? + (this._isFabric() ? 'animatedComponent' : undefined) } /* TODO: T68258846. */ // The native driver updates views directly through the UI thread so we // have to make sure the view doesn't get optimized away because it cannot From f72c6f23cf0b23a3fa2c857e6ceafa6226ee741b Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Fri, 18 Sep 2020 09:58:49 -0700 Subject: [PATCH 029/241] Reintroduce experiment flag for Reparenting/Flattening Differ Summary: This flag was deleted in D23374948 (https://github.com/facebook/react-native/commit/6729a3e0bfc01119c8513dfcbb1f5fbe5fe81263), reintroduce it. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D23771273 fbshipit-source-id: ae9595194bf14bc740d05b2ca6e7b5e22bdd566f --- ReactCommon/react/renderer/mounting/MountingCoordinator.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp b/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp index d98ed0a4a740d9..2c9af008a80447 100644 --- a/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp +++ b/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp @@ -93,7 +93,9 @@ better::optional MountingCoordinator::pullTransaction() telemetry.willDiff(); auto mutations = calculateShadowViewMutations( - *baseRevision_.rootShadowNode, *lastRevision_->rootShadowNode); + *baseRevision_.rootShadowNode, + *lastRevision_->rootShadowNode, + enableReparentingDetection_); telemetry.didDiff(); From 22804a6144f1d7f921ed15258cb8dd926225d5cd Mon Sep 17 00:00:00 2001 From: Riley Dulin Date: Fri, 18 Sep 2020 10:33:50 -0700 Subject: [PATCH 030/241] Add cause to jsi::instrumentation::collectGarbage Summary: Continuing the adding of a "cause" field for logging to GCs. This allows embedders of Hermes (such as React Native) to specify the cause of a call to `collectGarbage`. Notably, this allows Hermes to know when a GC is triggered by a memory warning. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D23742099 fbshipit-source-id: 99453e632328c00045b92a72f789d41c898dc518 --- ReactCommon/jsi/jsi/decorator.h | 4 ++-- ReactCommon/jsi/jsi/instrumentation.h | 6 ++++-- ReactCommon/jsi/jsi/jsi.cpp | 2 +- ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ReactCommon/jsi/jsi/decorator.h b/ReactCommon/jsi/jsi/decorator.h index 46e7414abbecb1..bef9796dc5b8af 100644 --- a/ReactCommon/jsi/jsi/decorator.h +++ b/ReactCommon/jsi/jsi/decorator.h @@ -331,8 +331,8 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { return plain().instrumentation().getHeapInfo(includeExpensive); } - void collectGarbage() override { - plain().instrumentation().collectGarbage(); + void collectGarbage(std::string cause) override { + plain().instrumentation().collectGarbage(std::move(cause)); } void startTrackingHeapObjectStackTraces() override { diff --git a/ReactCommon/jsi/jsi/instrumentation.h b/ReactCommon/jsi/jsi/instrumentation.h index 04c76ce2594d62..b8aa4da02b2366 100644 --- a/ReactCommon/jsi/jsi/instrumentation.h +++ b/ReactCommon/jsi/jsi/instrumentation.h @@ -49,8 +49,10 @@ class JSI_EXPORT Instrumentation { virtual std::unordered_map getHeapInfo( bool includeExpensive) = 0; - /// perform a full garbage collection - virtual void collectGarbage() = 0; + /// Perform a full garbage collection. + /// \param cause The cause of this collection, as it should be reported in + /// logs. + virtual void collectGarbage(std::string cause) = 0; /// Start capturing JS stack-traces for all JS heap allocated objects. These /// can be accessed via \c ::createSnapshotToFile(). diff --git a/ReactCommon/jsi/jsi/jsi.cpp b/ReactCommon/jsi/jsi/jsi.cpp index e4a7e431fca00d..eac4c4b6356a8a 100644 --- a/ReactCommon/jsi/jsi/jsi.cpp +++ b/ReactCommon/jsi/jsi/jsi.cpp @@ -97,7 +97,7 @@ Instrumentation& Runtime::instrumentation() { return std::unordered_map{}; } - void collectGarbage() override {} + void collectGarbage(std::string) override {} void startTrackingHeapObjectStackTraces() override {} void stopTrackingHeapObjectStackTraces() override {} diff --git a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp index 0d91ca7778e0b0..c4a3d90625e078 100644 --- a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp +++ b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp @@ -344,7 +344,7 @@ void JSIExecutor::handleMemoryPressure(int pressureLevel) { // collections. LOG(INFO) << "Memory warning (pressure level: " << levelName << ") received by JS VM, running a GC"; - runtime_->instrumentation().collectGarbage(); + runtime_->instrumentation().collectGarbage("memory warning"); break; default: // Use the raw number instead of the name here since the name is From d8b70b19b39ead4dd41895d666d116a43c56474e Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Fri, 18 Sep 2020 12:21:02 -0700 Subject: [PATCH 031/241] Update Flipper (#29787) Summary: The current Flipper version included in new React Native is quite old, causing some bugs to be present which have long been solved, such as freezing the UI after inspecting it. Fixes This fixes https://github.com/facebook/react-native/issues/29492 / https://github.com/facebook/flipper/issues/1399 ## Changelog [general][changed] - Update Flipper to 0.54 Pull Request resolved: https://github.com/facebook/react-native/pull/29787 Test Plan: Updated the RN 0.63.2 based test project https://github.com/mweststrate/flipper-issue-1399-repo with `use_flipper!('Flipper' => '0.54.0')` (in `ios/Podspec`) / `FLIPPER_VERSION=0.52.1` in `gradle.properties` in the test project https://github.com/mweststrate/flipper-issue-1399-repo and verified that everything builds and connects correctly, and that the bug is no longer present. Tried to run RN-tester project in this repo. For iOS this succeeded, on Android I got a build error: ``` make: Leaving directory '/Users/mweststrate/Desktop/react-native/ReactAndroid/src/main/jni/react/jni' make: Entering directory '/Users/mweststrate/Desktop/react-native/ReactAndroid/src/main/jni/react/jni' fcntl(): Bad file descriptor [armeabi-v7a] Compile++ thumb: folly_json <= FileUtil.cpp /Users/mweststrate/Desktop/react-native/ReactAndroid/build/third-party-ndk/folly/folly/FileUtil.cpp:37:14: error: no matching function for call to 'wrapNoInt' make: Leaving directory '/Users/mweststrate/Desktop/react-native/ReactAndroid/src/main/jni/react/jni' return int(wrapNoInt(open, name, flags, mode)); ^~~~~~~~~ /Users/mweststrate/Desktop/react-native/ReactAndroid/build/third-party-ndk/folly/folly/detail/FileUtilDetail.h:34:9: note: candidate template ignored: couldn't infer template argument 'F' ssize_t wrapNoInt(F f, Args... args) { ^ 1 error generated. make: *** [/opt/android_sdk/ndk/21.3.6528147/build/core/build-binary.mk:478: /Users/mweststrate/Desktop/react-native/ReactAndroid/build/tmp/buildReactNdkLib/local/armeabi-v7a/objs/folly_json/folly/FileUtil.o] Error 1 make: *** Waiting for unfinished jobs.... fcntl(): Bad file descriptor make: Entering directory '/Users/mweststrate/Desktop/react-native/ReactAndroid/src/main/jni/react/jni' [armeabi-v7a] Compile++ thumb: folly_json <= Demangle.cpp ``` No idea if it is related. I guess not since without making the change I got the same error. Reviewed By: mweststrate Differential Revision: D23767388 Pulled By: fkgozali fbshipit-source-id: 35f0d3ddec41942f5bbc96cb391975d84729ef5e --- packages/rn-tester/Podfile.lock | 66 +++++++++---------- packages/rn-tester/README.md | 2 +- .../rn-tester/android/app/gradle.properties | 2 +- scripts/react_native_pods.rb | 2 +- template/android/gradle.properties | 2 +- 5 files changed, 37 insertions(+), 37 deletions(-) diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 51a0dea2c3b33c..8b5b2eda2819e1 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -11,7 +11,7 @@ PODS: - React-Core (= 1000.0.0) - React-jsi (= 1000.0.0) - ReactCommon/turbomodule/core (= 1000.0.0) - - Flipper (0.41.5): + - Flipper (0.54.0): - Flipper-Folly (~> 2.2) - Flipper-RSocket (~> 1.1) - Flipper-DoubleConversion (1.1.7) @@ -25,36 +25,36 @@ PODS: - Flipper-PeerTalk (0.0.4) - Flipper-RSocket (1.1.0): - Flipper-Folly (~> 2.2) - - FlipperKit (0.41.5): - - FlipperKit/Core (= 0.41.5) - - FlipperKit/Core (0.41.5): - - Flipper (~> 0.41.5) + - FlipperKit (0.54.0): + - FlipperKit/Core (= 0.54.0) + - FlipperKit/Core (0.54.0): + - Flipper (~> 0.54.0) - FlipperKit/CppBridge - FlipperKit/FBCxxFollyDynamicConvert - FlipperKit/FBDefines - FlipperKit/FKPortForwarding - - FlipperKit/CppBridge (0.41.5): - - Flipper (~> 0.41.5) - - FlipperKit/FBCxxFollyDynamicConvert (0.41.5): + - FlipperKit/CppBridge (0.54.0): + - Flipper (~> 0.54.0) + - FlipperKit/FBCxxFollyDynamicConvert (0.54.0): - Flipper-Folly (~> 2.2) - - FlipperKit/FBDefines (0.41.5) - - FlipperKit/FKPortForwarding (0.41.5): + - FlipperKit/FBDefines (0.54.0) + - FlipperKit/FKPortForwarding (0.54.0): - CocoaAsyncSocket (~> 7.6) - Flipper-PeerTalk (~> 0.0.4) - - FlipperKit/FlipperKitHighlightOverlay (0.41.5) - - FlipperKit/FlipperKitLayoutPlugin (0.41.5): + - FlipperKit/FlipperKitHighlightOverlay (0.54.0) + - FlipperKit/FlipperKitLayoutPlugin (0.54.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutTextSearchable - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutTextSearchable (0.41.5) - - FlipperKit/FlipperKitNetworkPlugin (0.41.5): + - FlipperKit/FlipperKitLayoutTextSearchable (0.54.0) + - FlipperKit/FlipperKitNetworkPlugin (0.54.0): - FlipperKit/Core - - FlipperKit/FlipperKitReactPlugin (0.41.5): + - FlipperKit/FlipperKitReactPlugin (0.54.0): - FlipperKit/Core - - FlipperKit/FlipperKitUserDefaultsPlugin (0.41.5): + - FlipperKit/FlipperKitUserDefaultsPlugin (0.54.0): - FlipperKit/Core - - FlipperKit/SKIOSNetworkPlugin (0.41.5): + - FlipperKit/SKIOSNetworkPlugin (0.54.0): - FlipperKit/Core - FlipperKit/FlipperKitNetworkPlugin - glog (0.3.5) @@ -357,25 +357,25 @@ DEPENDENCIES: - DoubleConversion (from `../../third-party-podspecs/DoubleConversion.podspec`) - FBLazyVector (from `../../Libraries/FBLazyVector`) - FBReactNativeSpec (from `../../Libraries/FBReactNativeSpec`) - - Flipper (~> 0.41.1) + - Flipper (~> 0.54.0) - Flipper-DoubleConversion (= 1.1.7) - Flipper-Folly (~> 2.2) - Flipper-Glog (= 0.3.6) - Flipper-PeerTalk (~> 0.0.4) - Flipper-RSocket (~> 1.1) - - FlipperKit (~> 0.41.1) - - FlipperKit/Core (~> 0.41.1) - - FlipperKit/CppBridge (~> 0.41.1) - - FlipperKit/FBCxxFollyDynamicConvert (~> 0.41.1) - - FlipperKit/FBDefines (~> 0.41.1) - - FlipperKit/FKPortForwarding (~> 0.41.1) - - FlipperKit/FlipperKitHighlightOverlay (~> 0.41.1) - - FlipperKit/FlipperKitLayoutPlugin (~> 0.41.1) - - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.41.1) - - FlipperKit/FlipperKitNetworkPlugin (~> 0.41.1) - - FlipperKit/FlipperKitReactPlugin (~> 0.41.1) - - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.41.1) - - FlipperKit/SKIOSNetworkPlugin (~> 0.41.1) + - FlipperKit (~> 0.54.0) + - FlipperKit/Core (~> 0.54.0) + - FlipperKit/CppBridge (~> 0.54.0) + - FlipperKit/FBCxxFollyDynamicConvert (~> 0.54.0) + - FlipperKit/FBDefines (~> 0.54.0) + - FlipperKit/FKPortForwarding (~> 0.54.0) + - FlipperKit/FlipperKitHighlightOverlay (~> 0.54.0) + - FlipperKit/FlipperKitLayoutPlugin (~> 0.54.0) + - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.54.0) + - FlipperKit/FlipperKitNetworkPlugin (~> 0.54.0) + - FlipperKit/FlipperKitReactPlugin (~> 0.54.0) + - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.54.0) + - FlipperKit/SKIOSNetworkPlugin (~> 0.54.0) - glog (from `../../third-party-podspecs/glog.podspec`) - RCT-Folly (from `../../third-party-podspecs/RCT-Folly.podspec`) - RCTRequired (from `../../Libraries/RCTRequired`) @@ -491,13 +491,13 @@ SPEC CHECKSUMS: DoubleConversion: cde416483dac037923206447da6e1454df403714 FBLazyVector: 8ea0285646adaf7fa725c20ed737c49ee5ea680a FBReactNativeSpec: 29b1b8a11346e71351f3a2ba126439810edee362 - Flipper: 33585e2d9810fe5528346be33bcf71b37bb7ae13 + Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365 Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3 Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7 - FlipperKit: bc68102cd4952a258a23c9c1b316c7bec1fecf83 + FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3 OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355 RCT-Folly: b39288cedafe50da43317ec7d91bcc8cc0abbf33 diff --git a/packages/rn-tester/README.md b/packages/rn-tester/README.md index 4c9733acf95377..e1d52db22883a7 100644 --- a/packages/rn-tester/README.md +++ b/packages/rn-tester/README.md @@ -13,7 +13,7 @@ Before running the app, make sure you ran: ### Running on iOS Both macOS and Xcode are required. - +- `cd packages/rn-tester` - Install [Bundler](https://bundler.io/): `gem install bundler`. We use bundler to install the right version of [CocoaPods](https://cocoapods.org/) locally. - Install Bundler and CocoaPods dependencies: `bundle install && bundle exec pod install` - Open the generated `RNTesterPods.xcworkspace`. This is not checked in, as it is generated by CocoaPods. Do not open `RNTesterPods.xcodeproj` directly. diff --git a/packages/rn-tester/android/app/gradle.properties b/packages/rn-tester/android/app/gradle.properties index bba40bc51018e2..5c93b3bca832dc 100644 --- a/packages/rn-tester/android/app/gradle.properties +++ b/packages/rn-tester/android/app/gradle.properties @@ -10,4 +10,4 @@ android.useAndroidX=true android.enableJetifier=true # Version of flipper SDK to use with React Native -FLIPPER_VERSION=0.37.0 +FLIPPER_VERSION=0.54.0 diff --git a/scripts/react_native_pods.rb b/scripts/react_native_pods.rb index ffdbfee75bfe1a..213f8543398c21 100644 --- a/scripts/react_native_pods.rb +++ b/scripts/react_native_pods.rb @@ -70,7 +70,7 @@ def use_react_native! (options={}) end def use_flipper!(versions = {}, configurations: ['Debug']) - versions['Flipper'] ||= '~> 0.41.1' + versions['Flipper'] ||= '~> 0.54.0' versions['Flipper-DoubleConversion'] ||= '1.1.7' versions['Flipper-Folly'] ||= '~> 2.2' versions['Flipper-Glog'] ||= '0.3.6' diff --git a/template/android/gradle.properties b/template/android/gradle.properties index 04ca0ef29e7d10..3bdbd3d4e90ec3 100644 --- a/template/android/gradle.properties +++ b/template/android/gradle.properties @@ -25,4 +25,4 @@ android.useAndroidX=true android.enableJetifier=true # Version of flipper SDK to use with React Native -FLIPPER_VERSION=0.37.0 +FLIPPER_VERSION=0.54.0 From fa44c46e37baabc5664e860ac83c90cfdc9f4d68 Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Fri, 18 Sep 2020 16:35:46 -0700 Subject: [PATCH 032/241] Fix flattening/unflattening case on Android Summary: There are cases where we Delete+Create a node in the same frame. Practically, the new differ should prevent this, but we don't want to rely on that necessarily. See comments for further justification on why deleteView can do less work overall. In reparenting cases, this causes crashes because dropView removes *and deletes* children that shouldn't necessarily be deleted. Changelog: [Internal] Reviewed By: shergin Differential Revision: D23775453 fbshipit-source-id: c577c5af8c27cfb185d527f0afd8aeb08ee3a5fe --- .../react/fabric/mounting/MountingManager.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java index 4629668d4944b4..41cf97c71fb095 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java @@ -577,13 +577,15 @@ public void deleteView(int reactTag) { return; } - View view = viewState.mView; - - if (view != null) { - dropView(view, false); - } else { - mTagToViewState.remove(reactTag); - } + // To delete we simply remove the tag from the registry. + // In the past we called dropView here, but we want to rely on either + // (1) the correct set of MountInstructions being sent to the platform + // and/or (2) dropView being called by stopSurface. + // If Views are orphaned at this stage and leaked, it's a problem in + // the differ or LayoutAnimations, not MountingManager. + // Additionally, as documented in `dropView`, we cannot always trust a + // view's children to be up-to-date. + mTagToViewState.remove(reactTag); } @UiThread From 03448715af55a52a67fd218f55cfc218e8d2591b Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Fri, 18 Sep 2020 16:35:46 -0700 Subject: [PATCH 033/241] Have BatchMountItem log the exact item that crashes Summary: Because BatchMountItem executes many items, sometimes it's unclear which MountItem causes a crash. Catch and log the exact item. This shouldn't cause perf regressions because we have a try/catch block in FabricUIManager where execute is called. Changelog: [Internal] Reviewed By: shergin Differential Revision: D23775500 fbshipit-source-id: c878e085c23d3d3a7ef02a34e5aca57759376aa6 --- .../mounting/mountitems/BatchMountItem.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/BatchMountItem.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/BatchMountItem.java index d685be1ae02868..fc908e2deedc29 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/BatchMountItem.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/BatchMountItem.java @@ -8,6 +8,7 @@ package com.facebook.react.fabric.mounting.mountitems; import androidx.annotation.NonNull; +import com.facebook.common.logging.FLog; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.ReactMarker; import com.facebook.react.bridge.ReactMarkerConstants; @@ -70,8 +71,20 @@ private void endMarkers() { public void execute(@NonNull MountingManager mountingManager) { beginMarkers("mountViews"); - for (int mountItemIndex = 0; mountItemIndex < mSize; mountItemIndex++) { - mMountItems[mountItemIndex].execute(mountingManager); + int mountItemIndex = 0; + try { + for (; mountItemIndex < mSize; mountItemIndex++) { + mMountItems[mountItemIndex].execute(mountingManager); + } + } catch (RuntimeException e) { + FLog.e( + TAG, + "Caught exception executing mountItem @" + + mountItemIndex + + ": " + + mMountItems[mountItemIndex].toString(), + e); + throw e; } endMarkers(); From 752e709b51eaaf16db9c3704654d98580948d181 Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Fri, 18 Sep 2020 16:35:46 -0700 Subject: [PATCH 034/241] Additional differ test: flattening differ should not produce DELETE and CREATE mutation for the same tag in the same frame Summary: See additional assertion. Tests still pass, so no other change was necessary. Changelog: [Internal] Differential Revision: D23775553 fbshipit-source-id: 57d3191f25dd55ab4da189207f6d201a31b175e0 --- .../tests/ShadowTreeLifeCycleTest.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp b/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp index 851231da950841..46d2fc4c6cdd25 100644 --- a/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp +++ b/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp @@ -5,11 +5,14 @@ * LICENSE file in the root directory of this source tree. */ +#include + #include #include #include #include +#include #include #include @@ -102,6 +105,25 @@ static void testShadowNodeTreeLifeCycle( auto mutations = calculateShadowViewMutations( *currentRootNode, *nextRootNode, useFlattener); + // If using flattener: make sure that in a single frame, a DELETE for a view is not + // followed by a CREATE for the same view. + if (useFlattener) { + std::vector deletedTags{}; + for (auto const& mutation : mutations) { + if (mutation.type == ShadowViewMutation::Type::Delete) { + deletedTags.push_back(mutation.oldChildShadowView.tag); + } + } + for (auto const& mutation : mutations) { + if (mutation.type == ShadowViewMutation::Type::Create) { + if (std::find(deletedTags.begin(), deletedTags.end(), mutation.newChildShadowView.tag) != deletedTags.end()) { + LOG(ERROR) << "Deleted tag was recreated in mutations list: [" << mutation.newChildShadowView.tag << "]"; + FAIL(); + } + } + } + } + // Mutating the view tree. viewTree.mutate(mutations); From 1ddc6542c79b7a4390c1c1b92db603de2932fdf1 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Fri, 18 Sep 2020 17:05:10 -0700 Subject: [PATCH 035/241] Fabric: Removing catching all exceptions in UIManager::completeRoot Summary: This is a revert of D23529233 (https://github.com/facebook/react-native/commit/902611f14837752353e919dd1740812ec7260fb8). It turns out it was a bad idea. With this catch-all thing, we don't get new information. Yeah, we crash earlier now but seems we have even less information about the crash. :( I think D23754284 (https://github.com/facebook/react-native/commit/04c874bd9c6b15274fd87acf10cb3533b2eabc0d) should fix the issue. Changelog: [Internal] Fabric-specific internal change. Original commit changeset: 7ac7fb26ac08 Reviewed By: sammy-SC Differential Revision: D23786086 fbshipit-source-id: 86784df0193abfb7328c4d5a16a9af4214e9a161 --- .../renderer/uimanager/UIManagerBinding.cpp | 49 ++++++------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index 5c2765ca3996f9..0633fbcc6e6993 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -430,28 +430,17 @@ jsi::Value UIManagerBinding::get( jsi::Value const &thisValue, jsi::Value const *arguments, size_t count) -> jsi::Value { - try { - auto surfaceId = surfaceIdFromValue(runtime, arguments[0]); - auto shadowNodeList = - shadowNodeListFromValue(runtime, arguments[1]); - - if (sharedUIManager->backgroundExecutor_) { - sharedUIManager->backgroundExecutor_( - [sharedUIManager, surfaceId, shadowNodeList] { - sharedUIManager->completeSurface( - surfaceId, shadowNodeList); - }); - } else { - uiManager->completeSurface(surfaceId, shadowNodeList); - } - } catch (std::exception const &e) { - LOG(ERROR) << "Exception in UIManagerBinding::completeRoot(): " - << e.what(); - abort(); - } catch (...) { - LOG(ERROR) - << "Exception in UIManagerBinding::completeRoot(): Unknown."; - abort(); + auto surfaceId = surfaceIdFromValue(runtime, arguments[0]); + auto shadowNodeList = + shadowNodeListFromValue(runtime, arguments[1]); + + if (sharedUIManager->backgroundExecutor_) { + sharedUIManager->backgroundExecutor_( + [sharedUIManager, surfaceId, shadowNodeList] { + sharedUIManager->completeSurface(surfaceId, shadowNodeList); + }); + } else { + uiManager->completeSurface(surfaceId, shadowNodeList); } return jsi::Value::undefined(); @@ -468,19 +457,9 @@ jsi::Value UIManagerBinding::get( jsi::Value const &thisValue, jsi::Value const *arguments, size_t count) -> jsi::Value { - try { - uiManager->completeSurface( - surfaceIdFromValue(runtime, arguments[0]), - shadowNodeListFromValue(runtime, arguments[1])); - } catch (std::exception const &e) { - LOG(ERROR) << "Exception in UIManagerBinding::completeRoot(): " - << e.what(); - abort(); - } catch (...) { - LOG(ERROR) - << "Exception in UIManagerBinding::completeRoot(): Unknown."; - abort(); - } + uiManager->completeSurface( + surfaceIdFromValue(runtime, arguments[0]), + shadowNodeListFromValue(runtime, arguments[1])); return jsi::Value::undefined(); }); From 57dd48b2464ac04b860f2f69cb4f131990fe4dbd Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Fri, 18 Sep 2020 17:05:10 -0700 Subject: [PATCH 036/241] Fabric: Marking all JS function lambdas `noexcept` in UIManagerBinding Summary: Exceptions in C++ work quite differently from exceptions in other languages. To make exceptions actually work **correctly** all the code needs to be written with "exceptions in mind" (e.g., see https://www.stroustrup.com/except.pdf). In short, if the code is not "exceptions ready", throwing an exception causes memory leaks, dangling pointers, and invariant violations all over the place, which will probably cause another crashes down the road (which will be especially hard to investigate and attribute to the original issue). Fabric Core (Layout, Props parsing, ShadowNodes management, and so on) does not use exceptions because in most (all?) the cases the exception is now recoverable. So, if a program detects some internal state invariant violation or missing some resource, *logically* it's fatal. We also don't want to pay code-size and performance tax for exception support, so that's why we don't use them. It's just not the right fit for Fabric Core. This does not mean that exceptions don't happen though. C++ standard library can throw them... sometimes. And if our library is compiled with exceptions enabled (still the case, unfortunately), an exception can bubble to JavaScript code and losing all context down the road. And it's hard to investigate such crashes. To isolate those occasional exceptions inside C++ core we are marking all C++/JS boundaries with `noexcept` that stops the bubbling. I hope that will give us much more informative crash reports. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D23787492 fbshipit-source-id: 0822dbf36fc680c15b02b5cd0f2d87328296b642 --- .../RCTSurfacePresenterBridgeAdapter.mm | 2 +- .../react/renderer/core/EventTarget.cpp | 2 +- ReactCommon/react/renderer/core/EventTarget.h | 2 +- .../react/renderer/uimanager/UIManager.cpp | 4 +- .../react/renderer/uimanager/UIManager.h | 4 +- .../uimanager/UIManagerAnimationDelegate.h | 4 +- .../renderer/uimanager/UIManagerBinding.cpp | 138 +++++++++--------- .../renderer/uimanager/UIManagerBinding.h | 14 +- .../react/renderer/uimanager/primitives.h | 16 +- 9 files changed, 93 insertions(+), 93 deletions(-) diff --git a/React/Fabric/RCTSurfacePresenterBridgeAdapter.mm b/React/Fabric/RCTSurfacePresenterBridgeAdapter.mm index a70c494d937838..8c20e934870864 100644 --- a/React/Fabric/RCTSurfacePresenterBridgeAdapter.mm +++ b/React/Fabric/RCTSurfacePresenterBridgeAdapter.mm @@ -48,7 +48,7 @@ static RuntimeExecutor RCTRuntimeExecutorFromBridge(RCTBridge *bridge) auto bridgeWeakWrapper = wrapManagedObjectWeakly([bridge batchedBridge] ?: bridge); RuntimeExecutor runtimeExecutor = [bridgeWeakWrapper]( - std::function &&callback) { + std::function &&callback) { RCTBridge *bridge = unwrapManagedObjectWeakly(bridgeWeakWrapper); RCTAssert(bridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil at the moment of scheduling a call."); diff --git a/ReactCommon/react/renderer/core/EventTarget.cpp b/ReactCommon/react/renderer/core/EventTarget.cpp index c9f418c2d88fa6..01a5a77bd50f48 100644 --- a/ReactCommon/react/renderer/core/EventTarget.cpp +++ b/ReactCommon/react/renderer/core/EventTarget.cpp @@ -14,7 +14,7 @@ using Tag = EventTarget::Tag; EventTarget::EventTarget( jsi::Runtime &runtime, - const jsi::Value &instanceHandle, + jsi::Value const &instanceHandle, Tag tag) : weakInstanceHandle_( jsi::WeakObject(runtime, instanceHandle.asObject(runtime))), diff --git a/ReactCommon/react/renderer/core/EventTarget.h b/ReactCommon/react/renderer/core/EventTarget.h index 8bd7a03a1995ac..92f382a96c4965 100644 --- a/ReactCommon/react/renderer/core/EventTarget.h +++ b/ReactCommon/react/renderer/core/EventTarget.h @@ -35,7 +35,7 @@ class EventTarget { /* * Constructs an EventTarget from a weak instance handler and a tag. */ - EventTarget(jsi::Runtime &runtime, const jsi::Value &instanceHandle, Tag tag); + EventTarget(jsi::Runtime &runtime, jsi::Value const &instanceHandle, Tag tag); /* * Sets the `enabled` flag that allows creating a strong instance handle from diff --git a/ReactCommon/react/renderer/uimanager/UIManager.cpp b/ReactCommon/react/renderer/uimanager/UIManager.cpp index e4c4cdb8f5b461..a8b4c6964808fc 100644 --- a/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -311,8 +311,8 @@ void UIManager::dispatchCommand( void UIManager::configureNextLayoutAnimation( jsi::Runtime &runtime, RawValue const &config, - const jsi::Value &successCallback, - const jsi::Value &failureCallback) const { + jsi::Value const &successCallback, + jsi::Value const &failureCallback) const { if (animationDelegate_) { animationDelegate_->uiManagerDidConfigureNextLayoutAnimation( runtime, diff --git a/ReactCommon/react/renderer/uimanager/UIManager.h b/ReactCommon/react/renderer/uimanager/UIManager.h index 4e53389615d04f..1235cf3532af14 100644 --- a/ReactCommon/react/renderer/uimanager/UIManager.h +++ b/ReactCommon/react/renderer/uimanager/UIManager.h @@ -147,8 +147,8 @@ class UIManager final : public ShadowTreeDelegate { void configureNextLayoutAnimation( jsi::Runtime &runtime, RawValue const &config, - const jsi::Value &successCallback, - const jsi::Value &failureCallback) const; + jsi::Value const &successCallback, + jsi::Value const &failureCallback) const; ShadowTreeRegistry const &getShadowTreeRegistry() const; diff --git a/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h b/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h index cfbb0c00e558ea..9e55d1f3862e91 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h +++ b/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h @@ -26,8 +26,8 @@ class UIManagerAnimationDelegate { virtual void uiManagerDidConfigureNextLayoutAnimation( jsi::Runtime &runtime, RawValue const &config, - const jsi::Value &successCallback, - const jsi::Value &failureCallback) const = 0; + jsi::Value const &successCallback, + jsi::Value const &failureCallback) const = 0; /** * Set ComponentDescriptor registry. diff --git a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index 0633fbcc6e6993..024eb3424dc573 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -17,7 +17,7 @@ namespace react { static jsi::Object getModule( jsi::Runtime &runtime, - const std::string &moduleName) { + std::string const &moduleName) { auto batchedBridge = runtime.global().getPropertyAsObject(runtime, "__fbBatchedBridge"); auto getCallableModule = @@ -82,8 +82,8 @@ void UIManagerBinding::attach(std::shared_ptr const &uiManager) { void UIManagerBinding::startSurface( jsi::Runtime &runtime, SurfaceId surfaceId, - const std::string &moduleName, - const folly::dynamic &initalProps) const { + std::string const &moduleName, + folly::dynamic const &initalProps) const { folly::dynamic parameters = folly::dynamic::object(); parameters["rootTag"] = surfaceId; parameters["initialProps"] = initalProps; @@ -128,9 +128,9 @@ void UIManagerBinding::stopSurface(jsi::Runtime &runtime, SurfaceId surfaceId) void UIManagerBinding::dispatchEvent( jsi::Runtime &runtime, - const EventTarget *eventTarget, - const std::string &type, - const ValueFactory &payloadFactory) const { + EventTarget const *eventTarget, + std::string const &type, + ValueFactory const &payloadFactory) const { SystraceSection s("UIManagerBinding::dispatchEvent"); auto payload = payloadFactory(runtime); @@ -158,7 +158,7 @@ void UIManagerBinding::dispatchEvent( : jsi::Value::null(); auto &eventHandlerWrapper = - static_cast(*eventHandler_); + static_cast(*eventHandler_); eventHandlerWrapper.callback.call( runtime, @@ -173,7 +173,7 @@ void UIManagerBinding::invalidate() const { jsi::Value UIManagerBinding::get( jsi::Runtime &runtime, - const jsi::PropNameID &name) { + jsi::PropNameID const &name) { auto methodName = name.utf8(runtime); // Convert shared_ptr to a raw ptr @@ -213,9 +213,9 @@ jsi::Value UIManagerBinding::get( 5, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { return valueFromShadowNode( runtime, uiManager->createNode( @@ -235,9 +235,9 @@ jsi::Value UIManagerBinding::get( 1, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { return valueFromShadowNode( runtime, uiManager->cloneNode(shadowNodeFromValue(runtime, arguments[0]))); @@ -251,9 +251,9 @@ jsi::Value UIManagerBinding::get( 2, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { uiManager->setJSResponder( shadowNodeFromValue(runtime, arguments[0]), arguments[1].getBool()); @@ -269,9 +269,9 @@ jsi::Value UIManagerBinding::get( 2, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { auto node = shadowNodeFromValue(runtime, arguments[0]); auto locationX = (Float)arguments[1].getNumber(); auto locationY = (Float)arguments[2].getNumber(); @@ -299,9 +299,9 @@ jsi::Value UIManagerBinding::get( 0, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { uiManager->clearJSResponder(); return jsi::Value::undefined(); @@ -316,9 +316,9 @@ jsi::Value UIManagerBinding::get( 1, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { return valueFromShadowNode( runtime, uiManager->cloneNode( @@ -335,10 +335,10 @@ jsi::Value UIManagerBinding::get( 2, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { - const auto &rawProps = RawProps(runtime, arguments[1]); + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { + auto const &rawProps = RawProps(runtime, arguments[1]); return valueFromShadowNode( runtime, uiManager->cloneNode( @@ -356,10 +356,10 @@ jsi::Value UIManagerBinding::get( 2, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { - const auto &rawProps = RawProps(runtime, arguments[1]); + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { + auto const &rawProps = RawProps(runtime, arguments[1]); return valueFromShadowNode( runtime, uiManager->cloneNode( @@ -376,9 +376,9 @@ jsi::Value UIManagerBinding::get( 2, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { uiManager->appendChild( shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1])); @@ -392,9 +392,9 @@ jsi::Value UIManagerBinding::get( name, 1, [](jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { auto shadowNodeList = std::make_shared(SharedShadowNodeList({})); return valueFromShadowNodeList(runtime, shadowNodeList); @@ -407,9 +407,9 @@ jsi::Value UIManagerBinding::get( name, 2, [](jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { auto shadowNodeList = shadowNodeListFromValue(runtime, arguments[0]); auto shadowNode = shadowNodeFromValue(runtime, arguments[1]); shadowNodeList->push_back(shadowNode); @@ -429,7 +429,7 @@ jsi::Value UIManagerBinding::get( jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) -> jsi::Value { + size_t count) noexcept -> jsi::Value { auto surfaceId = surfaceIdFromValue(runtime, arguments[0]); auto shadowNodeList = shadowNodeListFromValue(runtime, arguments[1]); @@ -456,7 +456,7 @@ jsi::Value UIManagerBinding::get( jsi::Runtime &runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) -> jsi::Value { + size_t count) noexcept -> jsi::Value { uiManager->completeSurface( surfaceIdFromValue(runtime, arguments[0]), shadowNodeListFromValue(runtime, arguments[1])); @@ -473,9 +473,9 @@ jsi::Value UIManagerBinding::get( 1, [this]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { auto eventHandler = arguments[0].getObject(runtime).getFunction(runtime); eventHandler_ = @@ -491,9 +491,9 @@ jsi::Value UIManagerBinding::get( 2, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1]).get(), @@ -515,9 +515,9 @@ jsi::Value UIManagerBinding::get( 3, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { uiManager->dispatchCommand( shadowNodeFromValue(runtime, arguments[0]), stringFromValue(runtime, arguments[1]), @@ -535,9 +535,9 @@ jsi::Value UIManagerBinding::get( 4, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1]).get(), @@ -571,9 +571,9 @@ jsi::Value UIManagerBinding::get( 2, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), nullptr, @@ -606,9 +606,9 @@ jsi::Value UIManagerBinding::get( 2, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), nullptr, @@ -641,9 +641,9 @@ jsi::Value UIManagerBinding::get( 2, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { uiManager->setNativeProps( *shadowNodeFromValue(runtime, arguments[0]), RawProps(runtime, arguments[1])); @@ -659,9 +659,9 @@ jsi::Value UIManagerBinding::get( 3, [uiManager]( jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept -> jsi::Value { uiManager->configureNextLayoutAnimation( runtime, // TODO: pass in JSI value instead of folly::dynamic to RawValue diff --git a/ReactCommon/react/renderer/uimanager/UIManagerBinding.h b/ReactCommon/react/renderer/uimanager/UIManagerBinding.h index d4e133f59d38d4..d5616310716a19 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerBinding.h +++ b/ReactCommon/react/renderer/uimanager/UIManagerBinding.h @@ -47,8 +47,8 @@ class UIManagerBinding : public jsi::HostObject { void startSurface( jsi::Runtime &runtime, SurfaceId surfaceId, - const std::string &moduleName, - const folly::dynamic &initalProps) const; + std::string const &moduleName, + folly::dynamic const &initalProps) const; /* * Stops React Native Surface with given id. @@ -62,9 +62,9 @@ class UIManagerBinding : public jsi::HostObject { */ void dispatchEvent( jsi::Runtime &runtime, - const EventTarget *eventTarget, - const std::string &type, - const ValueFactory &payloadFactory) const; + EventTarget const *eventTarget, + std::string const &type, + ValueFactory const &payloadFactory) const; /* * Invalidates the binding and underlying UIManager. @@ -78,11 +78,11 @@ class UIManagerBinding : public jsi::HostObject { /* * `jsi::HostObject` specific overloads. */ - jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &name) override; + jsi::Value get(jsi::Runtime &runtime, jsi::PropNameID const &name) override; private: std::shared_ptr uiManager_; - std::unique_ptr eventHandler_; + std::unique_ptr eventHandler_; }; } // namespace react diff --git a/ReactCommon/react/renderer/uimanager/primitives.h b/ReactCommon/react/renderer/uimanager/primitives.h index cd9dde4a662c69..9fb2ea92786844 100644 --- a/ReactCommon/react/renderer/uimanager/primitives.h +++ b/ReactCommon/react/renderer/uimanager/primitives.h @@ -42,7 +42,7 @@ struct ShadowNodeListWrapper : public jsi::HostObject { inline static ShadowNode::Shared shadowNodeFromValue( jsi::Runtime &runtime, - const jsi::Value &value) { + jsi::Value const &value) { return value.getObject(runtime) .getHostObject(runtime) ->shadowNode; @@ -57,7 +57,7 @@ inline static jsi::Value valueFromShadowNode( inline static SharedShadowNodeUnsharedList shadowNodeListFromValue( jsi::Runtime &runtime, - const jsi::Value &value) { + jsi::Value const &value) { return value.getObject(runtime) .getHostObject(runtime) ->shadowNodeList; @@ -72,31 +72,31 @@ inline static jsi::Value valueFromShadowNodeList( inline static SharedEventTarget eventTargetFromValue( jsi::Runtime &runtime, - const jsi::Value &eventTargetValue, - const jsi::Value &tagValue) { + jsi::Value const &eventTargetValue, + jsi::Value const &tagValue) { return std::make_shared( runtime, eventTargetValue, tagValue.getNumber()); } -inline static Tag tagFromValue(jsi::Runtime &runtime, const jsi::Value &value) { +inline static Tag tagFromValue(jsi::Runtime &runtime, jsi::Value const &value) { return (Tag)value.getNumber(); } inline static SurfaceId surfaceIdFromValue( jsi::Runtime &runtime, - const jsi::Value &value) { + jsi::Value const &value) { return (SurfaceId)value.getNumber(); } inline static std::string stringFromValue( jsi::Runtime &runtime, - const jsi::Value &value) { + jsi::Value const &value) { return value.getString(runtime).utf8(runtime); } inline static folly::dynamic commandArgsFromValue( jsi::Runtime &runtime, - const jsi::Value &value) { + jsi::Value const &value) { return jsi::dynamicFromValue(runtime, value); } From 7c93f5b0015306b08f59f07254ea99c81a673135 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Sat, 19 Sep 2020 00:40:18 -0700 Subject: [PATCH 037/241] Move TurboModule Core from ReactCommon/turbomodule to ReactCommon/react/nativemodule Summary: This diff moves the code of TurboModule Core from ReactCommon/turbomodule to ReactCommon/react/nativemodule For iOS: Pod spec name stays as "ReactCommon/turbomodule/..." for now, only the source/header location is affected. The target will be renamed/restructured closer to TurboModule rollout. changelog: [internal] Internal Reviewed By: RSNara Differential Revision: D23362253 fbshipit-source-id: c2c8207578e50821c7573255d4319b9051b58a37 --- Libraries/FBReactNativeSpec/BUCK | 2 +- .../src/main/java/com/facebook/fbreact/specs/BUCK | 2 +- .../java/com/facebook/react/turbomodule/core/jni/BUCK | 2 +- ReactCommon/ReactCommon.podspec | 10 ++++++---- .../{turbomodule => react/nativemodule}/.clang-tidy | 0 .../{turbomodule => react/nativemodule}/core/BUCK | 0 .../nativemodule}/core/LongLivedObject.cpp | 0 .../nativemodule}/core/LongLivedObject.h | 0 .../nativemodule}/core/TurboCxxModule.cpp | 0 .../nativemodule}/core/TurboCxxModule.h | 0 .../nativemodule}/core/TurboModule.cpp | 0 .../nativemodule}/core/TurboModule.h | 0 .../nativemodule}/core/TurboModuleBinding.cpp | 0 .../nativemodule}/core/TurboModuleBinding.h | 0 .../nativemodule}/core/TurboModulePerfLogger.cpp | 0 .../nativemodule}/core/TurboModulePerfLogger.h | 0 .../nativemodule}/core/TurboModuleUtils.cpp | 0 .../nativemodule}/core/TurboModuleUtils.h | 0 .../core/platform/android/JavaTurboModule.cpp | 0 .../core/platform/android/JavaTurboModule.h | 0 .../nativemodule}/core/platform/ios/RCTTurboModule.h | 0 .../nativemodule}/core/platform/ios/RCTTurboModule.mm | 0 .../core/platform/ios/RCTTurboModuleManager.h | 0 .../core/platform/ios/RCTTurboModuleManager.mm | 0 .../{turbomodule => react/nativemodule}/samples/BUCK | 2 +- .../samples/NativeSampleTurboCxxModuleSpecJSI.cpp | 0 .../samples/NativeSampleTurboCxxModuleSpecJSI.h | 0 .../nativemodule}/samples/SampleTurboCxxModule.cpp | 0 .../nativemodule}/samples/SampleTurboCxxModule.h | 0 .../platform/ios/RCTNativeSampleTurboModuleSpec.h | 0 .../platform/ios/RCTNativeSampleTurboModuleSpec.mm | 0 .../samples/platform/ios/RCTSampleTurboCxxModule.h | 0 .../samples/platform/ios/RCTSampleTurboCxxModule.mm | 0 .../samples/platform/ios/RCTSampleTurboModule.h | 0 .../samples/platform/ios/RCTSampleTurboModule.mm | 0 .../platform/ios/SampleTurboCxxModuleLegacyImpl.cpp | 0 .../platform/ios/SampleTurboCxxModuleLegacyImpl.h | 0 packages/react-native-codegen/DEFS.bzl | 2 +- packages/rn-tester/Podfile.lock | 2 +- 39 files changed, 12 insertions(+), 10 deletions(-) rename ReactCommon/{turbomodule => react/nativemodule}/.clang-tidy (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/BUCK (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/LongLivedObject.cpp (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/LongLivedObject.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/TurboCxxModule.cpp (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/TurboCxxModule.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/TurboModule.cpp (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/TurboModule.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/TurboModuleBinding.cpp (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/TurboModuleBinding.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/TurboModulePerfLogger.cpp (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/TurboModulePerfLogger.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/TurboModuleUtils.cpp (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/TurboModuleUtils.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/platform/android/JavaTurboModule.cpp (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/platform/android/JavaTurboModule.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/platform/ios/RCTTurboModule.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/platform/ios/RCTTurboModule.mm (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/platform/ios/RCTTurboModuleManager.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/core/platform/ios/RCTTurboModuleManager.mm (100%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/BUCK (97%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/NativeSampleTurboCxxModuleSpecJSI.cpp (100%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/NativeSampleTurboCxxModuleSpecJSI.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/SampleTurboCxxModule.cpp (100%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/SampleTurboCxxModule.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm (100%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/platform/ios/RCTSampleTurboCxxModule.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/platform/ios/RCTSampleTurboCxxModule.mm (100%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/platform/ios/RCTSampleTurboModule.h (100%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/platform/ios/RCTSampleTurboModule.mm (100%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp (100%) rename ReactCommon/{turbomodule => react/nativemodule}/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h (100%) diff --git a/Libraries/FBReactNativeSpec/BUCK b/Libraries/FBReactNativeSpec/BUCK index 3ab64ac8a7a14a..96f09f1d31c205 100644 --- a/Libraries/FBReactNativeSpec/BUCK +++ b/Libraries/FBReactNativeSpec/BUCK @@ -20,6 +20,6 @@ fb_apple_library( deps = [ "//xplat/js/react-native-github:RCTTypeSafety", "//xplat/js/react-native-github/Libraries/RCTRequired:RCTRequired", - react_native_xplat_target_apple("turbomodule/core:core"), + react_native_xplat_target_apple("react/nativemodule/core:core"), ], ) diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK index 94067e4f20f33d..49f617762e45c9 100644 --- a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK @@ -51,7 +51,7 @@ rn_xplat_cxx_library( react_native_xplat_target("cxxreact:bridge"), ], exported_deps = ([ - react_native_xplat_target("turbomodule/core:core"), + react_native_xplat_target("react/nativemodule/core:core"), ]) + ([ "//xplat/jsi:jsi", ]) if not IS_OSS_BUILD else [], diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK index 84258474072b91..934dd73ac5d5c7 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK @@ -34,7 +34,7 @@ rn_xplat_cxx_library( exported_deps = [ ":callinvokerholder", "//xplat/jsi:jsi", - react_native_xplat_target("turbomodule/core:core"), + react_native_xplat_target("react/nativemodule/core:core"), react_native_target("java/com/facebook/react/reactperflogger/jni:jni"), ], ) diff --git a/ReactCommon/ReactCommon.podspec b/ReactCommon/ReactCommon.podspec index bad800f338c274..5c0efe17a630dd 100644 --- a/ReactCommon/ReactCommon.podspec +++ b/ReactCommon/ReactCommon.podspec @@ -36,6 +36,8 @@ Pod::Spec.new do |s| "USE_HEADERMAP" => "YES", "CLANG_CXX_LANGUAGE_STANDARD" => "c++14" } + # TODO (T48588859): Restructure this target to align with dir structure: "react/nativemodule/..." + # Note: Update this only when ready to minimize breaking changes. s.subspec "turbomodule" do |ss| ss.dependency "React-callinvoker", version ss.dependency "React-perflogger", version @@ -47,13 +49,13 @@ Pod::Spec.new do |s| ss.dependency "glog" ss.subspec "core" do |sss| - sss.source_files = "turbomodule/core/*.{cpp,h}", - "turbomodule/core/platform/ios/*.{mm,cpp,h}" + sss.source_files = "react/nativemodule/core/*.{cpp,h}", + "react/nativemodule/core/platform/ios/*.{mm,cpp,h}" end ss.subspec "samples" do |sss| - sss.source_files = "turbomodule/samples/*.{cpp,h}", - "turbomodule/samples/platform/ios/*.{mm,cpp,h}" + sss.source_files = "react/nativemodule/samples/*.{cpp,h}", + "react/nativemodule/samples/platform/ios/*.{mm,cpp,h}" sss.dependency "ReactCommon/turbomodule/core", version end end diff --git a/ReactCommon/turbomodule/.clang-tidy b/ReactCommon/react/nativemodule/.clang-tidy similarity index 100% rename from ReactCommon/turbomodule/.clang-tidy rename to ReactCommon/react/nativemodule/.clang-tidy diff --git a/ReactCommon/turbomodule/core/BUCK b/ReactCommon/react/nativemodule/core/BUCK similarity index 100% rename from ReactCommon/turbomodule/core/BUCK rename to ReactCommon/react/nativemodule/core/BUCK diff --git a/ReactCommon/turbomodule/core/LongLivedObject.cpp b/ReactCommon/react/nativemodule/core/LongLivedObject.cpp similarity index 100% rename from ReactCommon/turbomodule/core/LongLivedObject.cpp rename to ReactCommon/react/nativemodule/core/LongLivedObject.cpp diff --git a/ReactCommon/turbomodule/core/LongLivedObject.h b/ReactCommon/react/nativemodule/core/LongLivedObject.h similarity index 100% rename from ReactCommon/turbomodule/core/LongLivedObject.h rename to ReactCommon/react/nativemodule/core/LongLivedObject.h diff --git a/ReactCommon/turbomodule/core/TurboCxxModule.cpp b/ReactCommon/react/nativemodule/core/TurboCxxModule.cpp similarity index 100% rename from ReactCommon/turbomodule/core/TurboCxxModule.cpp rename to ReactCommon/react/nativemodule/core/TurboCxxModule.cpp diff --git a/ReactCommon/turbomodule/core/TurboCxxModule.h b/ReactCommon/react/nativemodule/core/TurboCxxModule.h similarity index 100% rename from ReactCommon/turbomodule/core/TurboCxxModule.h rename to ReactCommon/react/nativemodule/core/TurboCxxModule.h diff --git a/ReactCommon/turbomodule/core/TurboModule.cpp b/ReactCommon/react/nativemodule/core/TurboModule.cpp similarity index 100% rename from ReactCommon/turbomodule/core/TurboModule.cpp rename to ReactCommon/react/nativemodule/core/TurboModule.cpp diff --git a/ReactCommon/turbomodule/core/TurboModule.h b/ReactCommon/react/nativemodule/core/TurboModule.h similarity index 100% rename from ReactCommon/turbomodule/core/TurboModule.h rename to ReactCommon/react/nativemodule/core/TurboModule.h diff --git a/ReactCommon/turbomodule/core/TurboModuleBinding.cpp b/ReactCommon/react/nativemodule/core/TurboModuleBinding.cpp similarity index 100% rename from ReactCommon/turbomodule/core/TurboModuleBinding.cpp rename to ReactCommon/react/nativemodule/core/TurboModuleBinding.cpp diff --git a/ReactCommon/turbomodule/core/TurboModuleBinding.h b/ReactCommon/react/nativemodule/core/TurboModuleBinding.h similarity index 100% rename from ReactCommon/turbomodule/core/TurboModuleBinding.h rename to ReactCommon/react/nativemodule/core/TurboModuleBinding.h diff --git a/ReactCommon/turbomodule/core/TurboModulePerfLogger.cpp b/ReactCommon/react/nativemodule/core/TurboModulePerfLogger.cpp similarity index 100% rename from ReactCommon/turbomodule/core/TurboModulePerfLogger.cpp rename to ReactCommon/react/nativemodule/core/TurboModulePerfLogger.cpp diff --git a/ReactCommon/turbomodule/core/TurboModulePerfLogger.h b/ReactCommon/react/nativemodule/core/TurboModulePerfLogger.h similarity index 100% rename from ReactCommon/turbomodule/core/TurboModulePerfLogger.h rename to ReactCommon/react/nativemodule/core/TurboModulePerfLogger.h diff --git a/ReactCommon/turbomodule/core/TurboModuleUtils.cpp b/ReactCommon/react/nativemodule/core/TurboModuleUtils.cpp similarity index 100% rename from ReactCommon/turbomodule/core/TurboModuleUtils.cpp rename to ReactCommon/react/nativemodule/core/TurboModuleUtils.cpp diff --git a/ReactCommon/turbomodule/core/TurboModuleUtils.h b/ReactCommon/react/nativemodule/core/TurboModuleUtils.h similarity index 100% rename from ReactCommon/turbomodule/core/TurboModuleUtils.h rename to ReactCommon/react/nativemodule/core/TurboModuleUtils.h diff --git a/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.cpp b/ReactCommon/react/nativemodule/core/platform/android/JavaTurboModule.cpp similarity index 100% rename from ReactCommon/turbomodule/core/platform/android/JavaTurboModule.cpp rename to ReactCommon/react/nativemodule/core/platform/android/JavaTurboModule.cpp diff --git a/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.h b/ReactCommon/react/nativemodule/core/platform/android/JavaTurboModule.h similarity index 100% rename from ReactCommon/turbomodule/core/platform/android/JavaTurboModule.h rename to ReactCommon/react/nativemodule/core/platform/android/JavaTurboModule.h diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h b/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.h similarity index 100% rename from ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h rename to ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.h diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm b/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.mm similarity index 100% rename from ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm rename to ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.mm diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h b/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.h similarity index 100% rename from ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h rename to ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.h diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm b/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm similarity index 100% rename from ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm rename to ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm diff --git a/ReactCommon/turbomodule/samples/BUCK b/ReactCommon/react/nativemodule/samples/BUCK similarity index 97% rename from ReactCommon/turbomodule/samples/BUCK rename to ReactCommon/react/nativemodule/samples/BUCK index ae15f4706b606f..c760c2f06218ab 100644 --- a/ReactCommon/turbomodule/samples/BUCK +++ b/ReactCommon/react/nativemodule/samples/BUCK @@ -75,6 +75,6 @@ rn_xplat_cxx_library( ], exported_deps = [ "//xplat/jsi:jsi", - react_native_xplat_target("turbomodule/core:core"), + react_native_xplat_target("react/nativemodule/core:core"), ], ) diff --git a/ReactCommon/turbomodule/samples/NativeSampleTurboCxxModuleSpecJSI.cpp b/ReactCommon/react/nativemodule/samples/NativeSampleTurboCxxModuleSpecJSI.cpp similarity index 100% rename from ReactCommon/turbomodule/samples/NativeSampleTurboCxxModuleSpecJSI.cpp rename to ReactCommon/react/nativemodule/samples/NativeSampleTurboCxxModuleSpecJSI.cpp diff --git a/ReactCommon/turbomodule/samples/NativeSampleTurboCxxModuleSpecJSI.h b/ReactCommon/react/nativemodule/samples/NativeSampleTurboCxxModuleSpecJSI.h similarity index 100% rename from ReactCommon/turbomodule/samples/NativeSampleTurboCxxModuleSpecJSI.h rename to ReactCommon/react/nativemodule/samples/NativeSampleTurboCxxModuleSpecJSI.h diff --git a/ReactCommon/turbomodule/samples/SampleTurboCxxModule.cpp b/ReactCommon/react/nativemodule/samples/SampleTurboCxxModule.cpp similarity index 100% rename from ReactCommon/turbomodule/samples/SampleTurboCxxModule.cpp rename to ReactCommon/react/nativemodule/samples/SampleTurboCxxModule.cpp diff --git a/ReactCommon/turbomodule/samples/SampleTurboCxxModule.h b/ReactCommon/react/nativemodule/samples/SampleTurboCxxModule.h similarity index 100% rename from ReactCommon/turbomodule/samples/SampleTurboCxxModule.h rename to ReactCommon/react/nativemodule/samples/SampleTurboCxxModule.h diff --git a/ReactCommon/turbomodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h b/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h rename to ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h diff --git a/ReactCommon/turbomodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm b/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm rename to ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm diff --git a/ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboCxxModule.h b/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboCxxModule.h similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboCxxModule.h rename to ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboCxxModule.h diff --git a/ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboCxxModule.mm b/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboCxxModule.mm similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboCxxModule.mm rename to ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboCxxModule.mm diff --git a/ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboModule.h b/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.h similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboModule.h rename to ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.h diff --git a/ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboModule.mm b/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.mm similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboModule.mm rename to ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.mm diff --git a/ReactCommon/turbomodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp b/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp rename to ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp diff --git a/ReactCommon/turbomodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h b/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h rename to ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h diff --git a/packages/react-native-codegen/DEFS.bzl b/packages/react-native-codegen/DEFS.bzl index da49dac7fbdb32..33abd24eeaffe5 100644 --- a/packages/react-native-codegen/DEFS.bzl +++ b/packages/react-native-codegen/DEFS.bzl @@ -383,7 +383,7 @@ def rn_codegen_cxx_modules( ], visibility = ["PUBLIC"], exported_deps = [ - react_native_xplat_target("turbomodule/core:core"), + react_native_xplat_target("react/nativemodule/core:core"), ], ) diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 8b5b2eda2819e1..6f1ee93179b05f 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -524,7 +524,7 @@ SPEC CHECKSUMS: React-RCTText: 6c01963d3e562109f5548262b09b1b2bc260dd60 React-RCTVibration: 0997fcaf753c7ac0a341177db120eebc438484cf React-runtimeexecutor: 60dd6204a13f68a1aa1118870edcc604a791df2b - ReactCommon: 1f231b4fbed866032aa084ca23973063bbb51927 + ReactCommon: 982fb4cf650ef68a435f3c0bfff76fbdda1ba663 Yoga: f7fa200d8c49f97b54c9421079e781fb900b5cae YogaKit: f782866e155069a2cca2517aafea43200b01fd5a From 7b82df287d36e39073d29e3ae3adbe82cf424055 Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Sat, 19 Sep 2020 02:31:08 -0700 Subject: [PATCH 038/241] Notify ViewManagers when a View is deleted Summary: In a previous recent diff we changed Android's Delete mount instruction to *not* recursively delete the tree. This is fine, but because of that, we stopped calling `onDropViewInstance` when views are normally deleted. Bring back that behaviour. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D23801666 fbshipit-source-id: 54e6b52ab51fff2a45102e37077fe41081499888 --- .../facebook/react/fabric/mounting/MountingManager.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java index 41cf97c71fb095..6ff40bda0ffb18 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java @@ -399,7 +399,7 @@ public void removeViewAt(final int tag, final int parentTag, final int index) { } throw new IllegalStateException( - "Tried to delete view [" + "Tried to remove view [" + tag + "] of parent [" + parentTag @@ -586,6 +586,12 @@ public void deleteView(int reactTag) { // Additionally, as documented in `dropView`, we cannot always trust a // view's children to be up-to-date. mTagToViewState.remove(reactTag); + + // For non-root views we notify viewmanager with {@link ViewManager#onDropInstance} + ViewManager viewManager = viewState.mViewManager; + if (!viewState.mIsRoot && viewManager != null) { + viewManager.onDropViewInstance(viewState.mView); + } } @UiThread From 412fc7d3249a11e6a0af944303b8e8f6dd3791da Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Sun, 20 Sep 2020 08:16:56 -0700 Subject: [PATCH 039/241] Prevent calling onTextLayout with the same value Summary: Changelog: [internal] In D23648430 (https://github.com/facebook/react-native/commit/a315e4cd30e4b8da841f587650146a62c868f67d) I made a mistake. I prevented calling `onTextLayout` unless there are attachments in the component. It fixed the problem because I unintentionally prevented `onTextLayout` to be called. Therefore, changes from D23648430 (https://github.com/facebook/react-native/commit/a315e4cd30e4b8da841f587650146a62c868f67d) need to be reverted. To prevent infinite loop in `onTextLayout`, ParagraphEventEmitter checks if `linesMeasurements` have changed before dispatching it to JS. Reviewed By: shergin Differential Revision: D23782717 fbshipit-source-id: 0e84ae4f46d79ce0cf4c7340cd32be6f562ae179 --- .../components/text/ParagraphEventEmitter.cpp | 17 +++++++++------ .../components/text/ParagraphEventEmitter.h | 4 ++++ .../components/text/ParagraphShadowNode.cpp | 17 +++++---------- .../textlayoutmanager/TextMeasureCache.cpp | 21 ++++++++++++++++++- .../textlayoutmanager/TextMeasureCache.h | 2 ++ 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp b/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp index 1295108286f14b..ee16f5d02655c9 100644 --- a/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp +++ b/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp @@ -38,12 +38,17 @@ static jsi::Value linesMeasurementsPayload( void ParagraphEventEmitter::onTextLayout( LinesMeasurements const &linesMeasurements) const { - dispatchEvent( - "textLayout", - [linesMeasurements](jsi::Runtime &runtime) { - return linesMeasurementsPayload(runtime, linesMeasurements); - }, - EventPriority::AsynchronousBatched); + { + std::lock_guard guard(linesMeasurementsMutex_); + if (linesMeasurementsMetrics_ == linesMeasurements) { + return; + } + linesMeasurementsMetrics_ = linesMeasurements; + } + + dispatchEvent("textLayout", [linesMeasurements](jsi::Runtime &runtime) { + return linesMeasurementsPayload(runtime, linesMeasurements); + }); } } // namespace react diff --git a/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.h b/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.h index b73eb967d5afb2..597ef6baa2ddcb 100644 --- a/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.h +++ b/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.h @@ -18,6 +18,10 @@ class ParagraphEventEmitter : public ViewEventEmitter { using ViewEventEmitter::ViewEventEmitter; void onTextLayout(LinesMeasurements const &linesMeasurements) const; + + private: + mutable std::mutex linesMeasurementsMutex_; + mutable LinesMeasurements linesMeasurementsMetrics_; }; } // namespace react diff --git a/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp b/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp index b4fc65923a619d..2a7f0421dea480 100644 --- a/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -164,18 +164,6 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { updateStateIfNeeded(content); - if (content.attachments.empty()) { -#ifndef ANDROID - if (getConcreteProps().onTextLayout) { - // `onTextLayout` needs to be called even if text is empty - // to be compatible with Paper. - getConcreteEventEmitter().onTextLayout({}); - } -#endif - // No attachments, nothing to layout. - return; - } - auto measurement = textLayoutManager_->measure( AttributedStringBox{content.attributedString}, content.paragraphAttributes, @@ -191,6 +179,11 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { } #endif + if (content.attachments.empty()) { + // No attachments to layout. + return; + } + // Iterating on attachments, we clone shadow nodes and moving // `paragraphShadowNode` that represents clones of `this` object. auto paragraphShadowNode = static_cast(this); diff --git a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp index 70904ed8b64de8..26b50ffd184972 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp +++ b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp @@ -8,5 +8,24 @@ #include "TextMeasureCache.h" namespace facebook { -namespace react {} // namespace react +namespace react { + +bool LineMeasurement::operator==(LineMeasurement const &rhs) const { + return std::tie( + this->text, + this->frame, + this->descender, + this->capHeight, + this->ascender, + this->xHeight) == + std::tie( + rhs.text, + rhs.frame, + rhs.descender, + rhs.capHeight, + rhs.ascender, + rhs.xHeight); +} + +} // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index 4a15c17925684b..34a0a2d870581c 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -23,6 +23,8 @@ struct LineMeasurement { Float capHeight; Float ascender; Float xHeight; + + bool operator==(LineMeasurement const &rhs) const; }; using LinesMeasurements = std::vector; From 5be44456f2f15b0dbc7343033854bbf77efbfbd2 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Sun, 20 Sep 2020 14:21:52 -0700 Subject: [PATCH 040/241] TurboModule Android: compile TurboModule C++ Core into ReactAndroid Summary: This is to prepare for enabling TurboModule on Android. This commit compiles in all the core files (C++) into the ReactAndroid NDK build step. This doesn't yet enable TurboModule by default, just compiling in the infra, just like for iOS. New shared libs: * libreact_nativemodule_core.so: The TurboModule Android core * libreact_nativemodule_manager.so: The TurboModule manager/delegate To be compatible with ` ReactCommon}/LongLivedObject.cpp | 0 .../core/{ => ReactCommon}/LongLivedObject.h | 0 .../core/{ => ReactCommon}/TurboCxxModule.cpp | 0 .../core/{ => ReactCommon}/TurboCxxModule.h | 0 .../core/{ => ReactCommon}/TurboModule.cpp | 0 .../core/{ => ReactCommon}/TurboModule.h | 0 .../{ => ReactCommon}/TurboModuleBinding.cpp | 0 .../{ => ReactCommon}/TurboModuleBinding.h | 0 .../TurboModulePerfLogger.cpp | 0 .../{ => ReactCommon}/TurboModulePerfLogger.h | 0 .../{ => ReactCommon}/TurboModuleUtils.cpp | 0 .../core/{ => ReactCommon}/TurboModuleUtils.h | 0 .../{ => ReactCommon}/JavaTurboModule.cpp | 0 .../{ => ReactCommon}/JavaTurboModule.h | 0 packages/rn-tester/Podfile.lock | 2 +- 20 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 ReactCommon/react/nativemodule/core/Android.mk rename ReactCommon/react/nativemodule/core/{ => ReactCommon}/LongLivedObject.cpp (100%) rename ReactCommon/react/nativemodule/core/{ => ReactCommon}/LongLivedObject.h (100%) rename ReactCommon/react/nativemodule/core/{ => ReactCommon}/TurboCxxModule.cpp (100%) rename ReactCommon/react/nativemodule/core/{ => ReactCommon}/TurboCxxModule.h (100%) rename ReactCommon/react/nativemodule/core/{ => ReactCommon}/TurboModule.cpp (100%) rename ReactCommon/react/nativemodule/core/{ => ReactCommon}/TurboModule.h (100%) rename ReactCommon/react/nativemodule/core/{ => ReactCommon}/TurboModuleBinding.cpp (100%) rename ReactCommon/react/nativemodule/core/{ => ReactCommon}/TurboModuleBinding.h (100%) rename ReactCommon/react/nativemodule/core/{ => ReactCommon}/TurboModulePerfLogger.cpp (100%) rename ReactCommon/react/nativemodule/core/{ => ReactCommon}/TurboModulePerfLogger.h (100%) rename ReactCommon/react/nativemodule/core/{ => ReactCommon}/TurboModuleUtils.cpp (100%) rename ReactCommon/react/nativemodule/core/{ => ReactCommon}/TurboModuleUtils.h (100%) rename ReactCommon/react/nativemodule/core/platform/android/{ => ReactCommon}/JavaTurboModule.cpp (100%) rename ReactCommon/react/nativemodule/core/platform/android/{ => ReactCommon}/JavaTurboModule.h (100%) diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk index 80ba4096da6480..ba788dafca2713 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk @@ -5,6 +5,10 @@ LOCAL_PATH := $(call my-dir) +######################### +### callinvokerholder ### +######################### + include $(CLEAR_VARS) # Header search path for all source files in this module. @@ -15,10 +19,10 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall -LOCAL_STATIC_LIBRARIES = libcallinvoker libreactperfloggerjni - LOCAL_SHARED_LIBRARIES = libfb libfbjni +LOCAL_STATIC_LIBRARIES = libcallinvoker libreactperfloggerjni + # Name of this module. LOCAL_MODULE := callinvokerholder @@ -27,3 +31,30 @@ LOCAL_SRC_FILES := $(LOCAL_PATH)/ReactCommon/CallInvokerHolder.cpp # Build the files in this directory as a shared library include $(BUILD_STATIC_LIBRARY) + +################################## +### react_nativemodule_manager ### +################################## + +include $(CLEAR_VARS) + +# Name of this module. +LOCAL_MODULE := react_nativemodule_manager + +# Header search path for all source files in this module. +LOCAL_C_INCLUDES := $(LOCAL_PATH)/ReactCommon + +# Header search path for modules that depend on this module +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +LOCAL_SHARED_LIBRARIES = libfb libfbjni libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES = libcallinvokerholder libreactperfloggerjni + +# Compile all local c++ files +LOCAL_SRC_FILES := $(LOCAL_PATH)/ReactCommon/TurboModuleManager.cpp $(LOCAL_PATH)/ReactCommon/OnLoad.cpp + +# Build the files in this directory as a shared library +include $(BUILD_SHARED_LIBRARY) diff --git a/ReactAndroid/src/main/jni/react/jni/Android.mk b/ReactAndroid/src/main/jni/react/jni/Android.mk index 70b65082fac834..a816d4fea7aad3 100644 --- a/ReactAndroid/src/main/jni/react/jni/Android.mk +++ b/ReactAndroid/src/main/jni/react/jni/Android.mk @@ -80,7 +80,7 @@ LOCAL_LDLIBS += -landroid LOCAL_SHARED_LIBRARIES := libreactnativeutilsjni libfolly_json libfb libfbjni libglog_init libyoga # The static libraries (.a files) that this module depends on. -LOCAL_STATIC_LIBRARIES := libreactnative libcallinvokerholder libruntimeexecutor +LOCAL_STATIC_LIBRARIES := libreactnative libruntimeexecutor libcallinvokerholder # Name of this module. # @@ -128,8 +128,11 @@ $(call import-module,callinvoker) $(call import-module,reactperflogger) $(call import-module,hermes) $(call import-module,runtimeexecutor) +$(call import-module,react/nativemodule/core) include $(REACT_SRC_DIR)/reactperflogger/jni/Android.mk +# TODO (T48588859): Restructure this target to align with dir structure: "react/nativemodule/..." +# Note: Update this only when ready to minimize breaking changes. include $(REACT_SRC_DIR)/turbomodule/core/jni/Android.mk ifeq ($(BUILD_FABRIC),true) diff --git a/ReactCommon/ReactCommon.podspec b/ReactCommon/ReactCommon.podspec index 5c0efe17a630dd..0e306074b6239c 100644 --- a/ReactCommon/ReactCommon.podspec +++ b/ReactCommon/ReactCommon.podspec @@ -49,13 +49,13 @@ Pod::Spec.new do |s| ss.dependency "glog" ss.subspec "core" do |sss| - sss.source_files = "react/nativemodule/core/*.{cpp,h}", - "react/nativemodule/core/platform/ios/*.{mm,cpp,h}" + sss.source_files = "react/nativemodule/core/ReactCommon/**/*.{cpp,h}", + "react/nativemodule/core/platform/ios/**/*.{mm,cpp,h}" end ss.subspec "samples" do |sss| sss.source_files = "react/nativemodule/samples/*.{cpp,h}", - "react/nativemodule/samples/platform/ios/*.{mm,cpp,h}" + "react/nativemodule/samples/platform/ios/**/*.{mm,cpp,h}" sss.dependency "ReactCommon/turbomodule/core", version end end diff --git a/ReactCommon/react/nativemodule/core/Android.mk b/ReactCommon/react/nativemodule/core/Android.mk new file mode 100644 index 00000000000000..70f7651b4831f4 --- /dev/null +++ b/ReactCommon/react/nativemodule/core/Android.mk @@ -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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_nativemodule_core + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../ $(LOCAL_PATH)/ReactCommon $(LOCAL_PATH)/platform/android/ReactCommon + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/ReactCommon/*.cpp) $(wildcard $(LOCAL_PATH)/platform/android/ReactCommon/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/platform/android/ + +LOCAL_SHARED_LIBRARIES := libfbjni libfolly_json libreactnativejni + +LOCAL_STATIC_LIBRARIES := libjsi libreactperflogger + +LOCAL_CFLAGS := \ + -DLOG_TAG=\"ReactNative\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) + +$(call import-module,folly) +$(call import-module,jsi) +$(call import-module,reactperflogger) diff --git a/ReactCommon/react/nativemodule/core/BUCK b/ReactCommon/react/nativemodule/core/BUCK index 549ddef0a9e243..1bc3b9950df7ee 100644 --- a/ReactCommon/react/nativemodule/core/BUCK +++ b/ReactCommon/react/nativemodule/core/BUCK @@ -4,12 +4,12 @@ load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "FBJNI_TARGET", " rn_xplat_cxx_library( name = "core", srcs = glob( - ["*.cpp"], + ["ReactCommon/**/*.cpp"], ), header_namespace = "", exported_headers = subdir_glob( [ - ("", "*.h"), + ("ReactCommon", "*.h"), ], prefix = "ReactCommon", ), @@ -26,13 +26,13 @@ rn_xplat_cxx_library( ], fbandroid_exported_headers = subdir_glob( [ - ("platform/android", "*.h"), + ("platform/android/ReactCommon", "*.h"), ], prefix = "ReactCommon", ), fbandroid_srcs = glob( [ - "platform/android/**/*.cpp", + "platform/android/ReactCommon/*.cpp", ], ), fbobjc_compiler_flags = [ diff --git a/ReactCommon/react/nativemodule/core/LongLivedObject.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.cpp similarity index 100% rename from ReactCommon/react/nativemodule/core/LongLivedObject.cpp rename to ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.cpp diff --git a/ReactCommon/react/nativemodule/core/LongLivedObject.h b/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.h similarity index 100% rename from ReactCommon/react/nativemodule/core/LongLivedObject.h rename to ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.h diff --git a/ReactCommon/react/nativemodule/core/TurboCxxModule.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/TurboCxxModule.cpp similarity index 100% rename from ReactCommon/react/nativemodule/core/TurboCxxModule.cpp rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboCxxModule.cpp diff --git a/ReactCommon/react/nativemodule/core/TurboCxxModule.h b/ReactCommon/react/nativemodule/core/ReactCommon/TurboCxxModule.h similarity index 100% rename from ReactCommon/react/nativemodule/core/TurboCxxModule.h rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboCxxModule.h diff --git a/ReactCommon/react/nativemodule/core/TurboModule.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp similarity index 100% rename from ReactCommon/react/nativemodule/core/TurboModule.cpp rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp diff --git a/ReactCommon/react/nativemodule/core/TurboModule.h b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h similarity index 100% rename from ReactCommon/react/nativemodule/core/TurboModule.h rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h diff --git a/ReactCommon/react/nativemodule/core/TurboModuleBinding.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp similarity index 100% rename from ReactCommon/react/nativemodule/core/TurboModuleBinding.cpp rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp diff --git a/ReactCommon/react/nativemodule/core/TurboModuleBinding.h b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h similarity index 100% rename from ReactCommon/react/nativemodule/core/TurboModuleBinding.h rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h diff --git a/ReactCommon/react/nativemodule/core/TurboModulePerfLogger.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModulePerfLogger.cpp similarity index 100% rename from ReactCommon/react/nativemodule/core/TurboModulePerfLogger.cpp rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModulePerfLogger.cpp diff --git a/ReactCommon/react/nativemodule/core/TurboModulePerfLogger.h b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModulePerfLogger.h similarity index 100% rename from ReactCommon/react/nativemodule/core/TurboModulePerfLogger.h rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModulePerfLogger.h diff --git a/ReactCommon/react/nativemodule/core/TurboModuleUtils.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.cpp similarity index 100% rename from ReactCommon/react/nativemodule/core/TurboModuleUtils.cpp rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.cpp diff --git a/ReactCommon/react/nativemodule/core/TurboModuleUtils.h b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.h similarity index 100% rename from ReactCommon/react/nativemodule/core/TurboModuleUtils.h rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.h diff --git a/ReactCommon/react/nativemodule/core/platform/android/JavaTurboModule.cpp b/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp similarity index 100% rename from ReactCommon/react/nativemodule/core/platform/android/JavaTurboModule.cpp rename to ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp diff --git a/ReactCommon/react/nativemodule/core/platform/android/JavaTurboModule.h b/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h similarity index 100% rename from ReactCommon/react/nativemodule/core/platform/android/JavaTurboModule.h rename to ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 6f1ee93179b05f..35a1f000fa751e 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -524,7 +524,7 @@ SPEC CHECKSUMS: React-RCTText: 6c01963d3e562109f5548262b09b1b2bc260dd60 React-RCTVibration: 0997fcaf753c7ac0a341177db120eebc438484cf React-runtimeexecutor: 60dd6204a13f68a1aa1118870edcc604a791df2b - ReactCommon: 982fb4cf650ef68a435f3c0bfff76fbdda1ba663 + ReactCommon: e74025a9d02a02150f065719e14a8cbe03a93513 Yoga: f7fa200d8c49f97b54c9421079e781fb900b5cae YogaKit: f782866e155069a2cca2517aafea43200b01fd5a From e1b63ae17e0b4ad21e150ed2ea5bf276a94da770 Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Sun, 20 Sep 2020 14:51:44 -0700 Subject: [PATCH 041/241] Add additional logging and asserts to StubViewTree Summary: Helps in testing LayoutAnimations or differ changes. Changelog: [Internal] Reviewed By: fkgozali Differential Revision: D23797890 fbshipit-source-id: 1e612c04f9fbb256f2ace8a4a2ed9a477b4695a1 --- .../react/renderer/mounting/StubViewTree.cpp | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ReactCommon/react/renderer/mounting/StubViewTree.cpp b/ReactCommon/react/renderer/mounting/StubViewTree.cpp index fc68777f349fda..698af4374f2522 100644 --- a/ReactCommon/react/renderer/mounting/StubViewTree.cpp +++ b/ReactCommon/react/renderer/mounting/StubViewTree.cpp @@ -83,13 +83,15 @@ void StubViewTree::mutate( STUB_VIEW_ASSERT(registry.find(parentTag) != registry.end()); auto parentStubView = registry[parentTag]; auto childTag = mutation.newChildShadowView.tag; - STUB_VIEW_LOG({ - LOG(ERROR) << "StubView: Insert: " << childTag << " into " - << parentTag << " at " << mutation.index; - }); STUB_VIEW_ASSERT(registry.find(childTag) != registry.end()); auto childStubView = registry[childTag]; childStubView->update(mutation.newChildShadowView); + STUB_VIEW_LOG({ + LOG(ERROR) << "StubView: Insert: " << childTag << " into " + << parentTag << " at " << mutation.index << "(" + << parentStubView->children.size() << " children)"; + }); + STUB_VIEW_ASSERT(parentStubView->children.size() >= mutation.index); parentStubView->children.insert( parentStubView->children.begin() + mutation.index, childStubView); break; @@ -106,6 +108,7 @@ void StubViewTree::mutate( << parentTag << " at index " << mutation.index << " with " << parentStubView->children.size() << " children"; }); + STUB_VIEW_ASSERT(parentStubView->children.size() > mutation.index); STUB_VIEW_ASSERT(registry.find(childTag) != registry.end()); auto childStubView = registry[childTag]; bool childIsCorrect = @@ -130,8 +133,15 @@ void StubViewTree::mutate( STUB_VIEW_LOG({ LOG(ERROR) << "StubView: Update: " << mutation.newChildShadowView.tag; }); + + // We don't have a strict requirement that oldChildShadowView has any + // data. In particular, LayoutAnimations can produce UPDATEs with only a + // new node. STUB_VIEW_ASSERT( - mutation.newChildShadowView.tag == mutation.oldChildShadowView.tag); + mutation.newChildShadowView.tag == + mutation.oldChildShadowView.tag || + mutation.oldChildShadowView.tag == 0); + STUB_VIEW_ASSERT( registry.find(mutation.newChildShadowView.tag) != registry.end()); auto stubView = registry[mutation.newChildShadowView.tag]; From df9ada5fb7181763353f07470ef9224d49e4cb5f Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Sun, 20 Sep 2020 14:51:44 -0700 Subject: [PATCH 042/241] Deleting unnecessary Differentiator code Summary: In the new Flattening differ, I experimentally verified that these two code paths are not hit (or redundant) and deleted them. One of the branches did nothing and the other produced duplicate DELETE mutations for the same tag, that is handled elsewhere. Changelog: [Internal] Reviewed By: fkgozali Differential Revision: D23806161 fbshipit-source-id: 9ad2929e2d719a7b9b34640ed35f7a696103604b --- .../react/renderer/mounting/Differentiator.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/ReactCommon/react/renderer/mounting/Differentiator.cpp b/ReactCommon/react/renderer/mounting/Differentiator.cpp index 300c38059f8a64..d7fee08924385b 100644 --- a/ReactCommon/react/renderer/mounting/Differentiator.cpp +++ b/ReactCommon/react/renderer/mounting/Differentiator.cpp @@ -1076,11 +1076,7 @@ static void calculateShadowViewMutationsV2( for (auto &oldFlattenedNode : oldFlattenedNodes) { auto unvisitedOldChildPairIt = unvisitedOldChildPairs.find( oldFlattenedNode.shadowView.tag); - if (unvisitedOldChildPairIt != unvisitedOldChildPairs.end()) { - // Node unvisited - delete it entirely - deleteMutations.push_back(ShadowViewMutation::DeleteMutation( - oldFlattenedNode.shadowView)); - } else { + if (unvisitedOldChildPairIt == unvisitedOldChildPairs.end()) { // Node was visited - make sure to remove it from // "newRemainingPairs" map auto newRemainingIt = @@ -1202,11 +1198,7 @@ static void calculateShadowViewMutationsV2( for (auto &oldFlattenedNode : oldFlattenedNodes) { auto unvisitedOldChildPairIt = unvisitedOldChildPairs.find( oldFlattenedNode.shadowView.tag); - if (unvisitedOldChildPairIt != unvisitedOldChildPairs.end()) { - // Node unvisited - delete it entirely - deleteMutations.push_back(ShadowViewMutation::DeleteMutation( - oldFlattenedNode.shadowView)); - } else { + if (unvisitedOldChildPairIt == unvisitedOldChildPairs.end()) { // Node was visited - make sure to remove it from // "newRemainingPairs" map auto newRemainingIt = From 81c61705f78933ffd40731f2911aed1fb8415073 Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Sun, 20 Sep 2020 14:51:44 -0700 Subject: [PATCH 043/241] LayoutAnimations: simplify index adjustment, (un)flattening detection Summary: In this diff I simplify index adjustment and add comments to rigorously describe what we're doing at each step of index adjustment. I've also made unflattening detection more correct, robust, and slightly more efficient. Bugs that existed before: 1) The reparenting detection that existed in the animations layer had some subtle bugs. I don't have proof that it results in any correctness issues or crashes, but I suspect it. 2) Correctness of index adjustment: there were cases where the Android mounting layer would crash because LayoutAnimations would try to remove a View at an index, but the index was wrong. This is why I sat down and diagrammed the relationships between all the bits of data we have for index readjustment - I believe this to be correct now. 3) Correctness of INSERT index adjustment: I had the insight that when we have batches of INSERTs to consecutive indices, we essentially want them to be treated as a single INSERT for adjustment purposes, so that they're all placed consecutively in the view layer. I added `ConsecutiveAdjustmentMetadata` to deal with this, and there are more comments in the code. Changelog: [Internal] Reviewed By: yungsters Differential Revision: D23806163 fbshipit-source-id: cd9e94945034db8b840f2a806c6377034a91af61 --- .../LayoutAnimationKeyFrameManager.cpp | 449 ++++++++++-------- .../LayoutAnimationKeyFrameManager.h | 16 +- 2 files changed, 252 insertions(+), 213 deletions(-) diff --git a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp index 7599e85de2d62e..08fbe459d7a633 100644 --- a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp +++ b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -396,9 +397,12 @@ void LayoutAnimationKeyFrameManager:: adjustImmediateMutationIndicesForDelayedMutations( SurfaceId surfaceId, ShadowViewMutation &mutation, - ShadowViewMutationList *auxiliaryMutations) const { + ConsecutiveAdjustmentMetadata &consecutiveAdjustmentMetadata, + bool skipLastAnimation, + bool lastAnimationOnly) const { bool isRemoveMutation = mutation.type == ShadowViewMutation::Type::Remove; - assert(isRemoveMutation || mutation.type == ShadowViewMutation::Type::Insert); + bool isInsertMutation = mutation.type == ShadowViewMutation::Type::Insert; + assert(isRemoveMutation || isInsertMutation); // TODO: turn all of this into a lambda and share code? if (mutatedViewIsVirtual(mutation)) { @@ -412,36 +416,35 @@ void LayoutAnimationKeyFrameManager:: "[IndexAdjustment] Calling adjustImmediateMutationIndicesForDelayedMutations for:", mutation); + // When adjusting INSERTs, we want to batch adjacent inserts so they're all + // adjacent to each other in the resulting view. For instance, if we're + // processing INSERTS into positions 2,3,4,5,6, etc, it is possible that + // delayed mutations would otherwise cause the inserts to be adjusted to + // positions 2, 4, 6, 8... etc, creating a striping effect. We want to prevent + // that. + if (isInsertMutation && + mutation.parentShadowView.tag == + consecutiveAdjustmentMetadata.lastAdjustedParent && + mutation.index == (consecutiveAdjustmentMetadata.lastIndexOriginal + 1)) { + PrintMutationInstruction( + std::string( + "[IndexAdjustment] adjustImmediateMutationIndicesForDelayedMutations: Adjusting consecutive INSERT mutation by ") + + std::to_string(consecutiveAdjustmentMetadata.lastAdjustedDelta), + mutation); + consecutiveAdjustmentMetadata.lastIndexOriginal = mutation.index; + mutation.index += consecutiveAdjustmentMetadata.lastAdjustedDelta; + return; + } + // First, collect all final mutations that could impact this immediate // mutation. std::vector candidateMutations{}; - if (auxiliaryMutations != nullptr) { - for (auto &auxMutation : *auxiliaryMutations) { - if (auxMutation.parentShadowView.tag != mutation.parentShadowView.tag) { - continue; - } - if (auxMutation.type != ShadowViewMutation::Type::Remove) { - continue; - } - if (mutatedViewIsVirtual(auxMutation)) { - continue; - } - if (auxMutation.oldChildShadowView.tag == - (isRemoveMutation ? mutation.oldChildShadowView.tag - : mutation.newChildShadowView.tag)) { - continue; - } - - PrintMutationInstructionRelative( - "[IndexAdjustment] adjustImmediateMutationIndicesForDelayedMutations auxiliary CANDIDATE for:", - mutation, - auxMutation); - candidateMutations.push_back(&auxMutation); - } - } - - for (auto &inflightAnimation : inflightAnimations_) { + for (auto inflightAnimationIt = + inflightAnimations_.rbegin() + (skipLastAnimation ? 1 : 0); + inflightAnimationIt != inflightAnimations_.rend(); + inflightAnimationIt++) { + auto &inflightAnimation = *inflightAnimationIt; if (inflightAnimation.surfaceId != surfaceId) { continue; } @@ -464,9 +467,6 @@ void LayoutAnimationKeyFrameManager:: continue; } - if (animatedKeyFrame.type != AnimationConfigurationType::Noop) { - continue; - } if (!animatedKeyFrame.finalMutationForKeyFrame.has_value()) { continue; } @@ -491,21 +491,33 @@ void LayoutAnimationKeyFrameManager:: delayedMutation); candidateMutations.push_back(&delayedMutation); } + + if (lastAnimationOnly) { + break; + } } // While the mutation keeps being affected, keep checking. We use the vector // so we only perform one adjustment per delayed mutation. See comments at // bottom of adjustDelayedMutationIndicesForMutation for further explanation. bool changed = true; + int adjustedDelta = 0; + if (isInsertMutation) { + consecutiveAdjustmentMetadata.lastAdjustedParent = + mutation.parentShadowView.tag; + consecutiveAdjustmentMetadata.lastIndexOriginal = mutation.index; + } while (changed) { changed = false; candidateMutations.erase( std::remove_if( candidateMutations.begin(), candidateMutations.end(), - [&mutation, &changed](ShadowViewMutation *candidateMutation) { - if (candidateMutation->index <= mutation.index) { + [&](ShadowViewMutation *candidateMutation) { + bool indexConflicts = candidateMutation->index <= mutation.index; + if (indexConflicts) { mutation.index++; + adjustedDelta++; changed = true; PrintMutationInstructionRelative( "[IndexAdjustment] adjustImmediateMutationIndicesForDelayedMutations: Adjusting mutation UPWARD", @@ -517,19 +529,17 @@ void LayoutAnimationKeyFrameManager:: }), candidateMutations.end()); } -} - -void LayoutAnimationKeyFrameManager:: - adjustLastAnimationDelayedMutationIndicesForMutation( - SurfaceId surfaceId, - ShadowViewMutation const &mutation) const { - adjustDelayedMutationIndicesForMutation(surfaceId, mutation, true); + if (isInsertMutation) { + consecutiveAdjustmentMetadata.lastAdjustedDelta = adjustedDelta; + } else { + consecutiveAdjustmentMetadata.lastAdjustedParent = -1; + } } void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation( SurfaceId surfaceId, ShadowViewMutation const &mutation, - bool lastAnimationOnly) const { + bool skipLastAnimation) const { bool isRemoveMutation = mutation.type == ShadowViewMutation::Type::Remove; bool isInsertMutation = mutation.type == ShadowViewMutation::Type::Insert; assert(isRemoveMutation || isInsertMutation); @@ -545,7 +555,8 @@ void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation( // mutation. std::vector candidateMutations{}; - for (auto inflightAnimationIt = inflightAnimations_.rbegin(); + for (auto inflightAnimationIt = + inflightAnimations_.rbegin() + (skipLastAnimation ? 1 : 0); inflightAnimationIt != inflightAnimations_.rend(); inflightAnimationIt++) { auto &inflightAnimation = *inflightAnimationIt; @@ -572,9 +583,6 @@ void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation( continue; } - if (animatedKeyFrame.type != AnimationConfigurationType::Noop) { - continue; - } if (!animatedKeyFrame.finalMutationForKeyFrame.has_value()) { continue; } @@ -587,19 +595,19 @@ void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation( continue; } - if (!mutatedViewIsVirtual(*animatedKeyFrame.finalMutationForKeyFrame) && - finalAnimationMutation.type == ShadowViewMutation::Type::Remove) { - PrintMutationInstructionRelative( - "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: CANDIDATE:", - mutation, - *animatedKeyFrame.finalMutationForKeyFrame); - candidateMutations.push_back( - animatedKeyFrame.finalMutationForKeyFrame.get_pointer()); + if (finalAnimationMutation.type != ShadowViewMutation::Type::Remove) { + continue; + } + if (mutatedViewIsVirtual(*animatedKeyFrame.finalMutationForKeyFrame)) { + continue; } - } - if (lastAnimationOnly) { - break; + PrintMutationInstructionRelative( + "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: CANDIDATE:", + mutation, + *animatedKeyFrame.finalMutationForKeyFrame); + candidateMutations.push_back( + animatedKeyFrame.finalMutationForKeyFrame.get_pointer()); } } @@ -685,18 +693,6 @@ LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations( continue; } - // bool hasFinalMutation = - // animatedKeyFrame.finalMutationForKeyFrame.hasValue(); - // int finalMutationTag = hasFinalMutation - // ? (((*animatedKeyFrame.finalMutationForKeyFrame).type == - // ShadowViewMutation::Create || - // (*animatedKeyFrame.finalMutationForKeyFrame).type == - // ShadowViewMutation::Insert) - // ? (*animatedKeyFrame.finalMutationForKeyFrame) - // .newChildShadowView.tag - // : (*animatedKeyFrame.finalMutationForKeyFrame) - // .oldChildShadowView.tag) - // : -1; bool conflicting = animatedKeyFrame.tag == baselineShadowView.tag || ((mutation.type == ShadowViewMutation::Type::Delete || mutation.type == ShadowViewMutation::Type::Create) && @@ -704,20 +700,6 @@ LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations( finalMutationTag == baselineShadowView.tag*/ ; - // In some bizarre situations, there can be an ongoing Delete - // animation, and then a conflicting mutation to create and/or delete - // the same tag. In actuality this "bizarre" situation is just the - // animation of repeatedly flattening and unflattening a view; but - // it's not clear how to gracefully recover from this, so we just - // ensure that the Deletion is never executed in those cases. In these - // cases, the ongoing animation will stop; the view still exists; and - // then either a "Create" or "delete" animation will be recreated and - // executed for that tag. - bool shouldExecuteFinalMutation = - !(animatedKeyFrame.finalMutationForKeyFrame.hasValue() && - (*animatedKeyFrame.finalMutationForKeyFrame).type == - ShadowViewMutation::Delete); - // Conflicting animation detected: if we're mutating a tag under // animation, or deleting the parent of a tag under animation, or // reparenting. @@ -735,7 +717,9 @@ LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations( animatedKeyFrame.invalidated = true; - if (shouldExecuteFinalMutation) { + if (animatedKeyFrame.finalMutationForKeyFrame.has_value() && + !mutatedViewIsVirtual( + *animatedKeyFrame.finalMutationForKeyFrame)) { conflictingAnimations.push_back(std::make_tuple( animatedKeyFrame, *mutationConfig, &inflightAnimation)); } @@ -748,7 +732,7 @@ LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations( *animatedKeyFrame.finalMutationForKeyFrame); } else { PrintMutationInstruction( - "Found mutation that conflicts with existing in-flight animation:", + "Found mutation that conflicts with existing in-flight animation (no final mutation):", mutation); } #endif @@ -756,13 +740,6 @@ LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations( // Delete from existing animation it = inflightAnimation.keyFrames.erase(it); } else { - //#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - // if (hasFinalMutation) { - // PrintMutationInstructionRelative("getAndEraseConflictingAnimations, - // NOT erasing non-conflicting mutation of ", mutation, - // *animatedKeyFrame.finalMutationForKeyFrame); - // } - //#endif it++; } } @@ -844,7 +821,8 @@ LayoutAnimationKeyFrameManager::pullTransaction( if (keyframe.invalidated) { continue; } - if (keyframe.finalMutationForKeyFrame) { + if (keyframe.finalMutationForKeyFrame && + !mutatedViewIsVirtual(*keyframe.finalMutationForKeyFrame)) { std::string msg = "Animation " + std::to_string(i) + " keyframe " + std::to_string(j) + ": Final Animation"; PrintMutationInstruction(msg, *keyframe.finalMutationForKeyFrame); @@ -899,15 +877,23 @@ LayoutAnimationKeyFrameManager::pullTransaction( // being moves on the Differ level, since we know that there? We could use // TinyMap here, but it's not exposed by Differentiator (yet). std::vector insertedTags; - std::vector createdTags; + std::vector deletedTags; + std::vector reparentedTags; // tags that are deleted and recreated std::unordered_map movedTags; - std::vector reparentedTags; for (const auto &mutation : mutations) { if (mutation.type == ShadowViewMutation::Type::Insert) { insertedTags.push_back(mutation.newChildShadowView.tag); } + if (mutation.type == ShadowViewMutation::Type::Delete) { + deletedTags.push_back(mutation.oldChildShadowView.tag); + } if (mutation.type == ShadowViewMutation::Type::Create) { - createdTags.push_back(mutation.newChildShadowView.tag); + if (std::find( + deletedTags.begin(), + deletedTags.end(), + mutation.newChildShadowView.tag) != deletedTags.end()) { + reparentedTags.push_back(mutation.newChildShadowView.tag); + } } } @@ -954,26 +940,15 @@ LayoutAnimationKeyFrameManager::pullTransaction( // This should eventually be optimized out of the diffing algorithm, but // for now we detect reparenting and prevent the corresponding // Delete/Create instructions from being animated. - bool isReparented = - (mutation.type == ShadowViewMutation::Delete && - std::find( - createdTags.begin(), - createdTags.end(), - mutation.oldChildShadowView.tag) != createdTags.end()) || - (mutation.type == ShadowViewMutation::Create && - std::find( - reparentedTags.begin(), - reparentedTags.end(), - mutation.newChildShadowView.tag) != reparentedTags.end()); + bool isReparented = std::find( + reparentedTags.begin(), + reparentedTags.end(), + baselineShadowView.tag) != reparentedTags.end(); if (isRemoveReinserted) { movedTags.insert({mutation.oldChildShadowView.tag, mutation}); } - if (isReparented && mutation.type == ShadowViewMutation::Delete) { - reparentedTags.push_back(mutation.oldChildShadowView.tag); - } - // Inserts that follow a "remove" of the same tag should be treated as // an update (move) animation. bool wasInsertedTagRemoved = false; @@ -1205,30 +1180,31 @@ LayoutAnimationKeyFrameManager::pullTransaction( } #ifdef RN_SHADOW_TREE_INTROSPECTION +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING { - std::stringstream ss(getDebugDescription(immediateMutations, {})); - std::string to; - while (std::getline(ss, to, '\n')) { - LOG(ERROR) - << "LayoutAnimationKeyFrameManager.cpp: got IMMEDIATE list: Line: " - << to; + int idx = 0; + for (auto &mutation : immediateMutations) { + PrintMutationInstruction( + std::string("IMMEDIATE list: ") + std::to_string(idx) + "/" + + std::to_string(immediateMutations.size()), + mutation); + idx++; } } { + int idx = 0; for (const auto &keyframe : keyFramesToAnimate) { - if (keyframe.finalMutationForKeyFrame) { - std::stringstream ss( - getDebugDescription(*keyframe.finalMutationForKeyFrame, {})); - std::string to; - while (std::getline(ss, to, '\n')) { - LOG(ERROR) - << "LayoutAnimationKeyFrameManager.cpp: got FINAL list: Line: " - << to; - } + if (keyframe.finalMutationForKeyFrame.has_value()) { + PrintMutationInstruction( + std::string("FINAL list: ") + std::to_string(idx) + "/" + + std::to_string(keyFramesToAnimate.size()), + *keyframe.finalMutationForKeyFrame); } + idx++; } } +#endif #endif auto finalConflictingMutations = ShadowViewMutationList{}; @@ -1247,59 +1223,121 @@ LayoutAnimationKeyFrameManager::pullTransaction( finalConflictingMutations.end(), &shouldFirstComeBeforeSecondMutation); - // Use "final conflicting mutations" to adjust delayed mutations *before* - // we adjust immediate mutations based on delayed mutations + std::stable_sort( + immediateMutations.begin(), + immediateMutations.end(), + &shouldFirstComeBeforeSecondRemovesOnly); + + animation.keyFrames = keyFramesToAnimate; + inflightAnimations_.push_back(std::move(animation)); + + // At this point, we have the following information and knowledge graph: + // Knowledge Graph: + // [ImmediateMutations] -> assumes [FinalConflicting], [FrameDelayed], + // [Delayed] already executed [FrameDelayed] -> assumes + // [FinalConflicting], [Delayed] already executed [FinalConflicting] -> is + // adjusted based on [Delayed], no dependency on [FinalConflicting], + // [FrameDelayed] [Delayed] -> assumes [FinalConflicting], + // [ImmediateMutations] not executed yet + ConsecutiveAdjustmentMetadata consecutiveAdjustmentMetadata{}; + + // Adjust [Delayed] based on [FinalConflicting] + // Knowledge Graph: + // [ImmediateMutations] -> assumes [FinalConflicting], [FrameDelayed], + // [Delayed] already executed [FrameDelayed] -> assumes + // [FinalConflicting], [Delayed] already executed [FinalConflicting] -> is + // adjusted based on [Delayed], no dependency on [FinalConflicting], + // [FrameDelayed] [Delayed] -> adjusted for [FinalConflicting]; assumes + // [ImmediateMutations] not executed yet #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) - << "Adjust delayed mutations based on finalConflictingMutations"; + LOG(ERROR) << "Adjust [Delayed] based on [FinalConflicting]"; #endif for (auto &mutation : finalConflictingMutations) { - if (mutation.type == ShadowViewMutation::Remove || - mutation.type == ShadowViewMutation::Insert) { - adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + if (mutation.type == ShadowViewMutation::Type::Insert || + mutation.type == ShadowViewMutation::Type::Remove) { + adjustDelayedMutationIndicesForMutation(surfaceId, mutation, true); } } - // Adjust keyframes based on already-delayed, existing animations, before - // queueing. We adjust them as if finalConflictingMutations have already - // been executed - in all cases, finalConflictingMutations will be - // executed before any of these delayed mutations are. + // Adjust [FrameDelayed] based on [Delayed] + // Knowledge Graph: + // [ImmediateExecutions] -> assumes [FinalConflicting], [Delayed], + // [FrameDelayed] already executed [FrameDelayed] -> adjusted for + // [Delayed]; assumes [FinalConflicting] already executed + // [FinalConflicting] -> is adjusted based on [Delayed], no dependency on + // [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for + // [FinalConflicting]; assumes [ImmediateExecutions] not executed yet #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) - << "Adjust immediate keyFramesToAnimate based on delayed mutations and finalConflictingMutations"; + LOG(ERROR) << "Adjust [FrameDelayed] based on [Delayed]"; #endif - for (auto &keyframe : keyFramesToAnimate) { + for (auto &keyframe : inflightAnimations_.back().keyFrames) { if (keyframe.finalMutationForKeyFrame.has_value()) { - auto &delayedMutation = *keyframe.finalMutationForKeyFrame; - if (delayedMutation.type == ShadowViewMutation::Type::Remove) { + auto &mutation = *keyframe.finalMutationForKeyFrame; + if (mutation.type == ShadowViewMutation::Type::Insert || + mutation.type == ShadowViewMutation::Type::Remove) { + // When adjusting, skip adjusting against last animation - because + // all `mutation`s here come from the last animation, so we can't + // adjust a batch against itself. adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, delayedMutation /*, &finalConflictingMutations*/); + surfaceId, mutation, consecutiveAdjustmentMetadata, true); } } } - // REMOVE mutations from this animation batch *cannot* be impacted by - // other REMOVEs from this batch, since they're already taken into - // account. INSERTs can impact delayed REMOVEs; see below. + // Adjust [ImmediateExecutions] based on [Delayed] + // Knowledge Graph: + // [ImmediateExecutions] -> adjusted for [FrameDelayed], [Delayed]; + // assumes [FinalConflicting] already executed [FrameDelayed] -> adjusted + // for [Delayed]; assumes [FinalConflicting] already executed + // [FinalConflicting] -> is adjusted based on [Delayed], no dependency on + // [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for + // [FinalConflicting]; assumes [ImmediateExecutions] not executed yet #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) - << "Adjust immediateMutations REMOVEs only, based on previously delayed mutations, without most-recent animation"; + LOG(ERROR) << "Adjust [ImmediateExecutions] based on [Delayed]"; #endif - std::stable_sort( - immediateMutations.begin(), - immediateMutations.end(), - &shouldFirstComeBeforeSecondRemovesOnly); + consecutiveAdjustmentMetadata = ConsecutiveAdjustmentMetadata{}; for (auto &mutation : immediateMutations) { - if (mutation.type == ShadowViewMutation::Type::Remove) { + // Note: when adjusting [ImmediateExecutions] based on [FrameDelayed], + // we need only adjust Inserts. Since inserts are executed + // highest-index-first, lower indices being delayed does not impact the + // higher-index removals; and conversely, higher indices being delayed + // cannot impact lower index removal, regardless of order. + if (mutation.type == ShadowViewMutation::Type::Insert || + mutation.type == ShadowViewMutation::Type::Remove) { adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, mutation); - adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + surfaceId, + mutation, + consecutiveAdjustmentMetadata, + mutation.type == ShadowViewMutation::Type::Remove); } } - animation.keyFrames = keyFramesToAnimate; - inflightAnimations_.push_back(std::move(animation)); + // Adjust [Delayed] based on [ImmediateExecutions] and [FinalConflicting] + // Knowledge Graph: + // [ImmediateExecutions] -> adjusted for [FrameDelayed], [Delayed]; + // assumes [FinalConflicting] already executed [FrameDelayed] -> adjusted + // for [Delayed]; assumes [FinalConflicting] already executed + // [FinalConflicting] -> is adjusted based on [Delayed], no dependency on + // [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for + // [FinalConflicting], [ImmediateExecutions] +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) + << "Adjust [Delayed] based on [ImmediateExecutions] and [FinalConflicting]"; +#endif + for (auto &mutation : immediateMutations) { + if (mutation.type == ShadowViewMutation::Type::Insert || + mutation.type == ShadowViewMutation::Type::Remove) { + // Here we need to adjust both Delayed and FrameDelayed mutations. + // Delayed Removes can be impacted by non-delayed Inserts from the + // same frame. + adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + } + } + // If the knowledge graph progression above is correct, it is now safe to + // execute finalConflictingMutations and immediateMutations in that order, + // and to queue the delayed animations from this frame. + // // Execute the conflicting, delayed operations immediately. Any UPDATE // operations that smoothly transition into another animation will be // overridden by generated UPDATE operations at the end of the list, and @@ -1307,72 +1345,75 @@ LayoutAnimationKeyFrameManager::pullTransaction( // Additionally, this should allow us to avoid performing index adjustment // between this list of conflicting animations and the batch we're about // to execute. - mutations = ShadowViewMutationList{}; - for (auto &mutation : finalConflictingMutations) { - mutations.push_back(mutation); + finalConflictingMutations.insert( + finalConflictingMutations.end(), + immediateMutations.begin(), + immediateMutations.end()); + mutations = finalConflictingMutations; + } /* if (currentAnimation) */ else { + // If there's no "next" animation, make sure we queue up "final" + // operations from all ongoing, conflicting animations. +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) << "No Animation: Queue up final conflicting animations"; +#endif + ShadowViewMutationList finalMutationsForConflictingAnimations{}; + for (auto &conflictingKeyframeTuple : conflictingAnimations) { + auto &keyFrame = std::get<0>(conflictingKeyframeTuple); + if (keyFrame.finalMutationForKeyFrame.hasValue()) { + PrintMutationInstruction( + "No Animation: Queueing final mutation instruction", + *keyFrame.finalMutationForKeyFrame); + finalMutationsForConflictingAnimations.push_back( + *keyFrame.finalMutationForKeyFrame); + } } - // Before computing mutations based on animations / final mutations for - // this frame, we want to update any pending final mutations since they - // will execute *after* this batch of immediate mutations. Important case - // to consider (as an example, there are other interesting cases): there's - // a delayed "Remove", then an immediate "insert" is scheduled for an - // earlier index with the same parent. The remove needs to be adjusted - // upward here. Conversely, Inserts at later indices will assume the - // remove has already been executed, which may not be the case. + // Make sure that all operations execute in the proper order. + // REMOVE operations with highest indices must operate first. + std::stable_sort( + finalMutationsForConflictingAnimations.begin(), + finalMutationsForConflictingAnimations.end(), + &shouldFirstComeBeforeSecondMutation); + #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING LOG(ERROR) - << "Adjust immediateMutations and delayed mutations, including just-queued animations, based on each one"; + << "No Animation: Adjust delayed mutations based on all finalMutationsForConflictingAnimations"; #endif - for (auto &mutation : immediateMutations) { - if (mutation.type == ShadowViewMutation::Type::Remove) { - adjustLastAnimationDelayedMutationIndicesForMutation( - surfaceId, mutation); - } else if (mutation.type == ShadowViewMutation::Type::Insert) { - adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, mutation); + for (auto &mutation : finalMutationsForConflictingAnimations) { + if (mutation.type == ShadowViewMutation::Type::Remove || + mutation.type == ShadowViewMutation::Type::Insert) { adjustDelayedMutationIndicesForMutation(surfaceId, mutation); } } - // These will be executed immediately. These should already be sorted - // properly. - mutations.insert( - mutations.end(), - immediateMutations.begin(), - immediateMutations.end()); - } /* if (currentAnimation) */ else { // The ShadowTree layer doesn't realize that certain operations have been // delayed, so we must adjust all Remove and Insert operations based on // what else has been deferred, whether we are executing this immediately // or later. +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) + << "No Animation: Adjust mutations based on remaining delayed mutations"; +#endif + ConsecutiveAdjustmentMetadata consecutiveAdjustmentMetadata{}; for (auto &mutation : mutations) { if (mutation.type == ShadowViewMutation::Type::Remove || mutation.type == ShadowViewMutation::Type::Insert) { adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, mutation); - adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + surfaceId, mutation, consecutiveAdjustmentMetadata); } } - // If there's no "next" animation, make sure we queue up "final" - // operations from all ongoing, conflicting animations. - ShadowViewMutationList finalMutationsForConflictingAnimations{}; - for (auto &conflictingKeyframeTuple : conflictingAnimations) { - auto &keyFrame = std::get<0>(conflictingKeyframeTuple); - if (keyFrame.finalMutationForKeyFrame.hasValue()) { - finalMutationsForConflictingAnimations.push_back( - *keyFrame.finalMutationForKeyFrame); +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) + << "No Animation: Adjust delayed mutations based on all immediate mutations"; +#endif + for (auto &mutation : mutations) { + if (mutation.type == ShadowViewMutation::Type::Remove || + mutation.type == ShadowViewMutation::Type::Insert) { + adjustDelayedMutationIndicesForMutation(surfaceId, mutation); } } - // Make sure that all operations execute in the proper order. - // REMOVE operations with highest indices must operate first. - std::stable_sort( - finalMutationsForConflictingAnimations.begin(), - finalMutationsForConflictingAnimations.end(), - &shouldFirstComeBeforeSecondMutation); - // Append mutations to this list and swap - so that the final // conflicting mutations happen before any other mutations finalMutationsForConflictingAnimations.insert( @@ -1392,13 +1433,6 @@ LayoutAnimationKeyFrameManager::pullTransaction( ShadowViewMutationList mutationsForAnimation{}; animationMutationsForFrame(surfaceId, mutationsForAnimation, now); - // Erase any remaining animations that conflict with these mutations - // In some marginal cases, a DELETE animation can be queued up and a final - // DELETE mutation be executed by the animation driver. These cases deserve - // further scrutiny, but for now to prevent crashes, just make sure the queued - // DELETE operations are removed. - getAndEraseConflictingAnimations(surfaceId, mutationsForAnimation, true); - // If any delayed removes were executed, update remaining delayed keyframes #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING LOG(ERROR) @@ -1431,7 +1465,8 @@ LayoutAnimationKeyFrameManager::pullTransaction( if (keyframe.invalidated) { continue; } - if (keyframe.finalMutationForKeyFrame) { + if (keyframe.finalMutationForKeyFrame && + !mutatedViewIsVirtual(*keyframe.finalMutationForKeyFrame)) { std::string msg = "Animation " + std::to_string(i) + " keyframe " + std::to_string(j) + ": Final Animation"; PrintMutationInstruction(msg, *keyframe.finalMutationForKeyFrame); diff --git a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h index 686fcaa9da9e21..e6aba6e3c07bcd 100644 --- a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h +++ b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h @@ -110,6 +110,12 @@ struct AnimationKeyFrame { bool invalidated{false}; }; +struct ConsecutiveAdjustmentMetadata { + Tag lastAdjustedParent{-1}; + int lastAdjustedDelta{0}; + int lastIndexOriginal{0}; +}; + class LayoutAnimationCallbackWrapper { public: LayoutAnimationCallbackWrapper(jsi::Function &&callback) @@ -212,16 +218,14 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate, void adjustImmediateMutationIndicesForDelayedMutations( SurfaceId surfaceId, ShadowViewMutation &mutation, - ShadowViewMutationList *auxiliaryMutations = nullptr) const; + ConsecutiveAdjustmentMetadata &consecutiveAdjustmentMetadata, + bool skipLastAnimation = false, + bool lastAnimationOnly = false) const; void adjustDelayedMutationIndicesForMutation( SurfaceId surfaceId, ShadowViewMutation const &mutation, - bool lastAnimationOnly = false) const; - - void adjustLastAnimationDelayedMutationIndicesForMutation( - SurfaceId surfaceId, - ShadowViewMutation const &mutation) const; + bool skipLastAnimation = false) const; std::vector> getAndEraseConflictingAnimations( From 54016faa7dfdde321023e729636a45b5bb682856 Mon Sep 17 00:00:00 2001 From: Mike Vitousek Date: Sun, 20 Sep 2020 21:45:12 -0700 Subject: [PATCH 044/241] Deploy Flow v0.134.0 Summary: Changelog: [Internal] Reviewed By: dsainati1 Differential Revision: D23769795 fbshipit-source-id: 520e3974a53ba5961a081219c0cbf19b7dfade06 --- .flowconfig | 2 +- .flowconfig.android | 2 +- package.json | 2 +- repo-config/package.json | 2 +- template/_flowconfig | 2 +- yarn.lock | 8 ++++---- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.flowconfig b/.flowconfig index 75ab9756c8e742..edd3fc461862b4 100644 --- a/.flowconfig +++ b/.flowconfig @@ -75,4 +75,4 @@ untyped-import untyped-type-import [version] -^0.133.0 +^0.134.0 diff --git a/.flowconfig.android b/.flowconfig.android index 622c4e6b937556..63d4ec5bcecaaa 100644 --- a/.flowconfig.android +++ b/.flowconfig.android @@ -75,4 +75,4 @@ untyped-import untyped-type-import [version] -^0.133.0 +^0.134.0 diff --git a/package.json b/package.json index dc045489e56b55..8289be281dbbe8 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "ws": "^6.1.4" }, "devDependencies": { - "flow-bin": "^0.133.0", + "flow-bin": "^0.134.0", "react": "16.13.1" }, "detox": { diff --git a/repo-config/package.json b/repo-config/package.json index 1e36007fd97303..ea2ac36be0f543 100644 --- a/repo-config/package.json +++ b/repo-config/package.json @@ -33,7 +33,7 @@ "eslint-plugin-react-hooks": "^4.0.7", "eslint-plugin-react-native": "3.8.1", "eslint-plugin-relay": "1.7.1", - "flow-bin": "^0.133.0", + "flow-bin": "^0.134.0", "jest": "^26.0.1", "jest-junit": "^10.0.0", "jscodeshift": "^0.9.0", diff --git a/template/_flowconfig b/template/_flowconfig index 8c7fe9a703f98e..b1e00ccf3aa23f 100644 --- a/template/_flowconfig +++ b/template/_flowconfig @@ -63,4 +63,4 @@ untyped-import untyped-type-import [version] -^0.133.0 +^0.134.0 diff --git a/yarn.lock b/yarn.lock index 05196e9070286f..b609e76d6021d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3261,10 +3261,10 @@ flatted@^2.0.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== -flow-bin@^0.133.0: - version "0.133.0" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.133.0.tgz#2ee44e3f5d0c0256cfe8e99d9a85e9801c281c50" - integrity sha512-01T5g8GdhtJEn+lhAwuv5zkrMStrmkuHrY3Nn9/aS9y6waNmNgimMKlzRpFH66S0F6Ez9EqU9psz5QaRveSJIA== +flow-bin@^0.134.0: + version "0.134.0" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.134.0.tgz#e98e5724f6ed5a1265cf904bbb5e4c096ea3a026" + integrity sha512-j5aCugO3jmwDsUKc+7KReArgnL6aVjHLo6DlozKhxKYN+TaP8BY+mintPSISjSQtKZFJyvoNAc1oXA79X5WjIA== flow-parser@0.*: version "0.89.0" From 3f85b83653411d8eaf120d30fd16e1337d56af2f Mon Sep 17 00:00:00 2001 From: generatedunixname89002005287564 Date: Mon, 21 Sep 2020 04:36:22 -0700 Subject: [PATCH 045/241] Daily `arc lint --take CLANGFORMAT` Reviewed By: zertosh Differential Revision: D23811656 fbshipit-source-id: 4104948d2e4db786998320bcfdb1598d4a497f2d --- .../RCTSurfacePresenterBridgeAdapter.mm | 2 +- .../tests/ShadowTreeLifeCycleTest.cpp | 18 ++-- .../renderer/uimanager/UIManagerBinding.cpp | 86 +++++++++---------- 3 files changed, 55 insertions(+), 51 deletions(-) diff --git a/React/Fabric/RCTSurfacePresenterBridgeAdapter.mm b/React/Fabric/RCTSurfacePresenterBridgeAdapter.mm index 8c20e934870864..a70c494d937838 100644 --- a/React/Fabric/RCTSurfacePresenterBridgeAdapter.mm +++ b/React/Fabric/RCTSurfacePresenterBridgeAdapter.mm @@ -48,7 +48,7 @@ static RuntimeExecutor RCTRuntimeExecutorFromBridge(RCTBridge *bridge) auto bridgeWeakWrapper = wrapManagedObjectWeakly([bridge batchedBridge] ?: bridge); RuntimeExecutor runtimeExecutor = [bridgeWeakWrapper]( - std::function &&callback) { + std::function &&callback) { RCTBridge *bridge = unwrapManagedObjectWeakly(bridgeWeakWrapper); RCTAssert(bridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil at the moment of scheduling a call."); diff --git a/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp b/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp index 46d2fc4c6cdd25..1ecfd21a69b670 100644 --- a/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp +++ b/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp @@ -12,8 +12,8 @@ #include #include -#include #include +#include #include #include "Entropy.h" @@ -105,19 +105,23 @@ static void testShadowNodeTreeLifeCycle( auto mutations = calculateShadowViewMutations( *currentRootNode, *nextRootNode, useFlattener); - // If using flattener: make sure that in a single frame, a DELETE for a view is not - // followed by a CREATE for the same view. + // If using flattener: make sure that in a single frame, a DELETE for a + // view is not followed by a CREATE for the same view. if (useFlattener) { std::vector deletedTags{}; - for (auto const& mutation : mutations) { + for (auto const &mutation : mutations) { if (mutation.type == ShadowViewMutation::Type::Delete) { deletedTags.push_back(mutation.oldChildShadowView.tag); } } - for (auto const& mutation : mutations) { + for (auto const &mutation : mutations) { if (mutation.type == ShadowViewMutation::Type::Create) { - if (std::find(deletedTags.begin(), deletedTags.end(), mutation.newChildShadowView.tag) != deletedTags.end()) { - LOG(ERROR) << "Deleted tag was recreated in mutations list: [" << mutation.newChildShadowView.tag << "]"; + if (std::find( + deletedTags.begin(), + deletedTags.end(), + mutation.newChildShadowView.tag) != deletedTags.end()) { + LOG(ERROR) << "Deleted tag was recreated in mutations list: [" + << mutation.newChildShadowView.tag << "]"; FAIL(); } } diff --git a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index 024eb3424dc573..14eee6775f405b 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -212,10 +212,10 @@ jsi::Value UIManagerBinding::get( name, 5, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { return valueFromShadowNode( runtime, uiManager->createNode( @@ -234,10 +234,10 @@ jsi::Value UIManagerBinding::get( name, 1, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { return valueFromShadowNode( runtime, uiManager->cloneNode(shadowNodeFromValue(runtime, arguments[0]))); @@ -250,10 +250,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { uiManager->setJSResponder( shadowNodeFromValue(runtime, arguments[0]), arguments[1].getBool()); @@ -268,10 +268,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { auto node = shadowNodeFromValue(runtime, arguments[0]); auto locationX = (Float)arguments[1].getNumber(); auto locationY = (Float)arguments[2].getNumber(); @@ -298,10 +298,10 @@ jsi::Value UIManagerBinding::get( name, 0, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { uiManager->clearJSResponder(); return jsi::Value::undefined(); @@ -315,10 +315,10 @@ jsi::Value UIManagerBinding::get( name, 1, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { return valueFromShadowNode( runtime, uiManager->cloneNode( @@ -334,10 +334,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { auto const &rawProps = RawProps(runtime, arguments[1]); return valueFromShadowNode( runtime, @@ -355,10 +355,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { auto const &rawProps = RawProps(runtime, arguments[1]); return valueFromShadowNode( runtime, @@ -375,10 +375,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { uiManager->appendChild( shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1])); @@ -391,10 +391,10 @@ jsi::Value UIManagerBinding::get( runtime, name, 1, - [](jsi::Runtime &runtime, + [](jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { auto shadowNodeList = std::make_shared(SharedShadowNodeList({})); return valueFromShadowNodeList(runtime, shadowNodeList); @@ -406,10 +406,10 @@ jsi::Value UIManagerBinding::get( runtime, name, 2, - [](jsi::Runtime &runtime, + [](jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { auto shadowNodeList = shadowNodeListFromValue(runtime, arguments[0]); auto shadowNode = shadowNodeFromValue(runtime, arguments[1]); shadowNodeList->push_back(shadowNode); @@ -425,11 +425,11 @@ jsi::Value UIManagerBinding::get( runtime, name, 2, - [uiManager, sharedUIManager = uiManager_]( - jsi::Runtime &runtime, + [ uiManager, sharedUIManager = uiManager_ ]( + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { auto surfaceId = surfaceIdFromValue(runtime, arguments[0]); auto shadowNodeList = shadowNodeListFromValue(runtime, arguments[1]); @@ -453,10 +453,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { uiManager->completeSurface( surfaceIdFromValue(runtime, arguments[0]), shadowNodeListFromValue(runtime, arguments[1])); @@ -472,10 +472,10 @@ jsi::Value UIManagerBinding::get( name, 1, [this]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { auto eventHandler = arguments[0].getObject(runtime).getFunction(runtime); eventHandler_ = @@ -490,10 +490,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1]).get(), @@ -514,10 +514,10 @@ jsi::Value UIManagerBinding::get( name, 3, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { uiManager->dispatchCommand( shadowNodeFromValue(runtime, arguments[0]), stringFromValue(runtime, arguments[1]), @@ -534,10 +534,10 @@ jsi::Value UIManagerBinding::get( name, 4, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1]).get(), @@ -570,10 +570,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), nullptr, @@ -605,10 +605,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), nullptr, @@ -640,10 +640,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { uiManager->setNativeProps( *shadowNodeFromValue(runtime, arguments[0]), RawProps(runtime, arguments[1])); @@ -658,10 +658,10 @@ jsi::Value UIManagerBinding::get( name, 3, [uiManager]( - jsi::Runtime &runtime, + jsi::Runtime & runtime, jsi::Value const &thisValue, jsi::Value const *arguments, - size_t count) noexcept -> jsi::Value { + size_t count) noexcept->jsi::Value { uiManager->configureNextLayoutAnimation( runtime, // TODO: pass in JSI value instead of folly::dynamic to RawValue From 22b5f32f74bc99be5a4d2f0ea987fa1b08a1c901 Mon Sep 17 00:00:00 2001 From: Andrei Shikov Date: Mon, 21 Sep 2020 07:34:43 -0700 Subject: [PATCH 046/241] Add extras to timespan and points in performance logger Summary: Changelog: [Internal][Added] Added point-level extras to performance logger Reviewed By: lunaleaps, rubennorte Differential Revision: D23730275 fbshipit-source-id: 285c5d7ac769bd109df7ce0294da024401edf7d3 --- .../__tests__/PerformanceLogger-test.js | 22 +++++++++ .../Utilities/createPerformanceLogger.js | 45 +++++++++++++++---- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/Libraries/Utilities/__tests__/PerformanceLogger-test.js b/Libraries/Utilities/__tests__/PerformanceLogger-test.js index ea940f10947785..a762157a1654dd 100644 --- a/Libraries/Utilities/__tests__/PerformanceLogger-test.js +++ b/Libraries/Utilities/__tests__/PerformanceLogger-test.js @@ -21,6 +21,8 @@ const EXTRA_VALUE_2 = ''; const POINT = ''; const POINT_TIMESTAMP = 99; const POINT_TIMESTAMP_2 = 999; +const POINT_ANNOTATION_1 = {extra: 'value1'}; +const POINT_ANNOTATION_2 = {extra: 'value2'}; describe('PerformanceLogger', () => { beforeEach(() => { @@ -167,4 +169,24 @@ describe('PerformanceLogger', () => { checkLogger(localPerformanceLogger2, true); checkLogger(GlobalPerformanceLogger, true); }); + + it('records extras for a timespan', () => { + let perfLogger = createPerformanceLogger(); + perfLogger.startTimespan(TIMESPAN_1, POINT_ANNOTATION_1); + perfLogger.stopTimespan(TIMESPAN_1, POINT_ANNOTATION_2); + expect(perfLogger.getTimespans()[TIMESPAN_1].startExtras).toEqual( + POINT_ANNOTATION_1, + ); + expect(perfLogger.getTimespans()[TIMESPAN_1].endExtras).toEqual( + POINT_ANNOTATION_2, + ); + }); + + it('records extras for a point', () => { + let perfLogger = createPerformanceLogger(); + perfLogger.markPoint(POINT, POINT_TIMESTAMP, POINT_ANNOTATION_1); + + expect(Object.keys(perfLogger.getPointExtras())).toEqual([POINT]); + expect(perfLogger.getPointExtras()[POINT]).toEqual(POINT_ANNOTATION_1); + }); }); diff --git a/Libraries/Utilities/createPerformanceLogger.js b/Libraries/Utilities/createPerformanceLogger.js index c9baf0cd07aa90..195c1b30807c7a 100644 --- a/Libraries/Utilities/createPerformanceLogger.js +++ b/Libraries/Utilities/createPerformanceLogger.js @@ -20,15 +20,25 @@ type Timespan = { startTime: number, endTime?: number, totalTime?: number, + startExtras?: Extras, + endExtras?: Extras, }; // Extra values should be serializable primitives type ExtraValue = number | string | boolean; +type Extras = {[key: string]: ExtraValue}; + export interface IPerformanceLogger { - addTimespan(key: string, startTime: number, endTime: number): void; - startTimespan(key: string): void; - stopTimespan(key: string): void; + addTimespan( + key: string, + startTime: number, + endTime: number, + startExtras?: Extras, + endExtras?: Extras, + ): void; + startTimespan(key: string, extras?: Extras): void; + stopTimespan(key: string, extras?: Extras): void; clear(): void; clearCompleted(): void; currentTimestamp(): number; @@ -37,8 +47,9 @@ export interface IPerformanceLogger { setExtra(key: string, value: ExtraValue): void; getExtras(): {[key: string]: ExtraValue, ...}; removeExtra(key: string): ExtraValue | void; - markPoint(key: string, timestamp?: number): void; + markPoint(key: string, timestamp?: number, extras?: Extras): void; getPoints(): {[key: string]: number, ...}; + getPointExtras(): {[key: string]: Extras, ...}; logEverything(): void; } @@ -50,8 +61,15 @@ class PerformanceLogger implements IPerformanceLogger { _timespans: {[key: string]: Timespan} = {}; _extras: {[key: string]: ExtraValue} = {}; _points: {[key: string]: number} = {}; + _pointExtras: {[key: string]: Extras, ...} = {}; - addTimespan(key: string, startTime: number, endTime: number) { + addTimespan( + key: string, + startTime: number, + endTime: number, + startExtras?: Extras, + endExtras?: Extras, + ) { if (this._timespans[key]) { if (PRINT_TO_CONSOLE && __DEV__) { infoLog( @@ -66,10 +84,12 @@ class PerformanceLogger implements IPerformanceLogger { startTime, endTime, totalTime: endTime - (startTime || 0), + startExtras, + endExtras, }; } - startTimespan(key: string) { + startTimespan(key: string, extras?: Extras) { if (this._timespans[key]) { if (PRINT_TO_CONSOLE && __DEV__) { infoLog( @@ -82,6 +102,7 @@ class PerformanceLogger implements IPerformanceLogger { this._timespans[key] = { startTime: performanceNow(), + startExtras: extras, }; _cookies[key] = Systrace.beginAsyncEvent(key); if (PRINT_TO_CONSOLE) { @@ -89,7 +110,7 @@ class PerformanceLogger implements IPerformanceLogger { } } - stopTimespan(key: string) { + stopTimespan(key: string, extras?: Extras) { const timespan = this._timespans[key]; if (!timespan || timespan.startTime == null) { if (PRINT_TO_CONSOLE && __DEV__) { @@ -110,6 +131,7 @@ class PerformanceLogger implements IPerformanceLogger { return; } + timespan.endExtras = extras; timespan.endTime = performanceNow(); timespan.totalTime = timespan.endTime - (timespan.startTime || 0); if (PRINT_TO_CONSOLE) { @@ -179,7 +201,7 @@ class PerformanceLogger implements IPerformanceLogger { return value; } - markPoint(key: string, timestamp?: number) { + markPoint(key: string, timestamp?: number, extras?: Extras) { if (this._points[key]) { if (PRINT_TO_CONSOLE && __DEV__) { infoLog( @@ -190,12 +212,19 @@ class PerformanceLogger implements IPerformanceLogger { return; } this._points[key] = timestamp ?? performanceNow(); + if (extras) { + this._pointExtras[key] = extras; + } } getPoints() { return this._points; } + getPointExtras() { + return this._pointExtras; + } + logEverything() { if (PRINT_TO_CONSOLE) { // log timespans From e1bf515ae8e77fb24f76037d9f22e903799fb637 Mon Sep 17 00:00:00 2001 From: Agastya Darma Date: Mon, 21 Sep 2020 10:06:25 -0700 Subject: [PATCH 047/241] Making Android versionCodeOverride for new apps using the template human-readable (#29808) Summary: The current calculation on versionCodeOverride is not human-readable. Imagine if we have an android app with **versionName** `4.0` and **version code** `4`. In the current implementation, the result of **versionCode** `4` for `armeabi-v7a` will be the seemingly arbitrary **1048580**. This makes the version code to be seemingly arbitrary and hard to read for humans. This PR proposes to change this calculation closer to google implementation of build number in Flutter (`abiVersionCode * 1000 + variant.versionCode`). https://github.com/flutter/flutter/blob/39d7a019c150ca421b980426e85b254a0ec63ebd/packages/flutter_tools/gradle/flutter.gradle#L643-L647 With this change, our app with `versionCode 4 versionName "4.0"` for `armeabi-v7a` will have **1004** as the version code instead of the seemingly arbitrary **1048580**. As you can see adopting the flutter style implementation make the version code easier to read and debug. **1004** **1** - The ABI Type `["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]` **004** - Our versionCode. Hopefully, this can prevent future issues like this https://github.com/facebook/react-native/issues/29790. ## Changelog [Android] [Changed] - Making Android versionCodeOverride for new apps using the template human-readable Pull Request resolved: https://github.com/facebook/react-native/pull/29808 Reviewed By: sammy-SC Differential Revision: D23804632 Pulled By: fkgozali fbshipit-source-id: 89b2c196b3bfe01fa608dfb595db6d3314ca1d63 --- template/android/app/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/template/android/app/build.gradle b/template/android/app/build.gradle index a5a0ea4e8bc90c..f75c160968e7eb 100644 --- a/template/android/app/build.gradle +++ b/template/android/app/build.gradle @@ -171,11 +171,12 @@ android { variant.outputs.each { output -> // For each separate APK per architecture, set a unique version code as described here: // https://developer.android.com/studio/build/configure-apk-splits.html + // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc. def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] def abi = output.getFilter(OutputFile.ABI) if (abi != null) { // null for the universal-debug, universal-release variants output.versionCodeOverride = - versionCodes.get(abi) * 1048576 + defaultConfig.versionCode + defaultConfig.versionCode * 1000 + versionCodes.get(abi) } } From 8cca54a7a76f49d263517da40e050d19b7c1e546 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Mon, 21 Sep 2020 11:06:54 -0700 Subject: [PATCH 048/241] Codegen Android: adjust JNI output to compile from Gradle Summary: This adjusted the C++ output for Android codegen (NativeModule specs) so we can compile it with ndk-build in Gradle: * Use `#include` instead of `#import` for header files * Added `#pragma once` * Removed direct include of `` -- this is not necessary * Added generated Android.mk file based on library name Changelog: [Internal] Reviewed By: shergin Differential Revision: D23809082 fbshipit-source-id: 11ddfea7b48c8b2eb6efe885641ace4fc327d50d --- .../modules/GenerateModuleJniCpp.js | 1 + .../generators/modules/GenerateModuleJniH.js | 52 +++- .../GenerateModuleJniCpp-test.js.snap | 7 + .../GenerateModuleJniH-test.js.snap | 280 +++++++++++++++--- scripts/generate-native-modules-specs-cli.js | 5 +- 5 files changed, 294 insertions(+), 51 deletions(-) diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js index 1af032bbc26e1e..aac88138b51f45 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js @@ -67,6 +67,7 @@ namespace react { std::shared_ptr ::_LIBRARY_NAME_::_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) { ::_MODULE_LOOKUP_:: + return nullptr; } } // namespace react diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniH.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniH.js index 1b50545f5f4071..4d9a5aac0cf688 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniH.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniH.js @@ -23,8 +23,7 @@ public: }; `; -const template = ` -/** +const template = `/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -33,10 +32,11 @@ const template = ` * ${'@'}generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -49,6 +49,35 @@ std::shared_ptr ::_LIBRARY_NAME_::_ModuleProvider(const std::string } // namespace facebook `; +const androidMkTemplate = `# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := ::_LIBRARY_NAME_:: + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\ + -DLOG_TAG=\\"ReactNative\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) +`; + module.exports = { generate( libraryName: string, @@ -76,6 +105,15 @@ module.exports = { const replacedTemplate = template .replace(/::_MODULES_::/g, modules) .replace(/::_LIBRARY_NAME_::/g, libraryName); - return new Map([[fileName, replacedTemplate]]); + return new Map([ + [fileName, replacedTemplate], + [ + 'Android.mk', + androidMkTemplate.replace( + /::_LIBRARY_NAME_::/g, + `react_codegen_${libraryName.toLowerCase()}`, + ), + ], + ]); }, }; diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap index d12482b2b57f7a..47f3a3168ef989 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap @@ -46,6 +46,7 @@ std::shared_ptr COMPLEX_OBJECTS_ModuleProvider(const std::string mo if (moduleName == \\"SampleTurboModule\\") { return std::make_shared(params); } + return nullptr; } } // namespace react @@ -82,6 +83,7 @@ std::shared_ptr EMPTY_NATIVE_MODULES_ModuleProvider(const std::stri if (moduleName == \\"SampleTurboModule\\") { return std::make_shared(params); } + return nullptr; } } // namespace react @@ -123,6 +125,7 @@ std::shared_ptr NATIVE_MODULES_WITH_TYPE_ALIASES_ModuleProvider(con if (moduleName == \\"AliasTurboModule\\") { return std::make_shared(params); } + return nullptr; } } // namespace react @@ -218,6 +221,7 @@ std::shared_ptr REAL_MODULE_EXAMPLE_ModuleProvider(const std::strin if (moduleName == \\"ExceptionsManager\\") { return std::make_shared(params); } + return nullptr; } } // namespace react @@ -307,6 +311,7 @@ std::shared_ptr SIMPLE_NATIVE_MODULES_ModuleProvider(const std::str if (moduleName == \\"SampleTurboModule\\") { return std::make_shared(params); } + return nullptr; } } // namespace react @@ -360,6 +365,7 @@ std::shared_ptr TWO_MODULES_DIFFERENT_FILES_ModuleProvider(const st if (moduleName == \\"Sample2TurboModule\\") { return std::make_shared(params); } + return nullptr; } } // namespace react @@ -411,6 +417,7 @@ std::shared_ptr TWO_MODULES_SAME_FILE_ModuleProvider(const std::str if (moduleName == \\"Sample2TurboModule\\") { return std::make_shared(params); } + return nullptr; } } // namespace react diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap index afb55a73886ba9..efe08283828084 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap @@ -2,8 +2,7 @@ exports[`GenerateModuleJniH can generate fixture COMPLEX_OBJECTS 1`] = ` Map { - "SampleSpec.h" => " -/** + "SampleSpec.h" => "/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -12,10 +11,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -33,14 +33,41 @@ std::shared_ptr COMPLEX_OBJECTS_ModuleProvider(const std::string mo } // namespace react } // namespace facebook +", + "Android.mk" => "# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_complex_objects + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; exports[`GenerateModuleJniH can generate fixture EMPTY_NATIVE_MODULES 1`] = ` Map { - "SampleSpec.h" => " -/** + "SampleSpec.h" => "/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -49,10 +76,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -70,14 +98,41 @@ std::shared_ptr EMPTY_NATIVE_MODULES_ModuleProvider(const std::stri } // namespace react } // namespace facebook +", + "Android.mk" => "# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_empty_native_modules + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; exports[`GenerateModuleJniH can generate fixture NATIVE_MODULES_WITH_TYPE_ALIASES 1`] = ` Map { - "SampleSpec.h" => " -/** + "SampleSpec.h" => "/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -86,10 +141,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -107,14 +163,41 @@ std::shared_ptr NATIVE_MODULES_WITH_TYPE_ALIASES_ModuleProvider(con } // namespace react } // namespace facebook +", + "Android.mk" => "# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_native_modules_with_type_aliases + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; exports[`GenerateModuleJniH can generate fixture REAL_MODULE_EXAMPLE 1`] = ` Map { - "SampleSpec.h" => " -/** + "SampleSpec.h" => "/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -123,10 +206,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -160,14 +244,41 @@ std::shared_ptr REAL_MODULE_EXAMPLE_ModuleProvider(const std::strin } // namespace react } // namespace facebook +", + "Android.mk" => "# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_real_module_example + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; exports[`GenerateModuleJniH can generate fixture SIMPLE_NATIVE_MODULES 1`] = ` Map { - "SampleSpec.h" => " -/** + "SampleSpec.h" => "/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -176,10 +287,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -197,14 +309,41 @@ std::shared_ptr SIMPLE_NATIVE_MODULES_ModuleProvider(const std::str } // namespace react } // namespace facebook +", + "Android.mk" => "# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_simple_native_modules + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; exports[`GenerateModuleJniH can generate fixture TWO_MODULES_DIFFERENT_FILES 1`] = ` Map { - "SampleSpec.h" => " -/** + "SampleSpec.h" => "/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -213,10 +352,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -242,14 +382,41 @@ std::shared_ptr TWO_MODULES_DIFFERENT_FILES_ModuleProvider(const st } // namespace react } // namespace facebook +", + "Android.mk" => "# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_two_modules_different_files + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; exports[`GenerateModuleJniH can generate fixture TWO_MODULES_SAME_FILE 1`] = ` Map { - "SampleSpec.h" => " -/** + "SampleSpec.h" => "/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -258,10 +425,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -287,6 +455,34 @@ std::shared_ptr TWO_MODULES_SAME_FILE_ModuleProvider(const std::str } // namespace react } // namespace facebook +", + "Android.mk" => "# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_two_modules_same_file + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; diff --git a/scripts/generate-native-modules-specs-cli.js b/scripts/generate-native-modules-specs-cli.js index 003c4105ab2024..a668c58b9ccecc 100644 --- a/scripts/generate-native-modules-specs-cli.js +++ b/scripts/generate-native-modules-specs-cli.js @@ -90,8 +90,9 @@ function generateSpec( files .filter( f => - f.startsWith(moduleSpecName) && - (f.endsWith('.h') || f.endsWith('.cpp')), + f === 'Android.mk' || + (f.startsWith(moduleSpecName) && + (f.endsWith('.h') || f.endsWith('.cpp'))), ) .forEach(f => { fs.copyFileSync( From 2b46fd065f8af654929e651cd3ee30a468e4db95 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Mon, 21 Sep 2020 11:06:54 -0700 Subject: [PATCH 049/241] Codegen Android: Compile ReactAndroid codegen C++ output Summary: Now that the react-native-codegen produces the Android.mk for the JNI C++ output, let's compile them into its own shared library, to be included in the ReactAndroid final deliverable: libreact_codegen_reactandroidspec.so. This is gated behind `USE_CODEGEN` env var. Note: RNTester specific C++ spec files are not compiled here. Changelog: [Internal] Reviewed By: shergin Differential Revision: D23809081 fbshipit-source-id: 7a90f420a923d5d02654facac01ffe025c321e44 --- ReactAndroid/build.gradle | 7 ++++++- ReactAndroid/src/main/jni/Application.mk | 2 +- ReactAndroid/src/main/jni/react/jni/Android.mk | 4 ++++ .../react/codegen/plugin/CodegenPlugin.java | 15 +++++++++++---- .../codegen/plugin/CodegenPluginExtension.java | 1 - packages/rn-tester/android/app/build.gradle | 1 - 6 files changed, 22 insertions(+), 8 deletions(-) diff --git a/ReactAndroid/build.gradle b/ReactAndroid/build.gradle index 1189ead6e9a4c4..1c0a3a0f9b301a 100644 --- a/ReactAndroid/build.gradle +++ b/ReactAndroid/build.gradle @@ -33,6 +33,9 @@ def thirdPartyNdkDir = new File("$buildDir/third-party-ndk") // - glog-0.3.5 def dependenciesPath = System.getenv("REACT_NATIVE_DEPENDENCIES") +// The 'USE_CODEGEN' environment variable will use codegen and compile the output. +def enableCodegen = (System.getenv('USE_CODEGEN') ?: '0').toBoolean() + // The 'USE_FABRIC' environment variable will build Fabric C++ code into the bundle // USE_FABRIC=0 will build RN excluding fabric // USE_FABRIC=1 will build RN including fabric @@ -301,6 +304,7 @@ def getNdkBuildFullPath() { def buildReactNdkLib = tasks.register("buildReactNdkLib", Exec) { dependsOn(prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog, extractAARHeaders, extractJNIFiles) + dependsOn("generateCodegenArtifactsFromSchema"); inputs.dir("$projectDir/../ReactCommon") inputs.dir("src/main/jni") @@ -314,8 +318,10 @@ def buildReactNdkLib = tasks.register("buildReactNdkLib", Exec) { "NDK_LIBS_OUT=$buildDir/react-ndk/all", "THIRD_PARTY_NDK_DIR=$thirdPartyNdkDir", "REACT_COMMON_DIR=$projectDir/../ReactCommon", + "REACT_GENERATED_SRC_DIR=$buildDir/generated/source", "REACT_SRC_DIR=$projectDir/src/main/java/com/facebook/react", "BUILD_FABRIC=$enableFabric", + "USE_CODEGEN=$enableCodegen", "-C", file("src/main/jni/react/jni").absolutePath, "--jobs", project.findProperty("jobs") ?: Runtime.runtime.availableProcessors() ) @@ -471,7 +477,6 @@ dependencies { apply(from: "release.gradle") react { - enableCodegen = System.getenv("USE_CODEGEN") ?: false jsRootDir = file("../Libraries") reactNativeRootDir = file("$rootDir") useJavaGenerator = System.getenv("USE_CODEGEN_JAVAPOET") ?: false diff --git a/ReactAndroid/src/main/jni/Application.mk b/ReactAndroid/src/main/jni/Application.mk index 84eae80adf4c9d..8bcc6cfa8b8817 100644 --- a/ReactAndroid/src/main/jni/Application.mk +++ b/ReactAndroid/src/main/jni/Application.mk @@ -22,7 +22,7 @@ APP_MK_DIR := $(dir $(lastword $(MAKEFILE_LIST))) # Where are APP_MK_DIR, THIRD_PARTY_NDK_DIR, etc. defined? # The directories inside NDK_MODULE_PATH (ex: APP_MK_DIR, THIRD_PARTY_NDK_DIR, # etc.) are defined inside build.gradle. -NDK_MODULE_PATH := $(APP_MK_DIR)$(HOST_DIRSEP)$(THIRD_PARTY_NDK_DIR)$(HOST_DIRSEP)$(REACT_COMMON_DIR)$(HOST_DIRSEP)$(APP_MK_DIR)first-party$(HOST_DIRSEP)$(REACT_SRC_DIR) +NDK_MODULE_PATH := $(APP_MK_DIR)$(HOST_DIRSEP)$(THIRD_PARTY_NDK_DIR)$(HOST_DIRSEP)$(REACT_COMMON_DIR)$(HOST_DIRSEP)$(APP_MK_DIR)first-party$(HOST_DIRSEP)$(REACT_SRC_DIR)$(HOST_DIRSEP)$(REACT_GENERATED_SRC_DIR) APP_STL := c++_shared diff --git a/ReactAndroid/src/main/jni/react/jni/Android.mk b/ReactAndroid/src/main/jni/react/jni/Android.mk index a816d4fea7aad3..b4ba8ddce6a245 100644 --- a/ReactAndroid/src/main/jni/react/jni/Android.mk +++ b/ReactAndroid/src/main/jni/react/jni/Android.mk @@ -148,3 +148,7 @@ include $(REACT_SRC_DIR)/jscexecutor/Android.mk include $(REACT_SRC_DIR)/../hermes/reactexecutor/Android.mk include $(REACT_SRC_DIR)/../hermes/instrumentation/Android.mk include $(REACT_SRC_DIR)/modules/blob/jni/Android.mk + +ifeq ($(USE_CODEGEN),true) + include $(REACT_GENERATED_SRC_DIR)/codegen/jni/Android.mk +endif diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java index 68cd4139a64ec0..1d58a2e561259d 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java @@ -25,6 +25,12 @@ public class CodegenPlugin implements Plugin { public void apply(final Project project) { + // This flag should have been defined in CodegenPluginExtension, but the extension values + // resolution is pending project full evaluation. Given that no codegen actual task should + // be defined if the flag is not enabled, read directly from env var here. + final String useCodegenVar = System.getenv("USE_CODEGEN"); + final boolean enableCodegen = + useCodegenVar != null && (Boolean.parseBoolean(useCodegenVar) || useCodegenVar.equals("1")); final CodegenPluginExtension extension = project.getExtensions().create("react", CodegenPluginExtension.class, project); @@ -39,7 +45,8 @@ public void apply(final Project project) { "generateCodegenSchemaFromJavaScript", Exec.class, task -> { - if (!extension.enableCodegen) { + if (!enableCodegen) { + task.commandLine("echo", "Skipping: not using react-native-codegen."); return; } @@ -79,7 +86,8 @@ public void apply(final Project project) { "generateCodegenArtifactsFromSchema", Exec.class, task -> { - if (!extension.enableCodegen) { + if (!enableCodegen) { + task.commandLine("echo", "Skipping: not using react-native-codegen."); return; } @@ -116,7 +124,7 @@ public void apply(final Project project) { // Note: This last step needs to happen after the project has been evaluated. project.afterEvaluate( s -> { - if (!extension.enableCodegen) { + if (!enableCodegen) { return; } @@ -142,7 +150,6 @@ public void apply(final Project project) { .getByName("main") .getJava() .srcDir(new File(generatedSrcDir, "java")); - // TODO: Add JNI sources. }); } diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPluginExtension.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPluginExtension.java index 97817617d947a5..fe540dd353dc92 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPluginExtension.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPluginExtension.java @@ -15,7 +15,6 @@ public class CodegenPluginExtension { // TODO: Remove beta. public String codegenJavaPackageName = "com.facebook.fbreact.specs.beta"; - public boolean enableCodegen = false; public File jsRootDir; public String libraryName; public String[] nodeExecutableAndArgs = {"node"}; diff --git a/packages/rn-tester/android/app/build.gradle b/packages/rn-tester/android/app/build.gradle index 3bad38b8dc353f..38b128c87a23ef 100644 --- a/packages/rn-tester/android/app/build.gradle +++ b/packages/rn-tester/android/app/build.gradle @@ -220,7 +220,6 @@ dependencies { } react { - enableCodegen = System.getenv("USE_CODEGEN") ?: false jsRootDir = file("$rootDir/packages/rn-tester") reactNativeRootDir = file("$rootDir") useJavaGenerator = System.getenv("USE_CODEGEN_JAVAPOET") ?: false From 30d170adc6b0d5edfc2fad525e2e8ab07866f771 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Mon, 21 Sep 2020 11:07:12 -0700 Subject: [PATCH 050/241] Use Element<> in FindNodeAtPointTest Summary: Changelog: [Internal] Reviewed By: JoshuaGross Differential Revision: D23815171 fbshipit-source-id: bf420be172a55a966f8881371473e121c3848c78 --- .../core/tests/FindNodeAtPointTest.cpp | 239 +++++++++--------- 1 file changed, 123 insertions(+), 116 deletions(-) diff --git a/ReactCommon/react/renderer/core/tests/FindNodeAtPointTest.cpp b/ReactCommon/react/renderer/core/tests/FindNodeAtPointTest.cpp index 79787b4bf9bfef..f84a89cf60a66b 100644 --- a/ReactCommon/react/renderer/core/tests/FindNodeAtPointTest.cpp +++ b/ReactCommon/react/renderer/core/tests/FindNodeAtPointTest.cpp @@ -6,134 +6,141 @@ */ #include +#include +#include + #include "TestComponent.h" using namespace facebook::react; -/* - *┌─────────────────────────┐ - *│nodeA_ │ - *│ │ - *│ │ - *│ │ - *│ │ - *│ │ - *│ │ - *│ ┌────────────────┐ │ - *│ │nodeAA_ │ │ - *│ │ │ │ - *│ │ ┌───────┐ │ │ - *│ │ │nodeAA_│ │ │ - *│ │ │ │ │ │ - *│ │ └───────┘ │ │ - *│ └────────────────┘ │ - *└─────────────────────────┘ - */ -class FindNodeAtPointTest : public ::testing::Test { - protected: - FindNodeAtPointTest() - : eventDispatcher_(std::shared_ptr()), - componentDescriptor_(TestComponentDescriptor({eventDispatcher_})) { - auto traits = TestShadowNode::BaseTraits(); - - auto familyA = std::make_shared( - ShadowNodeFamilyFragment{ - /* .tag = */ 9, - /* .surfaceId = */ 1, - /* .eventEmitter = */ nullptr, - }, - eventDispatcher_, - componentDescriptor_); - - nodeA_ = std::make_shared( - ShadowNodeFragment{ - /* .props = */ std::make_shared(), - /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), - }, - familyA, - traits); - - auto familyAA = std::make_shared( - ShadowNodeFamilyFragment{ - /* .tag = */ 10, - /* .surfaceId = */ 1, - /* .eventEmitter = */ nullptr, - }, - eventDispatcher_, - componentDescriptor_); - - nodeAA_ = std::make_shared( - ShadowNodeFragment{ - /* .props = */ std::make_shared(), - /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), - }, - familyAA, - traits); - - auto familyAAA = std::make_shared( - ShadowNodeFamilyFragment{ - /* .tag = */ 11, - /* .surfaceId = */ 1, - /* .eventEmitter = */ nullptr, - }, - eventDispatcher_, - componentDescriptor_); - - nodeAAA_ = std::make_shared( - ShadowNodeFragment{ - /* .props = */ std::make_shared(), - /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), - }, - familyAAA, - traits); - - nodeA_->appendChild(nodeAA_); - nodeAA_->appendChild(nodeAAA_); - - auto layoutMetrics = EmptyLayoutMetrics; - - layoutMetrics.frame = facebook::react::Rect{ - facebook::react::Point{0, 0}, facebook::react::Size{1000, 1000}}; - nodeA_->setLayoutMetrics(layoutMetrics); - - layoutMetrics.frame = facebook::react::Rect{ - facebook::react::Point{100, 100}, facebook::react::Size{100, 100}}; - nodeAA_->setLayoutMetrics(layoutMetrics); - - layoutMetrics.frame = facebook::react::Rect{facebook::react::Point{10, 10}, - facebook::react::Size{10, 10}}; - nodeAAA_->setLayoutMetrics(layoutMetrics); - } - - std::shared_ptr eventDispatcher_; - std::shared_ptr nodeA_; - std::shared_ptr nodeAA_; - std::shared_ptr nodeAAA_; - TestComponentDescriptor componentDescriptor_; -}; - -TEST_F(FindNodeAtPointTest, withoutTransform) { +TEST(FindNodeAtPointTest, withoutTransform) { + auto builder = simpleComponentBuilder(); + + // clang-format off + auto element = + Element() + .tag(1) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {1000, 1000}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(2) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {100, 100}; + layoutMetrics.frame.size = {100, 100}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(3) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {10, 10}; + layoutMetrics.frame.size = {10, 10}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + }) + }); + + auto parentShadowNode = builder.build(element); + EXPECT_EQ( - LayoutableShadowNode::findNodeAtPoint(nodeA_, {115, 115}), nodeAAA_); - EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {105, 105}), nodeAA_); - EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {900, 900}), nodeA_); + LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {115, 115})->getTag(), 3); + EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {105, 105})->getTag(), 2); + EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {900, 900})->getTag(), 1); EXPECT_EQ( - LayoutableShadowNode::findNodeAtPoint(nodeA_, {1001, 1001}), nullptr); + LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {1001, 1001}), nullptr); } -TEST_F(FindNodeAtPointTest, viewIsTranslated) { - nodeA_->_contentOriginOffset = {-100, -100}; +TEST(FindNodeAtPointTest, viewIsTranslated) { + auto builder = simpleComponentBuilder(); + + // clang-format off + auto element = + Element() + .tag(1) + .finalize([](ScrollViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {1000, 1000}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .stateData([](ScrollViewState &data) { + data.contentOffset = {100, 100}; + }) + .children({ + Element() + .tag(2) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {100, 100}; + layoutMetrics.frame.size = {100, 100}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(3) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {10, 10}; + layoutMetrics.frame.size = {10, 10}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + }) + }); + + auto parentShadowNode = builder.build(element); EXPECT_EQ( - LayoutableShadowNode::findNodeAtPoint(nodeA_, {15, 15})->getTag(), - nodeAAA_->getTag()); - EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {5, 5}), nodeAA_); + LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {15, 15})->getTag(), + 3); + EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {5, 5})->getTag(), 2); } -TEST_F(FindNodeAtPointTest, viewIsScaled) { - nodeAAA_->_transform = Transform::Identity() * Transform::Scale(0.5, 0.5, 0); +TEST(FindNodeAtPointTest, viewIsScaled) { + auto builder = simpleComponentBuilder(); + + // clang-format off + auto element = + Element() + .tag(1) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {1000, 1000}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(2) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {100, 100}; + layoutMetrics.frame.size = {100, 100}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(3) + .props([] { + auto sharedProps = std::make_shared(); + sharedProps->transform = Transform::Scale(0.5, 0.5, 0); + return sharedProps; + }) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {10, 10}; + layoutMetrics.frame.size = {10, 10}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + }) + }); + + auto parentShadowNode = builder.build(element); EXPECT_EQ( - LayoutableShadowNode::findNodeAtPoint(nodeA_, {119, 119})->getTag(), - nodeAA_->getTag()); + LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {119, 119})->getTag(), + 2); } From 18f29db5a765b1e3523773aad8301c0e7e52ab9a Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Mon, 21 Sep 2020 11:07:12 -0700 Subject: [PATCH 051/241] Fix ordering of children in LayoutableShadowNode::findNodeAtPoint Summary: Changelog: [internal] `LayoutableShadowNode::findNodeAtPoint` was iterating children in incorrect order and wasn't taking zIndex into accout. Reviewed By: JoshuaGross Differential Revision: D23814866 fbshipit-source-id: 38eee297147a5c5912304d139bb10f8b16ae2ee1 --- .../renderer/core/LayoutableShadowNode.cpp | 12 ++- .../core/tests/FindNodeAtPointTest.cpp | 83 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp b/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp index 51b29867787525..3809740c83e72d 100644 --- a/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp +++ b/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp @@ -238,7 +238,17 @@ ShadowNode::Shared LayoutableShadowNode::findNodeAtPoint( auto newPoint = point - transformedFrame.origin - layoutableShadowNode->getContentOriginOffset(); - for (const auto &childShadowNode : node->getChildren()) { + + auto sortedChildren = node->getChildren(); + std::stable_sort( + sortedChildren.begin(), + sortedChildren.end(), + [](auto const &lhs, auto const &rhs) -> bool { + return lhs->getOrderIndex() < rhs->getOrderIndex(); + }); + + for (auto it = sortedChildren.rbegin(); it != sortedChildren.rend(); it++) { + auto const &childShadowNode = *it; auto hitView = findNodeAtPoint(childShadowNode, newPoint); if (hitView) { return hitView; diff --git a/ReactCommon/react/renderer/core/tests/FindNodeAtPointTest.cpp b/ReactCommon/react/renderer/core/tests/FindNodeAtPointTest.cpp index f84a89cf60a66b..37f1c4106db603 100644 --- a/ReactCommon/react/renderer/core/tests/FindNodeAtPointTest.cpp +++ b/ReactCommon/react/renderer/core/tests/FindNodeAtPointTest.cpp @@ -144,3 +144,86 @@ TEST(FindNodeAtPointTest, viewIsScaled) { LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {119, 119})->getTag(), 2); } + +TEST(FindNodeAtPointTest, overlappingViews) { + auto builder = simpleComponentBuilder(); + + // clang-format off + auto element = + Element() + .tag(1) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {100, 100}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(2) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {25, 25}; + layoutMetrics.frame.size = {50, 50}; + shadowNode.setLayoutMetrics(layoutMetrics); + }), + Element() + .tag(3) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {50, 50}; + layoutMetrics.frame.size = {50, 50}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + }); + + auto parentShadowNode = builder.build(element); + + EXPECT_EQ( + LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {50, 50})->getTag(), 3); +} + +TEST(FindNodeAtPointTest, overlappingViewsWithZIndex) { + auto builder = simpleComponentBuilder(); + + // clang-format off + auto element = + Element() + .tag(1) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {100, 100}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(2) + .props([] { + auto sharedProps = std::make_shared(); + sharedProps->zIndex = 1; + auto &yogaStyle = sharedProps->yogaStyle; + yogaStyle.positionType() = YGPositionTypeAbsolute; + return sharedProps; + }) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {25, 25}; + layoutMetrics.frame.size = {50, 50}; + shadowNode.setLayoutMetrics(layoutMetrics); + }), + Element() + .tag(3) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {50, 50}; + layoutMetrics.frame.size = {50, 50}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + }); + + auto parentShadowNode = builder.build(element); + + EXPECT_EQ( + LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {50, 50})->getTag(), 2); +} + + From 23717e48aff3d7fdaea30c9b8dcdd6cfbb7802d5 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Mon, 21 Sep 2020 17:28:47 -0700 Subject: [PATCH 052/241] Call stopObserving on correct queue Summary: Since `dealloc` can be called from any thread, this would result `stopObserving` being called on a different thread/queue than the specified `methodQueue`. We specifically encountered this issue with a module needing the main queue having its `stopObserving` called on a background queue. Changelog: [iOS][Fixed] - Call [RCTEventEmitter stopObserving] on specified method queue Reviewed By: RSNara Differential Revision: D23821741 fbshipit-source-id: 693c3be6876f863da6dd214a829af2cc13a09c3f --- Libraries/NativeAnimation/RCTNativeAnimatedModule.mm | 1 + Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm | 1 + Libraries/Network/RCTNetworking.mm | 4 +++- React/CoreModules/RCTAppState.h | 2 +- React/CoreModules/RCTAppState.mm | 5 ----- React/CoreModules/RCTDevSettings.mm | 1 + React/CoreModules/RCTWebSocketModule.mm | 2 ++ React/Modules/RCTEventEmitter.h | 4 +++- React/Modules/RCTEventEmitter.m | 2 +- 9 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.mm b/Libraries/NativeAnimation/RCTNativeAnimatedModule.mm index 35adf39289aa07..33fe87ad4d4a10 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.mm +++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.mm @@ -46,6 +46,7 @@ - (instancetype)init - (void)invalidate { + [super invalidate]; [_nodesManager stopAnimationLoop]; [self.bridge.eventDispatcher removeDispatchObserver:self]; [self.bridge.uiManager.observerCoordinator removeObserver:self]; diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm b/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm index 6fc594872f874b..45213380d81d7a 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm +++ b/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm @@ -47,6 +47,7 @@ - (instancetype)init - (void)invalidate { + [super invalidate]; [_nodesManager stopAnimationLoop]; [self.bridge.eventDispatcher removeDispatchObserver:self]; [self.bridge.uiManager.observerCoordinator removeObserver:self]; diff --git a/Libraries/Network/RCTNetworking.mm b/Libraries/Network/RCTNetworking.mm index b857c43a9086e4..c4dbf8a7bf3364 100644 --- a/Libraries/Network/RCTNetworking.mm +++ b/Libraries/Network/RCTNetworking.mm @@ -168,6 +168,8 @@ - (instancetype)initWithHandlersProvider:(NSArray> * (^ - (void)invalidate { + [super invalidate]; + for (NSNumber *requestID in _tasksByRequestID) { [_tasksByRequestID[requestID] cancel]; } @@ -680,7 +682,7 @@ - (RCTNetworkTask *)networkTaskWithRequest:(NSURLRequest *)request completionBlo @"timeout": @(query.timeout()), @"withCredentials": @(query.withCredentials()), }; - + // TODO: buildRequest returns a cancellation block, but there's currently // no way to invoke it, if, for example the request is cancelled while // loading a large file to build the request body diff --git a/React/CoreModules/RCTAppState.h b/React/CoreModules/RCTAppState.h index 6e0f19c3356f65..0921f70e44723b 100644 --- a/React/CoreModules/RCTAppState.h +++ b/React/CoreModules/RCTAppState.h @@ -7,6 +7,6 @@ #import -@interface RCTAppState : RCTEventEmitter +@interface RCTAppState : RCTEventEmitter @end diff --git a/React/CoreModules/RCTAppState.mm b/React/CoreModules/RCTAppState.mm index 66a1e72693f2f6..b0dc10bce5ad6d 100644 --- a/React/CoreModules/RCTAppState.mm +++ b/React/CoreModules/RCTAppState.mm @@ -99,11 +99,6 @@ - (void)stopObserving [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (void)invalidate -{ - [self stopObserving]; -} - #pragma mark - App Notification Methods - (void)handleMemoryWarning diff --git a/React/CoreModules/RCTDevSettings.mm b/React/CoreModules/RCTDevSettings.mm index 50d9cd0cde8447..4bf03630201fe0 100644 --- a/React/CoreModules/RCTDevSettings.mm +++ b/React/CoreModules/RCTDevSettings.mm @@ -202,6 +202,7 @@ - (dispatch_queue_t)methodQueue - (void)invalidate { + [super invalidate]; #if ENABLE_PACKAGER_CONNECTION [[RCTPackagerConnection sharedPackagerConnection] removeHandler:_reloadToken]; #endif diff --git a/React/CoreModules/RCTWebSocketModule.mm b/React/CoreModules/RCTWebSocketModule.mm index e26423d245438b..3bc43d797f65d6 100644 --- a/React/CoreModules/RCTWebSocketModule.mm +++ b/React/CoreModules/RCTWebSocketModule.mm @@ -53,6 +53,8 @@ - (NSArray *)supportedEvents - (void)invalidate { + [super invalidate]; + _contentHandlers = nil; for (RCTSRWebSocket *socket in _sockets.allValues) { socket.delegate = nil; diff --git a/React/Modules/RCTEventEmitter.h b/React/Modules/RCTEventEmitter.h index 700368c599d016..f04d50f02418e4 100644 --- a/React/Modules/RCTEventEmitter.h +++ b/React/Modules/RCTEventEmitter.h @@ -12,7 +12,7 @@ * RCTEventEmitter is an abstract base class to be used for modules that emit * events to be observed by JS. */ -@interface RCTEventEmitter : NSObject +@interface RCTEventEmitter : NSObject @property (nonatomic, weak) RCTBridge *bridge; @@ -37,6 +37,8 @@ - (void)startObserving; - (void)stopObserving; +- (void)invalidate NS_REQUIRES_SUPER; + - (void)addListener:(NSString *)eventName; - (void)removeListeners:(double)count; diff --git a/React/Modules/RCTEventEmitter.m b/React/Modules/RCTEventEmitter.m index 7717ddbdcac451..6e5a6ff37e7f5d 100644 --- a/React/Modules/RCTEventEmitter.m +++ b/React/Modules/RCTEventEmitter.m @@ -78,7 +78,7 @@ - (void)stopObserving // Does nothing } -- (void)dealloc +- (void)invalidate { if (_listenerCount > 0) { [self stopObserving]; From acb967e1bbe2f980e7339f5279a1206c089c3774 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Tue, 22 Sep 2020 01:50:39 -0700 Subject: [PATCH 053/241] Pull out construction of Layout from TextLayoutManager.measureText into separate function Summary: Changelog: [Internal] Construction of Layout will be needed in `TextLayoutManager.measureLines`, pulling it out into separate function prevents code duplication. Reviewed By: shergin Differential Revision: D23782905 fbshipit-source-id: 8ab817559ca154716a190ca1012e809c5fa2fd6e --- .../react/views/text/TextLayoutManager.java | 103 ++++++++++-------- 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java index c1143b430830e7..3fa47c3a9d7c66 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java @@ -257,51 +257,19 @@ private static Spannable createSpannableFromAttributedString( return sb; } - public static long measureText( - Context context, - ReadableMap attributedString, - ReadableMap paragraphAttributes, + private static Layout createLayout( + Spannable text, + BoringLayout.Metrics boring, float width, YogaMeasureMode widthYogaMeasureMode, - float height, - YogaMeasureMode heightYogaMeasureMode, - ReactTextViewManagerCallback reactTextViewManagerCallback, - @Nullable float[] attachmentsPositions) { - - // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) + boolean includeFontPadding, + int textBreakStrategy) { + Layout layout; + int spanLength = text.length(); + boolean unconstrainedWidth = widthYogaMeasureMode == YogaMeasureMode.UNDEFINED || width < 0; TextPaint textPaint = sTextPaintInstance; - Spannable text; - if (attributedString.hasKey("cacheId")) { - int cacheId = attributedString.getInt("cacheId"); - if (sTagToSpannableCache.containsKey(cacheId)) { - text = sTagToSpannableCache.get(cacheId); - } else { - return 0; - } - } else { - text = getOrCreateSpannableForText(context, attributedString, reactTextViewManagerCallback); - } - - int textBreakStrategy = - TextAttributeProps.getTextBreakStrategy( - paragraphAttributes.getString(TEXT_BREAK_STRATEGY_KEY)); - boolean includeFontPadding = - paragraphAttributes.hasKey(INCLUDE_FONT_PADDING_KEY) - ? paragraphAttributes.getBoolean(INCLUDE_FONT_PADDING_KEY) - : DEFAULT_INCLUDE_FONT_PADDING; - - if (text == null) { - throw new IllegalStateException("Spannable element has not been prepared in onBeforeLayout"); - } - - BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); float desiredWidth = boring == null ? Layout.getDesiredWidth(text, textPaint) : Float.NaN; - // technically, width should never be negative, but there is currently a bug in - boolean unconstrainedWidth = widthYogaMeasureMode == YogaMeasureMode.UNDEFINED || width < 0; - - Layout layout; - int spanLength = text.length(); if (boring == null && (unconstrainedWidth || (!YogaConstants.isUndefined(desiredWidth) && desiredWidth <= width))) { @@ -367,6 +335,55 @@ public static long measureText( .build(); } } + return layout; + } + + public static long measureText( + Context context, + ReadableMap attributedString, + ReadableMap paragraphAttributes, + float width, + YogaMeasureMode widthYogaMeasureMode, + float height, + YogaMeasureMode heightYogaMeasureMode, + ReactTextViewManagerCallback reactTextViewManagerCallback, + @Nullable float[] attachmentsPositions) { + + // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) + TextPaint textPaint = sTextPaintInstance; + Spannable text; + if (attributedString.hasKey("cacheId")) { + int cacheId = attributedString.getInt("cacheId"); + if (sTagToSpannableCache.containsKey(cacheId)) { + text = sTagToSpannableCache.get(cacheId); + } else { + return 0; + } + } else { + text = getOrCreateSpannableForText(context, attributedString, reactTextViewManagerCallback); + } + + int textBreakStrategy = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(TEXT_BREAK_STRATEGY_KEY)); + boolean includeFontPadding = + paragraphAttributes.hasKey(INCLUDE_FONT_PADDING_KEY) + ? paragraphAttributes.getBoolean(INCLUDE_FONT_PADDING_KEY) + : DEFAULT_INCLUDE_FONT_PADDING; + + if (text == null) { + throw new IllegalStateException("Spannable element has not been prepared in onBeforeLayout"); + } + + BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); + float desiredWidth = boring == null ? Layout.getDesiredWidth(text, textPaint) : Float.NaN; + + // technically, width should never be negative, but there is currently a bug in + boolean unconstrainedWidth = widthYogaMeasureMode == YogaMeasureMode.UNDEFINED || width < 0; + + Layout layout = + createLayout( + text, boring, width, widthYogaMeasureMode, includeFontPadding, textBreakStrategy); int maximumNumberOfLines = paragraphAttributes.hasKey(MAXIMUM_NUMBER_OF_LINES_KEY) @@ -408,9 +425,9 @@ public static long measureText( // follows a similar logic than used in pre-fabric (see ReactTextView.onLayout method). int attachmentIndex = 0; int lastAttachmentFoundInSpan; - for (int i = 0; i < spanLength; i = lastAttachmentFoundInSpan) { + for (int i = 0; i < text.length(); i = lastAttachmentFoundInSpan) { lastAttachmentFoundInSpan = - text.nextSpanTransition(i, spanLength, TextInlineViewPlaceholderSpan.class); + text.nextSpanTransition(i, text.length(), TextInlineViewPlaceholderSpan.class); TextInlineViewPlaceholderSpan[] placeholders = text.getSpans(i, lastAttachmentFoundInSpan, TextInlineViewPlaceholderSpan.class); for (TextInlineViewPlaceholderSpan placeholder : placeholders) { @@ -432,7 +449,7 @@ public static long measureText( // There's a bug on Samsung devices where calling getPrimaryHorizontal on // the last offset in the layout will result in an endless loop. Work around // this bug by avoiding getPrimaryHorizontal in that case. - if (start == spanLength - 1) { + if (start == text.length() - 1) { placeholderLeftPosition = isRtlParagraph // Equivalent to `layout.getLineLeft(line)` but `getLineLeft` returns incorrect From c1af56df71cfbecea224440ac6a2ee2915a4aa51 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Tue, 22 Sep 2020 01:50:39 -0700 Subject: [PATCH 054/241] Wire call from C++ to Java to get lines measurements Summary: Changelog: [Internal] Reviewed By: shergin Differential Revision: D23782998 fbshipit-source-id: fa9bda274c024d5bbd3ca24f394b5d76f8c07ad2 --- .../react/fabric/FabricUIManager.java | 9 ++++ .../components/text/ParagraphShadowNode.cpp | 2 - .../textlayoutmanager/TextMeasureCache.cpp | 34 ++++++++++++++ .../textlayoutmanager/TextMeasureCache.h | 10 +++++ .../textlayoutmanager/TextLayoutManager.cpp | 44 +++++++++++++++++++ .../textlayoutmanager/TextLayoutManager.h | 9 ++++ 6 files changed, 106 insertions(+), 2 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index bf95f6e7da3376..6eb969d9c89f24 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -36,7 +36,9 @@ import com.facebook.infer.annotation.ThreadConfined; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.ReactRootView; +import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.LifecycleEventListener; +import com.facebook.react.bridge.NativeArray; import com.facebook.react.bridge.NativeMap; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; @@ -443,6 +445,13 @@ private MountItem createBatchMountItem( return new BatchMountItem(rootTag, items, size, commitNumber); } + @DoNotStrip + @SuppressWarnings("unused") + private NativeArray measureLines( + ReadableMap attributedString, ReadableMap paragraphAttributes, float width, float height) { + return (NativeArray) Arguments.createArray(); + } + @DoNotStrip @SuppressWarnings("unused") private long measure( diff --git a/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp b/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp index 2a7f0421dea480..87745126463ca9 100644 --- a/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -169,7 +169,6 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { content.paragraphAttributes, layoutConstraints); -#ifndef ANDROID if (getConcreteProps().onTextLayout) { auto linesMeasurements = textLayoutManager_->measureLines( content.attributedString, @@ -177,7 +176,6 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { measurement.size); getConcreteEventEmitter().onTextLayout(linesMeasurements); } -#endif if (content.attachments.empty()) { // No attachments to layout. diff --git a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp index 26b50ffd184972..d3d9845f93c518 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp +++ b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp @@ -10,6 +10,40 @@ namespace facebook { namespace react { +static Rect rectFromDynamic(folly::dynamic const &data) { + Point origin; + origin.x = data.getDefault("x", 0).getDouble(); + origin.y = data.getDefault("y", 0).getDouble(); + Size size; + size.width = data.getDefault("width", 0).getDouble(); + size.height = data.getDefault("height", 0).getDouble(); + Rect frame; + frame.origin = origin; + frame.size = size; + return frame; +} + +LineMeasurement::LineMeasurement( + std::string text, + Rect frame, + Float descender, + Float capHeight, + Float ascender, + Float xHeight) + : text(text), + frame(frame), + descender(descender), + capHeight(capHeight), + ascender(ascender) {} + +LineMeasurement::LineMeasurement(folly::dynamic const &data) + : text(data.getDefault("text", "").getString()), + frame(rectFromDynamic(data)), + descender(data.getDefault("descender", 0).getDouble()), + capHeight(data.getDefault("capHeight", 0).getDouble()), + ascender(data.getDefault("ascender", 0).getDouble()), + xHeight(data.getDefault("xHeight", 0).getDouble()) {} + bool LineMeasurement::operator==(LineMeasurement const &rhs) const { return std::tie( this->text, diff --git a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index 34a0a2d870581c..d0e71872810d75 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -24,6 +24,16 @@ struct LineMeasurement { Float ascender; Float xHeight; + LineMeasurement( + std::string text, + Rect frame, + Float descender, + Float capHeight, + Float ascender, + Float xHeight); + + LineMeasurement(folly::dynamic const &data); + bool operator==(LineMeasurement const &rhs) const; }; diff --git a/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp b/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp index 3d69b4a84ed1d5..be0201dec74d43 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp +++ b/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp @@ -98,6 +98,50 @@ TextMeasurement TextLayoutManager::measureCachedSpannableById( return TextMeasurement{size, attachments}; } +LinesMeasurements TextLayoutManager::measureLines( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const { + const jni::global_ref &fabricUIManager = + contextContainer_->at>("FabricUIManager"); + static auto measureLines = + jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") + ->getMethod("measureLines"); + + auto serializedAttributedString = toDynamic(attributedString); + + local_ref attributedStringRNM = + ReadableNativeMap::newObjectCxxArgs(serializedAttributedString); + local_ref paragraphAttributesRNM = + ReadableNativeMap::newObjectCxxArgs(toDynamic(paragraphAttributes)); + + local_ref attributedStringRM = make_local( + reinterpret_cast(attributedStringRNM.get())); + local_ref paragraphAttributesRM = make_local( + reinterpret_cast(paragraphAttributesRNM.get())); + + auto array = measureLines( + fabricUIManager, + attributedStringRM.get(), + paragraphAttributesRM.get(), + size.width, + size.height); + + auto dynamicArray = cthis(array)->consume(); + LinesMeasurements lineMeasurements; + lineMeasurements.reserve(dynamicArray.size()); + + for (auto const &data : dynamicArray) { + lineMeasurements.push_back(LineMeasurement(data)); + } + + return lineMeasurements; +} + TextMeasurement TextLayoutManager::doMeasure( AttributedString attributedString, ParagraphAttributes paragraphAttributes, diff --git a/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h b/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h index e1ec4b122f9a7e..681c54e22fb793 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h +++ b/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h @@ -46,6 +46,15 @@ class TextLayoutManager { ParagraphAttributes paragraphAttributes, LayoutConstraints layoutConstraints) const; + /* + * Measures lines of `attributedString` using native text rendering + * infrastructure. + */ + LinesMeasurements measureLines( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const; + /* * Returns an opaque pointer to platform-specific TextLayoutManager. * Is used on a native views layer to delegate text rendering to the manager. From a650696f0d144ab705a617bc48925da8805dc58b Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Tue, 22 Sep 2020 01:50:39 -0700 Subject: [PATCH 055/241] Implement onTextLayout on Text component. Summary: Changelog: [Internal] Add `Text.onTextLayout` implementation to Android's Text component. Reviewed By: JoshuaGross Differential Revision: D23782311 fbshipit-source-id: fdb5709aaf68efee0ab895a6661396f92cfc768a --- .../main/java/com/facebook/react/fabric/BUCK | 1 + .../react/fabric/FabricUIManager.java | 10 ++++++-- .../react/views/text/TextLayoutManager.java | 24 +++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK b/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK index 9ebcd431161f44..a1882a6cdbd5d1 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK @@ -39,6 +39,7 @@ rn_android_library( react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/views/view:view"), + react_native_target("java/com/facebook/react/views/text:text"), react_native_target("java/com/facebook/react/touch:touch"), ], exported_deps = [ diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index 6eb969d9c89f24..3e5b6c37aaba5f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -36,7 +36,6 @@ import com.facebook.infer.annotation.ThreadConfined; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.ReactRootView; -import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.NativeArray; import com.facebook.react.bridge.NativeMap; @@ -76,6 +75,7 @@ import com.facebook.react.fabric.mounting.mountitems.UpdateStateMountItem; import com.facebook.react.modules.core.ReactChoreographer; import com.facebook.react.modules.i18nmanager.I18nUtil; +import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactRoot; import com.facebook.react.uimanager.ReactRootViewTagGenerator; import com.facebook.react.uimanager.StateWrapper; @@ -84,6 +84,7 @@ import com.facebook.react.uimanager.ViewManagerPropertyUpdater; import com.facebook.react.uimanager.ViewManagerRegistry; import com.facebook.react.uimanager.events.EventDispatcher; +import com.facebook.react.views.text.TextLayoutManager; import com.facebook.systrace.Systrace; import java.util.ArrayDeque; import java.util.ArrayList; @@ -449,7 +450,12 @@ private MountItem createBatchMountItem( @SuppressWarnings("unused") private NativeArray measureLines( ReadableMap attributedString, ReadableMap paragraphAttributes, float width, float height) { - return (NativeArray) Arguments.createArray(); + return (NativeArray) + TextLayoutManager.measureLines( + mReactApplicationContext, + attributedString, + paragraphAttributes, + PixelUtil.toPixelFromDIP(width)); } @DoNotStrip diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java index 3fa47c3a9d7c66..92073cd3392124 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java @@ -27,6 +27,7 @@ import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableNativeMap; +import com.facebook.react.bridge.WritableArray; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactAccessibilityDelegate; @@ -519,6 +520,29 @@ public static long measureText( return YogaMeasureOutput.make(widthInSP, heightInSP); } + public static WritableArray measureLines( + @NonNull Context context, + ReadableMap attributedString, + ReadableMap paragraphAttributes, + float width) { + TextPaint textPaint = sTextPaintInstance; + Spannable text = getOrCreateSpannableForText(context, attributedString, null); + BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); + + int textBreakStrategy = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(TEXT_BREAK_STRATEGY_KEY)); + boolean includeFontPadding = + paragraphAttributes.hasKey(INCLUDE_FONT_PADDING_KEY) + ? paragraphAttributes.getBoolean(INCLUDE_FONT_PADDING_KEY) + : DEFAULT_INCLUDE_FONT_PADDING; + + Layout layout = + createLayout( + text, boring, width, YogaMeasureMode.EXACTLY, includeFontPadding, textBreakStrategy); + return FontMetricsUtil.getFontMetrics(text, layout, sTextPaintInstance, context); + } + // TODO T31905686: This class should be private public static class SetSpanOperation { protected int start, end; From 83777cb4fb5dda89c430b7eff9cd1f28d2577831 Mon Sep 17 00:00:00 2001 From: Makar Kotlov Date: Tue, 22 Sep 2020 06:14:20 -0700 Subject: [PATCH 056/241] Fix Xcode bundler in staging and release (#29477) Summary: Revert "feat: improve monorepo support by removing redundant PROJECT_ROOT (https://github.com/facebook/react-native/issues/28354)" This reverts commit a8e85026cfa60056b1bcbcd39cde789e4d65f9cb. This commit a8e85026cfa60056b1bcbcd39cde789e4d65f9cb somehow broke the bundler when making a staging or release build in Xcode that results in unresolved files and main.jsbundle nonexistance issue. I figured this out by replacing react-native-xcode.sh from RN v0.63.2 by the one from v0.62.2 where everything works just fine and then reverting the changes line by line. It seems like this pr will fix similar issues stated here https://stackoverflow.com/questions/62806319/main-jsbundle-does-not-exist-this-must-be-a-bug-with-main-jsbundle-issue-afte/62829256#62829256 and here https://github.com/facebook/react-native/issues/29205 ## Changelog [iOS] [Fixed] - fix "main.jsbundle does not exist" issue Pull Request resolved: https://github.com/facebook/react-native/pull/29477 Test Plan: With react-native-xcode.sh from RN v0.63.2 ![image](https://user-images.githubusercontent.com/32848434/88342113-7ce55d80-cd47-11ea-9dab-bf41ec6d6ab5.png) With my changes ![image](https://user-images.githubusercontent.com/32848434/88342376-f0876a80-cd47-11ea-9c08-96b892784da1.png) Reviewed By: sammy-SC Differential Revision: D23817847 Pulled By: hramos fbshipit-source-id: 4b729c1231d30e89073b2520aeadee944c84421c --- scripts/react-native-xcode.sh | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/scripts/react-native-xcode.sh b/scripts/react-native-xcode.sh index 98743b168b6183..eeb601653f3b7f 100755 --- a/scripts/react-native-xcode.sh +++ b/scripts/react-native-xcode.sh @@ -56,17 +56,11 @@ case "$CONFIGURATION" in ;; esac -# Setting up a project root was a workaround to enable support for non-standard -# structures, including monorepos. Today, CLI supports that out of the box -# and setting custom `PROJECT_ROOT` only makes it confusing. -# -# As a backwards-compatible change, I am leaving "PROJECT_ROOT" support for those -# who already use it - it is likely a non-breaking removal. -# -# For new users, we default to $PWD - not changing things all. -# -# For context: https://github.com/facebook/react-native/commit/9ccde378b6e6379df61f9d968be6346ca6be7ead#commitcomment-37914902 -PROJECT_ROOT=${PROJECT_ROOT:-$PWD} +# Path to react-native folder inside node_modules +REACT_NATIVE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# The project should be located next to where react-native is installed +# in node_modules. +PROJECT_ROOT=${PROJECT_ROOT:-"$REACT_NATIVE_DIR/../.."} cd "$PROJECT_ROOT" || exit @@ -109,9 +103,6 @@ if [[ ! -x node && -d ${HOME}/.anyenv/bin ]]; then fi fi -# Path to react-native folder inside node_modules -REACT_NATIVE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" - # check and assign NODE_BINARY env # shellcheck source=/dev/null source "$REACT_NATIVE_DIR/scripts/node-binary.sh" From 891b6b0c14d820d38f0a0b8f5ebf749c5cd14a94 Mon Sep 17 00:00:00 2001 From: chirag-singhal Date: Tue, 22 Sep 2020 11:21:51 -0700 Subject: [PATCH 057/241] New Share API Use Cases (#29856) Summary: * New use cases for share API in rn-tester ## Changelog [General] [Changed] - Changed use cases for share API in rn-tester ## Test Plan * Tested app in both Android and iOS ![Screenshot_2020-09-09-21-20-41-322_com facebook react uiapp](https://user-images.githubusercontent.com/42653703/92624772-83bf3400-f2e5-11ea-820f-a7f3d9a44a11.jpg) ![image](https://user-images.githubusercontent.com/42653703/92624985-c1bc5800-f2e5-11ea-9f30-b88ab786963b.png) Pull Request resolved: https://github.com/facebook/react-native/pull/29856 Reviewed By: hramos Differential Revision: D23784222 Pulled By: rickhanlonii fbshipit-source-id: f311201a49e0a76abb6634232106ed756143da30 --- .../js/examples/Share/ShareExample.js | 169 ++++++++---------- 1 file changed, 75 insertions(+), 94 deletions(-) diff --git a/packages/rn-tester/js/examples/Share/ShareExample.js b/packages/rn-tester/js/examples/Share/ShareExample.js index ed3268af252fef..3ec9a7d6f5c89f 100644 --- a/packages/rn-tester/js/examples/Share/ShareExample.js +++ b/packages/rn-tester/js/examples/Share/ShareExample.js @@ -12,115 +12,96 @@ const React = require('react'); -const { - StyleSheet, - View, - Text, - TouchableHighlight, - Share, -} = require('react-native'); +const {StyleSheet, View, Text, Button, Share} = require('react-native'); -type Props = $ReadOnly<{||}>; -type State = {|result: string|}; +const shareMessage = () => { + Share.share({ + message: ('Our top priority for React Native is to match the expectations people have for each platform. This is why React Native renders to platform primitives. We value native look-and-feel over cross-platform consistency.' + + 'For example, the TextInput in React Native renders to a UITextField on iOS. This ensures that integration with password managers and keyboard controls work out of the box. By using platform primitives, React Native apps are also able to stay up-to-date with design and behavior changes from new releases of Android and iOS.': string), + }); +}; -class ShareMessageExample extends React.Component { - _shareMessage: Function; - _shareText: Function; - _showResult: Function; - - constructor(props) { - super(props); - - this._shareMessage = this._shareMessage.bind(this); - this._shareText = this._shareText.bind(this); - this._showResult = this._showResult.bind(this); - - this.state = { - result: '', - }; - } - - render() { - return ( - - - - Click to share message - - - - - Click to share message, URL and title - - - {this.state.result} - - ); - } - - _shareMessage() { - Share.share({ - message: - 'React Native | A framework for building native apps using React', - }) - .then(this._showResult) - .catch(error => this.setState({result: 'error: ' + error.message})); - } +const shareText = () => { + Share.share( + { + title: 'Massive Scale', + message: ('Hundreds of screens in the Facebook app are implemented with React Native. The Facebook app is used by billions of people on a huge range of devices. This is why we invest in the most challenging problems at scale.' + + 'Deploying React Native in our apps lets us identify problems that we wouldn’t see at a smaller scale. For example, Facebook focuses on improving performance across a broad spectrum of devices from the newest iPhone to many older generations of Android devices. This focus informs our architecture projects such as Hermes, Fabric, and TurboModules.': string), + url: 'https://reactnative.dev/blog/2020/07/17/react-native-principles', + }, + { + subject: 'MUST READ: Massive Scale', + dialogTitle: 'Share React Native Blog', + excludedActivityTypes: ['com.apple.UIKit.activity.PostToTwitter'], + tintColor: 'blue', + }, + ); +}; - _shareText() { - Share.share( - { - message: 'A framework for building native apps using React', - url: 'https://reactnative.dev/', - title: 'React Native', - }, - { - subject: 'A subject to go in the email heading', - dialogTitle: 'Share React Native website', - excludedActivityTypes: ['com.apple.UIKit.activity.PostToTwitter'], - tintColor: 'green', - }, - ) - .then(this._showResult) - .catch(error => this.setState({result: 'error: ' + error.message})); - } +const ShareMessageWithoutTitle = () => { + return ( + + Native Experience + + Our top priority for React Native is to match the expectations people + have for each platform. This is why React Native renders to platform + primitives. We value native look-and-feel over cross-platform + consistency. For example, the TextInput in React Native renders to a + UITextField on iOS. This ensures that integration with password managers + and keyboard controls work out of the box. By using platform primitives, + React Native apps are also able to stay up-to-date with design and + behavior changes from new releases of Android and iOS. + +