diff --git a/.flowconfig b/.flowconfig index cb84f30941..8f18371ec2 100644 --- a/.flowconfig +++ b/.flowconfig @@ -80,4 +80,4 @@ untyped-import untyped-type-import [version] -^0.133.0 +^0.134.0 diff --git a/.flowconfig.android b/.flowconfig.android index 9ae9688898..dc18bc43a0 100644 --- a/.flowconfig.android +++ b/.flowconfig.android @@ -83,4 +83,4 @@ untyped-import untyped-type-import [version] -^0.133.0 +^0.134.0 diff --git a/.flowconfig.macos b/.flowconfig.macos index 8697df66dc..ac07c6646d 100644 --- a/.flowconfig.macos +++ b/.flowconfig.macos @@ -81,4 +81,4 @@ untyped-import untyped-type-import [version] -^0.133.0 +^0.134.0 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 80ba4096da..ba788dafca 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 70b65082fa..a816d4fea7 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 f5d311c1c3..b17f63f211 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 0000000000..70f7651b48 --- /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 549ddef0a9..1bc3b9950d 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/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp index 7599e85de2..08fbe459d7 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 -#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) - << "Adjust delayed mutations based on finalConflictingMutations"; -#endif - for (auto &mutation : finalConflictingMutations) { - if (mutation.type == ShadowViewMutation::Remove || - mutation.type == ShadowViewMutation::Insert) { - adjustDelayedMutationIndicesForMutation(surfaceId, mutation); - } - } - - // 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. -#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) - << "Adjust immediate keyFramesToAnimate based on delayed mutations and finalConflictingMutations"; -#endif - for (auto &keyframe : keyFramesToAnimate) { - if (keyframe.finalMutationForKeyFrame.has_value()) { - auto &delayedMutation = *keyframe.finalMutationForKeyFrame; - if (delayedMutation.type == ShadowViewMutation::Type::Remove) { - adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, delayedMutation /*, &finalConflictingMutations*/); - } - } - } - - // 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. -#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) - << "Adjust immediateMutations REMOVEs only, based on previously delayed mutations, without most-recent animation"; -#endif std::stable_sort( immediateMutations.begin(), immediateMutations.end(), &shouldFirstComeBeforeSecondRemovesOnly); - for (auto &mutation : immediateMutations) { - if (mutation.type == ShadowViewMutation::Type::Remove) { - adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, mutation); - adjustDelayedMutationIndicesForMutation(surfaceId, mutation); - } - } 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] based on [FinalConflicting]"; +#endif + for (auto &mutation : finalConflictingMutations) { + if (mutation.type == ShadowViewMutation::Type::Insert || + mutation.type == ShadowViewMutation::Type::Remove) { + adjustDelayedMutationIndicesForMutation(surfaceId, mutation, true); + } + } + + // 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 [FrameDelayed] based on [Delayed]"; +#endif + for (auto &keyframe : inflightAnimations_.back().keyFrames) { + if (keyframe.finalMutationForKeyFrame.has_value()) { + 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, mutation, consecutiveAdjustmentMetadata, true); + } + } + } + + // 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 [ImmediateExecutions] based on [Delayed]"; +#endif + consecutiveAdjustmentMetadata = ConsecutiveAdjustmentMetadata{}; + for (auto &mutation : immediateMutations) { + // 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, + consecutiveAdjustmentMetadata, + mutation.type == ShadowViewMutation::Type::Remove); + } + } + + // 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,60 +1345,24 @@ 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); - } - - // 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. -#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) - << "Adjust immediateMutations and delayed mutations, including just-queued animations, based on each one"; -#endif - for (auto &mutation : immediateMutations) { - if (mutation.type == ShadowViewMutation::Type::Remove) { - adjustLastAnimationDelayedMutationIndicesForMutation( - surfaceId, mutation); - } else if (mutation.type == ShadowViewMutation::Type::Insert) { - adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, mutation); - adjustDelayedMutationIndicesForMutation(surfaceId, mutation); - } - } - - // These will be executed immediately. These should already be sorted - // properly. - mutations.insert( - mutations.end(), + finalConflictingMutations.insert( + finalConflictingMutations.end(), immediateMutations.begin(), immediateMutations.end()); + mutations = finalConflictingMutations; } /* 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. - for (auto &mutation : mutations) { - if (mutation.type == ShadowViewMutation::Type::Remove || - mutation.type == ShadowViewMutation::Type::Insert) { - adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, mutation); - adjustDelayedMutationIndicesForMutation(surfaceId, mutation); - } - } - // 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); } @@ -1373,6 +1375,45 @@ LayoutAnimationKeyFrameManager::pullTransaction( finalMutationsForConflictingAnimations.end(), &shouldFirstComeBeforeSecondMutation); +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) + << "No Animation: Adjust delayed mutations based on all finalMutationsForConflictingAnimations"; +#endif + for (auto &mutation : finalMutationsForConflictingAnimations) { + if (mutation.type == ShadowViewMutation::Type::Remove || + mutation.type == ShadowViewMutation::Type::Insert) { + adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + } + } + + // 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, consecutiveAdjustmentMetadata); + } + } + +#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); + } + } + // 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 686fcaa9da..e6aba6e3c0 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( diff --git a/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp b/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp index 1295108286..ee16f5d026 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 b73eb967d5..597ef6baa2 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 b4fc65923a..2a7f0421de 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/mounting/Differentiator.cpp b/ReactCommon/react/renderer/mounting/Differentiator.cpp index 300c38059f..d7fee08924 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 = diff --git a/ReactCommon/react/renderer/mounting/StubViewTree.cpp b/ReactCommon/react/renderer/mounting/StubViewTree.cpp index fc68777f34..698af4374f 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]; diff --git a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp index 70904ed8b6..26b50ffd18 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 4a15c17925..34a0a2d870 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; diff --git a/package.json b/package.json index 779ba01afe..b28c22c586 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "ws": "^6.1.4" }, "devDependencies": { - "flow-bin": "^0.133.0", + "flow-bin": "^0.134.0", "react": "16.13.1" }, "detox": { diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 1adf3925bc..f49c92d3f3 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -510,8 +510,8 @@ SPEC CHECKSUMS: CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845 CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f DoubleConversion: 2b45d0f8e156a5b02354c8a4062de64d41ccb4e0 - FBLazyVector: 6840df244b2930eadc86dfddd63dd586de8fbf4b - FBReactNativeSpec: 6a5b272a87d8fba0a0267169a9c69104a33f4104 + FBLazyVector: a2e74b28704250f5c2840b8ac0c36a550fc58c5c + FBReactNativeSpec: f32ba61faf46d74f93ee06325baab8f1f160cd4d Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365 Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3 @@ -522,33 +522,33 @@ SPEC CHECKSUMS: glog: 789873d01e4b200777d0a09bc23d548446758699 OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355 RCT-Folly: 55d0039b24e192081ec0b2257f7bd9f42e382fb7 - RCTRequired: 8aab7fd83973da16ad4a7c320ab60d95daaf3a49 - RCTTypeSafety: 1b9fcfa91c95a819c8ee11b2d78792a1a0c9f008 - React: 0d0bbc598d4e4a42c2d8f501e2d85bf68f4b4c58 - React-callinvoker: 7171dcdb2310bc9843acfd9e9d1ad4432f069343 - React-Core: f9261d49de3292529f426b0337589cf85cbd2be5 - React-CoreModules: 6cd34c4feb240e2243a2b766ac42b4922a43f272 - React-cxxreact: 1d94eae472a7d96512c9616b79755f17b7d9e60d - React-jsi: 738aad53cef29cda532eb60393636882b048aa63 - React-jsiexecutor: 4f307d9cf526c904c6a29a492ee0d0a56a3955ac - React-jsinspector: 9a11ad30cee910a8f9af0f93863b3583512ea3ca - React-perflogger: 869de521c7b7dbdbb1b0ef973a3c00f7c8046ff8 - React-RCTActionSheet: a0bd6037f343183655817e8ccfb517d227ce89c6 - React-RCTAnimation: b89077f33ada3bc4d37b4b431810a90bce548395 - React-RCTBlob: ee093fdd17bea7fccea6b7798112d70f32e14590 - React-RCTImage: 6260a130d38065a6b6a0053b066b693b88bfcfb1 - React-RCTLinking: 455be7c2c9b567d1e8225d28cefbf0d6c3364f85 - React-RCTNetwork: a1d28da57b4d322b4dfd7c036da4e3fddc220ef1 - React-RCTPushNotification: 843cd5fdc428814ad09eef327f5e069e2962e5bc - React-RCTSettings: 2fd29ce8818a57f6016243ca3857a36dfda3f73a - React-RCTTest: f5e9ca6222f647dde3ca536ce5d983da1028cbb8 - React-RCTText: ae07257137eb22d788947b76d7176418c18d7f8b - React-RCTVibration: 96e1d2916264a8787a2a409e9a3dd1ded5f2a404 - React-runtimeexecutor: 165044764bc0b6176e07f654f57d3c84567107b8 + RCTRequired: f69bc029b4e19411dd37c82521ee7f40451c39bd + RCTTypeSafety: eac4bf166ab078acc91d836bf91c19227a52b5cb + React: e3ead01467fceb6371308544f0e94f35052230e7 + React-callinvoker: 14f7c592814aa4ed38de8ee15c87fb3284affcf0 + React-Core: 56999fc712186d46fcd3b82563dbc1cdcfbbab47 + React-CoreModules: 4f16f6042a515d575c6a8e745345403527c6b292 + React-cxxreact: efd175939ed4f9ed26c39ae528151841e785187c + React-jsi: 52b19336123975495cc71d6f8881f599ad5fc301 + React-jsiexecutor: 75e0be97411644e306e86ff043bc35eb9bf57f5d + React-jsinspector: 9ca47f83b073fba6c03b1271403d9a3cb811e46c + React-perflogger: f3c3ce0e1e370cf52418687d585e283daa1f00a2 + React-RCTActionSheet: 2e56a81fedeab83a4c143f5edebafd960a0486c5 + React-RCTAnimation: 0aa022dd8a649fd9c120e11b674a771d69a941b8 + React-RCTBlob: ce8f9f12e944a62845982fa0ffbb9be8c622cbe6 + React-RCTImage: f7cdc51bac96c263278fa86ed20d4c9d4b5b3bd3 + React-RCTLinking: bdc37257824e77aab193cb26b0dab9b99a31fd51 + React-RCTNetwork: 48fc780d995fc21ce459c9e081f9dd0d20aa774f + React-RCTPushNotification: 1ddbd932094081df005382c448bab53246e7ed21 + React-RCTSettings: 29752424f95d45c3540e98a1e6c6c22fce064679 + React-RCTTest: 3365b714e83c56defc48bf8b42643221db2bbc07 + React-RCTText: 5b09d89d4e07910d36e2ffd74c3a876aac00e67f + React-RCTVibration: 2cfcfe6f1688d04da1ae34a5c35261dfa3bfad04 + React-runtimeexecutor: 6ec1f9d6106b9f0b041a41c89abc7dd040b39437 React-TurboModuleCxx-RNW: 18bb71af41fe34c8b12a56bef60aae7ee32b0817 - React-TurboModuleCxx-WinRTPort: 8a81b72f4adac4e05a468c73e399c32a65bfcee9 - ReactCommon: f4ea3975534282e48263f282d0d6deb8a7e546a1 - Yoga: a55415d3b511bbb13a9689678be7e18a6cac73d2 + React-TurboModuleCxx-WinRTPort: c95d24f313b49e94af9111d0a42a84ff64a4a8ae + ReactCommon: 77f4858f4785302af8b5d4d8abd93b8a6a431a88 + Yoga: 3da5d46e05d2f7cd9a30618090e0d0fdee43e279 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a PODFILE CHECKSUM: cb260f8f7765c910b68f8267cbd74709f6ae6e54 diff --git a/repo-config/package.json b/repo-config/package.json index 06e4f465c5..2986a0c5b2 100644 --- a/repo-config/package.json +++ b/repo-config/package.json @@ -35,7 +35,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", "flow-remove-types": "1.2.3", "hermes-engine-darwin": "~0.5.0", "jest": "^26.0.1", diff --git a/template/_flowconfig b/template/_flowconfig index c45babcaed..77c4a77160 100644 --- a/template/_flowconfig +++ b/template/_flowconfig @@ -66,4 +66,4 @@ untyped-import untyped-type-import [version] -^0.133.0 +^0.134.0 diff --git a/yarn.lock b/yarn.lock index 1dba57e209..ad4898ba09 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4170,10 +4170,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"