yeet
This commit is contained in:
85
node_modules/react-native/ReactCommon/fabric/mounting/BUCK
generated
vendored
Normal file
85
node_modules/react-native/ReactCommon/fabric/mounting/BUCK
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode")
|
||||
load(
|
||||
"//tools/build_defs/oss:rn_defs.bzl",
|
||||
"ANDROID",
|
||||
"APPLE",
|
||||
"CXX",
|
||||
"fb_xplat_cxx_test",
|
||||
"get_apple_compiler_flags",
|
||||
"get_apple_inspector_flags",
|
||||
"react_native_xplat_target",
|
||||
"rn_xplat_cxx_library",
|
||||
"subdir_glob",
|
||||
)
|
||||
|
||||
APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
|
||||
|
||||
rn_xplat_cxx_library(
|
||||
name = "mounting",
|
||||
srcs = glob(
|
||||
["**/*.cpp"],
|
||||
exclude = glob(["tests/**/*.cpp"]),
|
||||
),
|
||||
headers = glob(
|
||||
["**/*.h"],
|
||||
exclude = glob(["tests/**/*.h"]),
|
||||
),
|
||||
header_namespace = "",
|
||||
exported_headers = subdir_glob(
|
||||
[
|
||||
("", "*.h"),
|
||||
("stubs", "*.h"),
|
||||
],
|
||||
prefix = "react/mounting",
|
||||
),
|
||||
compiler_flags = [
|
||||
"-fexceptions",
|
||||
"-frtti",
|
||||
"-std=c++14",
|
||||
"-Wall",
|
||||
],
|
||||
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
|
||||
fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"],
|
||||
fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(),
|
||||
force_static = True,
|
||||
macosx_tests_override = [],
|
||||
platforms = (ANDROID, APPLE, CXX),
|
||||
preprocessor_flags = [
|
||||
"-DLOG_TAG=\"ReactNative\"",
|
||||
"-DWITH_FBSYSTRACE=1",
|
||||
],
|
||||
tests = [":tests"],
|
||||
visibility = ["PUBLIC"],
|
||||
deps = [
|
||||
"//xplat/fbsystrace:fbsystrace",
|
||||
"//xplat/folly:headers_only",
|
||||
"//xplat/folly:memory",
|
||||
"//xplat/folly:molly",
|
||||
"//xplat/third-party/glog:glog",
|
||||
react_native_xplat_target("better:better"),
|
||||
react_native_xplat_target("fabric/components/root:root"),
|
||||
react_native_xplat_target("fabric/components/view:view"),
|
||||
react_native_xplat_target("fabric/core:core"),
|
||||
react_native_xplat_target("fabric/debug:debug"),
|
||||
react_native_xplat_target("utils:utils"),
|
||||
],
|
||||
)
|
||||
|
||||
fb_xplat_cxx_test(
|
||||
name = "tests",
|
||||
srcs = glob(["tests/**/*.cpp"]),
|
||||
headers = glob(["tests/**/*.h"]),
|
||||
compiler_flags = [
|
||||
"-fexceptions",
|
||||
"-frtti",
|
||||
"-std=c++14",
|
||||
"-Wall",
|
||||
],
|
||||
contacts = ["oncall+react_native@xmail.facebook.com"],
|
||||
platforms = (ANDROID, APPLE, CXX),
|
||||
deps = [
|
||||
":mounting",
|
||||
"//xplat/folly:molly",
|
||||
"//xplat/third-party/gmock:gtest",
|
||||
],
|
||||
)
|
739
node_modules/react-native/ReactCommon/fabric/mounting/Differentiator.cpp
generated
vendored
Normal file
739
node_modules/react-native/ReactCommon/fabric/mounting/Differentiator.cpp
generated
vendored
Normal file
@ -0,0 +1,739 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "Differentiator.h"
|
||||
|
||||
#include <better/map.h>
|
||||
#include <better/small_vector.h>
|
||||
#include <react/core/LayoutableShadowNode.h>
|
||||
#include <react/debug/SystraceSection.h>
|
||||
#include <algorithm>
|
||||
#include "ShadowView.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* Extremely simple and naive implementation of a map.
|
||||
* The map is simple but it's optimized for particular constraints that we have
|
||||
* here.
|
||||
*
|
||||
* A regular map implementation (e.g. `std::unordered_map`) has some basic
|
||||
* performance guarantees like constant average insertion and lookup complexity.
|
||||
* This is nice, but it's *average* complexity measured on a non-trivial amount
|
||||
* of data. The regular map is a very complex data structure that using hashing,
|
||||
* buckets, multiple comprising operations, multiple allocations and so on.
|
||||
*
|
||||
* In our particular case, we need a map for `int` to `void *` with a dozen
|
||||
* values. In these conditions, nothing can beat a naive implementation using a
|
||||
* stack-allocated vector. And this implementation is exactly this: no
|
||||
* allocation, no hashing, no complex branching, no buckets, no iterators, no
|
||||
* rehashing, no other guarantees. It's crazy limited, unsafe, and performant on
|
||||
* a trivial amount of data.
|
||||
*
|
||||
* Besides that, we also need to optimize for insertion performance (the case
|
||||
* where a bunch of views appears on the screen first time); in this
|
||||
* implementation, this is as performant as vector `push_back`.
|
||||
*/
|
||||
template <typename KeyT, typename ValueT, int DefaultSize = 16>
|
||||
class TinyMap final {
|
||||
public:
|
||||
using Pair = std::pair<KeyT, ValueT>;
|
||||
using Iterator = Pair *;
|
||||
|
||||
/**
|
||||
* This must strictly only be called from outside of this class.
|
||||
*/
|
||||
inline Iterator begin() {
|
||||
// Force a clean so that iterating over this TinyMap doesn't iterate over
|
||||
// erased elements. If all elements erased are at the front of the vector,
|
||||
// then we don't need to clean.
|
||||
cleanVector(erasedAtFront_ != numErased_);
|
||||
|
||||
return begin_();
|
||||
}
|
||||
|
||||
inline Iterator end() {
|
||||
// `back()` asserts on the vector being non-empty
|
||||
if (vector_.size() == 0 || numErased_ == vector_.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &vector_.back() + 1;
|
||||
}
|
||||
|
||||
inline Iterator find(KeyT key) {
|
||||
cleanVector();
|
||||
|
||||
assert(key != 0);
|
||||
|
||||
for (auto it = begin_() + erasedAtFront_; it != end(); it++) {
|
||||
if (it->first == key) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
return end();
|
||||
}
|
||||
|
||||
inline void insert(Pair pair) {
|
||||
assert(pair.first != 0);
|
||||
vector_.push_back(pair);
|
||||
}
|
||||
|
||||
inline void erase(Iterator iterator) {
|
||||
numErased_++;
|
||||
|
||||
// Invalidate tag.
|
||||
iterator->first = 0;
|
||||
|
||||
if (iterator == begin_() + erasedAtFront_) {
|
||||
erasedAtFront_++;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Same as begin() but doesn't call cleanVector at the beginning.
|
||||
*/
|
||||
inline Iterator begin_() {
|
||||
// `front()` asserts on the vector being non-empty
|
||||
if (vector_.size() == 0 || vector_.size() == numErased_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &vector_.front();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove erased elements from internal vector.
|
||||
* We only modify the vector if erased elements are at least half of the
|
||||
* vector.
|
||||
*/
|
||||
inline void cleanVector(bool forceClean = false) {
|
||||
if ((numErased_ < (vector_.size() / 2) && !forceClean) ||
|
||||
vector_.size() == 0 || numErased_ == 0 ||
|
||||
numErased_ == erasedAtFront_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (numErased_ == vector_.size()) {
|
||||
vector_.clear();
|
||||
} else {
|
||||
vector_.erase(
|
||||
std::remove_if(
|
||||
vector_.begin(),
|
||||
vector_.end(),
|
||||
[](auto const &item) { return item.first == 0; }),
|
||||
vector_.end());
|
||||
}
|
||||
numErased_ = 0;
|
||||
erasedAtFront_ = 0;
|
||||
}
|
||||
|
||||
better::small_vector<Pair, DefaultSize> vector_;
|
||||
int numErased_{0};
|
||||
int erasedAtFront_{0};
|
||||
};
|
||||
|
||||
/*
|
||||
* Sorting comparator for `reorderInPlaceIfNeeded`.
|
||||
*/
|
||||
static bool shouldFirstPairComesBeforeSecondOne(
|
||||
ShadowViewNodePair const &lhs,
|
||||
ShadowViewNodePair const &rhs) noexcept {
|
||||
return lhs.shadowNode->getOrderIndex() < rhs.shadowNode->getOrderIndex();
|
||||
}
|
||||
|
||||
/*
|
||||
* Reorders pairs in-place based on `orderIndex` using a stable sort algorithm.
|
||||
*/
|
||||
static void reorderInPlaceIfNeeded(ShadowViewNodePair::List &pairs) noexcept {
|
||||
if (pairs.size() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto isReorderNeeded = false;
|
||||
for (auto const &pair : pairs) {
|
||||
if (pair.shadowNode->getOrderIndex() != 0) {
|
||||
isReorderNeeded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isReorderNeeded) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::stable_sort(
|
||||
pairs.begin(), pairs.end(), &shouldFirstPairComesBeforeSecondOne);
|
||||
}
|
||||
|
||||
static void sliceChildShadowNodeViewPairsRecursively(
|
||||
ShadowViewNodePair::List &pairList,
|
||||
Point layoutOffset,
|
||||
ShadowNode const &shadowNode) {
|
||||
for (auto const &sharedChildShadowNode : shadowNode.getChildren()) {
|
||||
auto &childShadowNode = *sharedChildShadowNode;
|
||||
auto shadowView = ShadowView(childShadowNode);
|
||||
if (shadowView.layoutMetrics != EmptyLayoutMetrics) {
|
||||
shadowView.layoutMetrics.frame.origin += layoutOffset;
|
||||
}
|
||||
|
||||
if (childShadowNode.getTraits().check(
|
||||
ShadowNodeTraits::Trait::FormsStackingContext)) {
|
||||
pairList.push_back({shadowView, &childShadowNode});
|
||||
} else {
|
||||
if (childShadowNode.getTraits().check(
|
||||
ShadowNodeTraits::Trait::FormsView)) {
|
||||
pairList.push_back({shadowView, &childShadowNode});
|
||||
}
|
||||
|
||||
sliceChildShadowNodeViewPairsRecursively(
|
||||
pairList, shadowView.layoutMetrics.frame.origin, childShadowNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShadowViewNodePair::List sliceChildShadowNodeViewPairs(
|
||||
ShadowNode const &shadowNode) {
|
||||
auto pairList = ShadowViewNodePair::List{};
|
||||
|
||||
if (!shadowNode.getTraits().check(
|
||||
ShadowNodeTraits::Trait::FormsStackingContext) &&
|
||||
shadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView)) {
|
||||
return pairList;
|
||||
}
|
||||
|
||||
sliceChildShadowNodeViewPairsRecursively(pairList, {0, 0}, shadowNode);
|
||||
|
||||
return pairList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Before we start to diff, let's make sure all our core data structures are in
|
||||
* good shape to deliver the best performance.
|
||||
*/
|
||||
static_assert(
|
||||
std::is_move_constructible<ShadowViewMutation>::value,
|
||||
"`ShadowViewMutation` must be `move constructible`.");
|
||||
static_assert(
|
||||
std::is_move_constructible<ShadowView>::value,
|
||||
"`ShadowView` must be `move constructible`.");
|
||||
static_assert(
|
||||
std::is_move_constructible<ShadowViewNodePair>::value,
|
||||
"`ShadowViewNodePair` must be `move constructible`.");
|
||||
static_assert(
|
||||
std::is_move_constructible<ShadowViewNodePair::List>::value,
|
||||
"`ShadowViewNodePair::List` must be `move constructible`.");
|
||||
|
||||
static_assert(
|
||||
std::is_move_assignable<ShadowViewMutation>::value,
|
||||
"`ShadowViewMutation` must be `move assignable`.");
|
||||
static_assert(
|
||||
std::is_move_assignable<ShadowView>::value,
|
||||
"`ShadowView` must be `move assignable`.");
|
||||
static_assert(
|
||||
std::is_move_assignable<ShadowViewNodePair>::value,
|
||||
"`ShadowViewNodePair` must be `move assignable`.");
|
||||
static_assert(
|
||||
std::is_move_assignable<ShadowViewNodePair::List>::value,
|
||||
"`ShadowViewNodePair::List` must be `move assignable`.");
|
||||
|
||||
static void calculateShadowViewMutationsClassic(
|
||||
ShadowViewMutation::List &mutations,
|
||||
ShadowView const &parentShadowView,
|
||||
ShadowViewNodePair::List &&oldChildPairs,
|
||||
ShadowViewNodePair::List &&newChildPairs) {
|
||||
// This version of the algorithm is optimized for simplicity,
|
||||
// not for performance or optimal result.
|
||||
|
||||
if (oldChildPairs.size() == 0 && newChildPairs.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sorting pairs based on `orderIndex` if needed.
|
||||
reorderInPlaceIfNeeded(oldChildPairs);
|
||||
reorderInPlaceIfNeeded(newChildPairs);
|
||||
|
||||
auto index = int{0};
|
||||
|
||||
// Maps inserted node tags to pointers to them in `newChildPairs`.
|
||||
auto insertedPairs = TinyMap<Tag, ShadowViewNodePair const *>{};
|
||||
|
||||
// Lists of mutations
|
||||
auto createMutations = ShadowViewMutation::List{};
|
||||
auto deleteMutations = ShadowViewMutation::List{};
|
||||
auto insertMutations = ShadowViewMutation::List{};
|
||||
auto removeMutations = ShadowViewMutation::List{};
|
||||
auto updateMutations = ShadowViewMutation::List{};
|
||||
auto downwardMutations = ShadowViewMutation::List{};
|
||||
auto destructiveDownwardMutations = ShadowViewMutation::List{};
|
||||
|
||||
// Stage 1: Collecting `Update` mutations
|
||||
for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size();
|
||||
index++) {
|
||||
auto const &oldChildPair = oldChildPairs[index];
|
||||
auto const &newChildPair = newChildPairs[index];
|
||||
|
||||
if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) {
|
||||
// Totally different nodes, updating is impossible.
|
||||
break;
|
||||
}
|
||||
|
||||
if (oldChildPair.shadowView != newChildPair.shadowView) {
|
||||
updateMutations.push_back(ShadowViewMutation::UpdateMutation(
|
||||
parentShadowView,
|
||||
oldChildPair.shadowView,
|
||||
newChildPair.shadowView,
|
||||
index));
|
||||
}
|
||||
|
||||
auto oldGrandChildPairs =
|
||||
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
|
||||
auto newGrandChildPairs =
|
||||
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
|
||||
calculateShadowViewMutationsClassic(
|
||||
*(newGrandChildPairs.size() ? &downwardMutations
|
||||
: &destructiveDownwardMutations),
|
||||
oldChildPair.shadowView,
|
||||
std::move(oldGrandChildPairs),
|
||||
std::move(newGrandChildPairs));
|
||||
}
|
||||
|
||||
int lastIndexAfterFirstStage = index;
|
||||
|
||||
// Stage 2: Collecting `Insert` mutations
|
||||
for (; index < newChildPairs.size(); index++) {
|
||||
auto const &newChildPair = newChildPairs[index];
|
||||
|
||||
insertMutations.push_back(ShadowViewMutation::InsertMutation(
|
||||
parentShadowView, newChildPair.shadowView, index));
|
||||
|
||||
insertedPairs.insert({newChildPair.shadowView.tag, &newChildPair});
|
||||
}
|
||||
|
||||
// Stage 3: Collecting `Delete` and `Remove` mutations
|
||||
for (index = lastIndexAfterFirstStage; index < oldChildPairs.size();
|
||||
index++) {
|
||||
auto const &oldChildPair = oldChildPairs[index];
|
||||
|
||||
// Even if the old view was (re)inserted, we have to generate `remove`
|
||||
// mutation.
|
||||
removeMutations.push_back(ShadowViewMutation::RemoveMutation(
|
||||
parentShadowView, oldChildPair.shadowView, index));
|
||||
|
||||
auto const it = insertedPairs.find(oldChildPair.shadowView.tag);
|
||||
|
||||
if (it == insertedPairs.end()) {
|
||||
// The old view was *not* (re)inserted.
|
||||
// We have to generate `delete` mutation and apply the algorithm
|
||||
// recursively.
|
||||
deleteMutations.push_back(
|
||||
ShadowViewMutation::DeleteMutation(oldChildPair.shadowView));
|
||||
|
||||
// We also have to call the algorithm recursively to clean up the entire
|
||||
// subtree starting from the removed view.
|
||||
calculateShadowViewMutationsClassic(
|
||||
destructiveDownwardMutations,
|
||||
oldChildPair.shadowView,
|
||||
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode),
|
||||
{});
|
||||
} else {
|
||||
// The old view *was* (re)inserted.
|
||||
// We have to call the algorithm recursively if the inserted view
|
||||
// is *not* the same as removed one.
|
||||
auto const &newChildPair = *it->second;
|
||||
|
||||
if (newChildPair != oldChildPair) {
|
||||
auto oldGrandChildPairs =
|
||||
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
|
||||
auto newGrandChildPairs =
|
||||
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
|
||||
calculateShadowViewMutationsClassic(
|
||||
*(newGrandChildPairs.size() ? &downwardMutations
|
||||
: &destructiveDownwardMutations),
|
||||
newChildPair.shadowView,
|
||||
std::move(oldGrandChildPairs),
|
||||
std::move(newGrandChildPairs));
|
||||
}
|
||||
|
||||
// In any case we have to remove the view from `insertedPairs` as
|
||||
// indication that the view was actually removed (which means that
|
||||
// the view existed before), hence we don't have to generate
|
||||
// `create` mutation.
|
||||
insertedPairs.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
// Stage 4: Collecting `Create` mutations
|
||||
for (index = lastIndexAfterFirstStage; index < newChildPairs.size();
|
||||
index++) {
|
||||
auto const &newChildPair = newChildPairs[index];
|
||||
|
||||
if (insertedPairs.find(newChildPair.shadowView.tag) ==
|
||||
insertedPairs.end()) {
|
||||
// The new view was (re)inserted, so there is no need to create it.
|
||||
continue;
|
||||
}
|
||||
|
||||
createMutations.push_back(
|
||||
ShadowViewMutation::CreateMutation(newChildPair.shadowView));
|
||||
|
||||
calculateShadowViewMutationsClassic(
|
||||
downwardMutations,
|
||||
newChildPair.shadowView,
|
||||
{},
|
||||
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode));
|
||||
}
|
||||
|
||||
// All mutations in an optimal order:
|
||||
std::move(
|
||||
destructiveDownwardMutations.begin(),
|
||||
destructiveDownwardMutations.end(),
|
||||
std::back_inserter(mutations));
|
||||
std::move(
|
||||
updateMutations.begin(),
|
||||
updateMutations.end(),
|
||||
std::back_inserter(mutations));
|
||||
std::move(
|
||||
removeMutations.rbegin(),
|
||||
removeMutations.rend(),
|
||||
std::back_inserter(mutations));
|
||||
std::move(
|
||||
deleteMutations.begin(),
|
||||
deleteMutations.end(),
|
||||
std::back_inserter(mutations));
|
||||
std::move(
|
||||
createMutations.begin(),
|
||||
createMutations.end(),
|
||||
std::back_inserter(mutations));
|
||||
std::move(
|
||||
downwardMutations.begin(),
|
||||
downwardMutations.end(),
|
||||
std::back_inserter(mutations));
|
||||
std::move(
|
||||
insertMutations.begin(),
|
||||
insertMutations.end(),
|
||||
std::back_inserter(mutations));
|
||||
}
|
||||
|
||||
static void calculateShadowViewMutationsOptimizedMoves(
|
||||
ShadowViewMutation::List &mutations,
|
||||
ShadowView const &parentShadowView,
|
||||
ShadowViewNodePair::List &&oldChildPairs,
|
||||
ShadowViewNodePair::List &&newChildPairs) {
|
||||
if (oldChildPairs.size() == 0 && newChildPairs.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sorting pairs based on `orderIndex` if needed.
|
||||
reorderInPlaceIfNeeded(oldChildPairs);
|
||||
reorderInPlaceIfNeeded(newChildPairs);
|
||||
|
||||
auto index = int{0};
|
||||
|
||||
// Lists of mutations
|
||||
auto createMutations = ShadowViewMutation::List{};
|
||||
auto deleteMutations = ShadowViewMutation::List{};
|
||||
auto insertMutations = ShadowViewMutation::List{};
|
||||
auto removeMutations = ShadowViewMutation::List{};
|
||||
auto updateMutations = ShadowViewMutation::List{};
|
||||
auto downwardMutations = ShadowViewMutation::List{};
|
||||
auto destructiveDownwardMutations = ShadowViewMutation::List{};
|
||||
|
||||
// Stage 1: Collecting `Update` mutations
|
||||
for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size();
|
||||
index++) {
|
||||
auto const &oldChildPair = oldChildPairs[index];
|
||||
auto const &newChildPair = newChildPairs[index];
|
||||
|
||||
if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) {
|
||||
// Totally different nodes, updating is impossible.
|
||||
break;
|
||||
}
|
||||
|
||||
if (oldChildPair.shadowView != newChildPair.shadowView) {
|
||||
updateMutations.push_back(ShadowViewMutation::UpdateMutation(
|
||||
parentShadowView,
|
||||
oldChildPair.shadowView,
|
||||
newChildPair.shadowView,
|
||||
index));
|
||||
}
|
||||
|
||||
auto oldGrandChildPairs =
|
||||
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
|
||||
auto newGrandChildPairs =
|
||||
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
|
||||
calculateShadowViewMutationsOptimizedMoves(
|
||||
*(newGrandChildPairs.size() ? &downwardMutations
|
||||
: &destructiveDownwardMutations),
|
||||
oldChildPair.shadowView,
|
||||
std::move(oldGrandChildPairs),
|
||||
std::move(newGrandChildPairs));
|
||||
}
|
||||
|
||||
int lastIndexAfterFirstStage = index;
|
||||
|
||||
if (index == newChildPairs.size()) {
|
||||
// We've reached the end of the new children. We can delete+remove the
|
||||
// rest.
|
||||
for (; index < oldChildPairs.size(); index++) {
|
||||
auto const &oldChildPair = oldChildPairs[index];
|
||||
|
||||
deleteMutations.push_back(
|
||||
ShadowViewMutation::DeleteMutation(oldChildPair.shadowView));
|
||||
removeMutations.push_back(ShadowViewMutation::RemoveMutation(
|
||||
parentShadowView, oldChildPair.shadowView, index));
|
||||
|
||||
// We also have to call the algorithm recursively to clean up the entire
|
||||
// subtree starting from the removed view.
|
||||
calculateShadowViewMutationsOptimizedMoves(
|
||||
destructiveDownwardMutations,
|
||||
oldChildPair.shadowView,
|
||||
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode),
|
||||
{});
|
||||
}
|
||||
} else if (index == oldChildPairs.size()) {
|
||||
// If we don't have any more existing children we can choose a fast path
|
||||
// since the rest will all be create+insert.
|
||||
for (; index < newChildPairs.size(); index++) {
|
||||
auto const &newChildPair = newChildPairs[index];
|
||||
|
||||
insertMutations.push_back(ShadowViewMutation::InsertMutation(
|
||||
parentShadowView, newChildPair.shadowView, index));
|
||||
createMutations.push_back(
|
||||
ShadowViewMutation::CreateMutation(newChildPair.shadowView));
|
||||
|
||||
calculateShadowViewMutationsOptimizedMoves(
|
||||
downwardMutations,
|
||||
newChildPair.shadowView,
|
||||
{},
|
||||
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode));
|
||||
}
|
||||
} else {
|
||||
// Collect map of tags in the new list
|
||||
// In the future it would be nice to use TinyMap for newInsertedPairs, but
|
||||
// it's challenging to build an iterator that will work for our use-case
|
||||
// here.
|
||||
auto newRemainingPairs = TinyMap<Tag, ShadowViewNodePair const *>{};
|
||||
auto newInsertedPairs = TinyMap<Tag, ShadowViewNodePair const *>{};
|
||||
for (; index < newChildPairs.size(); index++) {
|
||||
auto const &newChildPair = newChildPairs[index];
|
||||
newRemainingPairs.insert({newChildPair.shadowView.tag, &newChildPair});
|
||||
}
|
||||
|
||||
// Walk through both lists at the same time
|
||||
// We will perform updates, create+insert, remove+delete, remove+insert
|
||||
// (move) here.
|
||||
int oldIndex = lastIndexAfterFirstStage,
|
||||
newIndex = lastIndexAfterFirstStage, newSize = newChildPairs.size(),
|
||||
oldSize = oldChildPairs.size();
|
||||
while (newIndex < newSize || oldIndex < oldSize) {
|
||||
bool haveNewPair = newIndex < newSize;
|
||||
bool haveOldPair = oldIndex < oldSize;
|
||||
|
||||
// Advance both pointers if pointing to the same element
|
||||
if (haveNewPair && haveOldPair) {
|
||||
auto const &newChildPair = newChildPairs[newIndex];
|
||||
auto const &oldChildPair = oldChildPairs[oldIndex];
|
||||
|
||||
int newTag = newChildPair.shadowView.tag;
|
||||
int oldTag = oldChildPair.shadowView.tag;
|
||||
|
||||
if (newTag == oldTag) {
|
||||
// Generate Update instructions
|
||||
if (oldChildPair.shadowView != newChildPair.shadowView) {
|
||||
updateMutations.push_back(ShadowViewMutation::UpdateMutation(
|
||||
parentShadowView,
|
||||
oldChildPair.shadowView,
|
||||
newChildPair.shadowView,
|
||||
index));
|
||||
}
|
||||
|
||||
// Remove from newRemainingPairs
|
||||
auto newRemainingPairIt = newRemainingPairs.find(oldTag);
|
||||
if (newRemainingPairIt != newRemainingPairs.end()) {
|
||||
newRemainingPairs.erase(newRemainingPairIt);
|
||||
}
|
||||
|
||||
// Update subtrees
|
||||
auto oldGrandChildPairs =
|
||||
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
|
||||
auto newGrandChildPairs =
|
||||
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
|
||||
calculateShadowViewMutationsOptimizedMoves(
|
||||
*(newGrandChildPairs.size() ? &downwardMutations
|
||||
: &destructiveDownwardMutations),
|
||||
oldChildPair.shadowView,
|
||||
std::move(oldGrandChildPairs),
|
||||
std::move(newGrandChildPairs));
|
||||
|
||||
newIndex++;
|
||||
oldIndex++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (haveOldPair) {
|
||||
auto const &oldChildPair = oldChildPairs[oldIndex];
|
||||
int oldTag = oldChildPair.shadowView.tag;
|
||||
|
||||
// Was oldTag already inserted? This indicates a reordering, not just
|
||||
// a move. The new node has already been inserted, we just need to
|
||||
// remove the node from its old position now.
|
||||
auto const insertedIt = newInsertedPairs.find(oldTag);
|
||||
if (insertedIt != newInsertedPairs.end()) {
|
||||
removeMutations.push_back(ShadowViewMutation::RemoveMutation(
|
||||
parentShadowView, oldChildPair.shadowView, oldIndex));
|
||||
|
||||
// Generate update instruction since we have an iterator ref to the
|
||||
// new node
|
||||
auto const &newChildPair = *insertedIt->second;
|
||||
if (oldChildPair.shadowView != newChildPair.shadowView) {
|
||||
updateMutations.push_back(ShadowViewMutation::UpdateMutation(
|
||||
parentShadowView,
|
||||
oldChildPair.shadowView,
|
||||
newChildPair.shadowView,
|
||||
index));
|
||||
}
|
||||
|
||||
// Update subtrees
|
||||
auto oldGrandChildPairs =
|
||||
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
|
||||
auto newGrandChildPairs =
|
||||
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
|
||||
calculateShadowViewMutationsOptimizedMoves(
|
||||
*(newGrandChildPairs.size() ? &downwardMutations
|
||||
: &destructiveDownwardMutations),
|
||||
oldChildPair.shadowView,
|
||||
std::move(oldGrandChildPairs),
|
||||
std::move(newGrandChildPairs));
|
||||
|
||||
newInsertedPairs.erase(insertedIt);
|
||||
oldIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Should we generate a delete+remove instruction for the old node?
|
||||
// If there's an old node and it's not found in the "new" list, we
|
||||
// generate remove+delete for this node and its subtree.
|
||||
auto const newIt = newRemainingPairs.find(oldTag);
|
||||
if (newIt == newRemainingPairs.end()) {
|
||||
removeMutations.push_back(ShadowViewMutation::RemoveMutation(
|
||||
parentShadowView, oldChildPair.shadowView, oldIndex));
|
||||
deleteMutations.push_back(
|
||||
ShadowViewMutation::DeleteMutation(oldChildPair.shadowView));
|
||||
|
||||
// We also have to call the algorithm recursively to clean up the
|
||||
// entire subtree starting from the removed view.
|
||||
calculateShadowViewMutationsOptimizedMoves(
|
||||
destructiveDownwardMutations,
|
||||
oldChildPair.shadowView,
|
||||
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode),
|
||||
{});
|
||||
|
||||
oldIndex++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, oldTag is -1 or is in the new list, and hasn't been
|
||||
// inserted or matched yet We're not sure yet if the new node is in the
|
||||
// old list - generate an insert instruction for the new node.
|
||||
auto const &newChildPair = newChildPairs[newIndex];
|
||||
insertMutations.push_back(ShadowViewMutation::InsertMutation(
|
||||
parentShadowView, newChildPair.shadowView, newIndex));
|
||||
newInsertedPairs.insert({newChildPair.shadowView.tag, &newChildPair});
|
||||
newIndex++;
|
||||
}
|
||||
|
||||
// Final step: generate Create instructions for new nodes
|
||||
for (auto it = newInsertedPairs.begin(); it != newInsertedPairs.end();
|
||||
it++) {
|
||||
auto const &newChildPair = *it->second;
|
||||
createMutations.push_back(
|
||||
ShadowViewMutation::CreateMutation(newChildPair.shadowView));
|
||||
|
||||
calculateShadowViewMutationsOptimizedMoves(
|
||||
downwardMutations,
|
||||
newChildPair.shadowView,
|
||||
{},
|
||||
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode));
|
||||
}
|
||||
}
|
||||
|
||||
// All mutations in an optimal order:
|
||||
std::move(
|
||||
destructiveDownwardMutations.begin(),
|
||||
destructiveDownwardMutations.end(),
|
||||
std::back_inserter(mutations));
|
||||
std::move(
|
||||
updateMutations.begin(),
|
||||
updateMutations.end(),
|
||||
std::back_inserter(mutations));
|
||||
std::move(
|
||||
removeMutations.rbegin(),
|
||||
removeMutations.rend(),
|
||||
std::back_inserter(mutations));
|
||||
std::move(
|
||||
deleteMutations.begin(),
|
||||
deleteMutations.end(),
|
||||
std::back_inserter(mutations));
|
||||
std::move(
|
||||
createMutations.begin(),
|
||||
createMutations.end(),
|
||||
std::back_inserter(mutations));
|
||||
std::move(
|
||||
downwardMutations.begin(),
|
||||
downwardMutations.end(),
|
||||
std::back_inserter(mutations));
|
||||
std::move(
|
||||
insertMutations.begin(),
|
||||
insertMutations.end(),
|
||||
std::back_inserter(mutations));
|
||||
}
|
||||
|
||||
ShadowViewMutation::List calculateShadowViewMutations(
|
||||
DifferentiatorMode differentiatorMode,
|
||||
ShadowNode const &oldRootShadowNode,
|
||||
ShadowNode const &newRootShadowNode) {
|
||||
SystraceSection s("calculateShadowViewMutations");
|
||||
|
||||
// Root shadow nodes must be belong the same family.
|
||||
assert(ShadowNode::sameFamily(oldRootShadowNode, newRootShadowNode));
|
||||
|
||||
auto mutations = ShadowViewMutation::List{};
|
||||
mutations.reserve(256);
|
||||
|
||||
auto oldRootShadowView = ShadowView(oldRootShadowNode);
|
||||
auto newRootShadowView = ShadowView(newRootShadowNode);
|
||||
|
||||
if (oldRootShadowView != newRootShadowView) {
|
||||
mutations.push_back(ShadowViewMutation::UpdateMutation(
|
||||
ShadowView(), oldRootShadowView, newRootShadowView, -1));
|
||||
}
|
||||
|
||||
if (differentiatorMode == DifferentiatorMode::Classic) {
|
||||
calculateShadowViewMutationsClassic(
|
||||
mutations,
|
||||
ShadowView(oldRootShadowNode),
|
||||
sliceChildShadowNodeViewPairs(oldRootShadowNode),
|
||||
sliceChildShadowNodeViewPairs(newRootShadowNode));
|
||||
} else {
|
||||
calculateShadowViewMutationsOptimizedMoves(
|
||||
mutations,
|
||||
ShadowView(oldRootShadowNode),
|
||||
sliceChildShadowNodeViewPairs(oldRootShadowNode),
|
||||
sliceChildShadowNodeViewPairs(newRootShadowNode));
|
||||
}
|
||||
|
||||
return mutations;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
36
node_modules/react-native/ReactCommon/fabric/mounting/Differentiator.h
generated
vendored
Normal file
36
node_modules/react-native/ReactCommon/fabric/mounting/Differentiator.h
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <react/core/ShadowNode.h>
|
||||
#include <react/mounting/ShadowViewMutation.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
enum class DifferentiatorMode { Classic, OptimizedMoves };
|
||||
|
||||
/*
|
||||
* Calculates a list of view mutations which describes how the old
|
||||
* `ShadowTree` can be transformed to the new one.
|
||||
* The list of mutations might be and might not be optimal.
|
||||
*/
|
||||
ShadowViewMutationList calculateShadowViewMutations(
|
||||
DifferentiatorMode differentiatorMode,
|
||||
ShadowNode const &oldRootShadowNode,
|
||||
ShadowNode const &newRootShadowNode);
|
||||
|
||||
/*
|
||||
* Generates a list of `ShadowViewNodePair`s that represents a layer of a
|
||||
* flattened view hierarchy.
|
||||
*/
|
||||
ShadowViewNodePair::List sliceChildShadowNodeViewPairs(
|
||||
ShadowNode const &shadowNode);
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
124
node_modules/react-native/ReactCommon/fabric/mounting/MountingCoordinator.cpp
generated
vendored
Normal file
124
node_modules/react-native/ReactCommon/fabric/mounting/MountingCoordinator.cpp
generated
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "MountingCoordinator.h"
|
||||
|
||||
#ifdef RN_SHADOW_TREE_INTROSPECTION
|
||||
#include <glog/logging.h>
|
||||
#include <sstream>
|
||||
#endif
|
||||
|
||||
#include <condition_variable>
|
||||
|
||||
#include <react/mounting/ShadowViewMutation.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
MountingCoordinator::MountingCoordinator(ShadowTreeRevision baseRevision)
|
||||
: surfaceId_(baseRevision.getRootShadowNode().getSurfaceId()),
|
||||
baseRevision_(baseRevision) {
|
||||
#ifdef RN_SHADOW_TREE_INTROSPECTION
|
||||
stubViewTree_ = stubViewTreeFromShadowNode(baseRevision_.getRootShadowNode());
|
||||
#endif
|
||||
}
|
||||
|
||||
SurfaceId MountingCoordinator::getSurfaceId() const {
|
||||
return surfaceId_;
|
||||
}
|
||||
|
||||
void MountingCoordinator::push(ShadowTreeRevision &&revision) const {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
assert(revision.getNumber() > baseRevision_.getNumber());
|
||||
assert(
|
||||
!lastRevision_.has_value() ||
|
||||
revision.getNumber() != lastRevision_->getNumber());
|
||||
|
||||
if (!lastRevision_.has_value() ||
|
||||
lastRevision_->getNumber() < revision.getNumber()) {
|
||||
lastRevision_ = std::move(revision);
|
||||
}
|
||||
}
|
||||
|
||||
signal_.notify_all();
|
||||
}
|
||||
|
||||
void MountingCoordinator::revoke() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
// We have two goals here.
|
||||
// 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();
|
||||
lastRevision_.reset();
|
||||
}
|
||||
|
||||
bool MountingCoordinator::waitForTransaction(
|
||||
std::chrono::duration<double> timeout) const {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
return signal_.wait_for(
|
||||
lock, timeout, [this]() { return lastRevision_.has_value(); });
|
||||
}
|
||||
|
||||
better::optional<MountingTransaction> MountingCoordinator::pullTransaction(
|
||||
DifferentiatorMode differentiatorMode) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (!lastRevision_.has_value()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
number_++;
|
||||
|
||||
auto telemetry = lastRevision_->getTelemetry();
|
||||
telemetry.willDiff();
|
||||
|
||||
auto mutations = calculateShadowViewMutations(
|
||||
differentiatorMode,
|
||||
baseRevision_.getRootShadowNode(),
|
||||
lastRevision_->getRootShadowNode());
|
||||
|
||||
telemetry.didDiff();
|
||||
|
||||
#ifdef RN_SHADOW_TREE_INTROSPECTION
|
||||
stubViewTree_.mutate(mutations);
|
||||
auto stubViewTree =
|
||||
stubViewTreeFromShadowNode(lastRevision_->getRootShadowNode());
|
||||
|
||||
std::string line;
|
||||
|
||||
std::stringstream ssOldTree(
|
||||
baseRevision_.getRootShadowNode().getDebugDescription());
|
||||
while (std::getline(ssOldTree, line, '\n')) {
|
||||
LOG(ERROR) << "Old tree:" << line;
|
||||
}
|
||||
|
||||
std::stringstream ssNewTree(
|
||||
lastRevision_->getRootShadowNode().getDebugDescription());
|
||||
while (std::getline(ssNewTree, line, '\n')) {
|
||||
LOG(ERROR) << "New tree:" << line;
|
||||
}
|
||||
|
||||
std::stringstream ssMutations(getDebugDescription(mutations, {}));
|
||||
while (std::getline(ssMutations, line, '\n')) {
|
||||
LOG(ERROR) << "Mutations:" << line;
|
||||
}
|
||||
|
||||
assert(stubViewTree_ == stubViewTree);
|
||||
#endif
|
||||
|
||||
baseRevision_ = std::move(*lastRevision_);
|
||||
lastRevision_.reset();
|
||||
|
||||
return MountingTransaction{
|
||||
surfaceId_, number_, std::move(mutations), telemetry};
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
101
node_modules/react-native/ReactCommon/fabric/mounting/MountingCoordinator.h
generated
vendored
Normal file
101
node_modules/react-native/ReactCommon/fabric/mounting/MountingCoordinator.h
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <better/optional.h>
|
||||
#include <chrono>
|
||||
|
||||
#include <react/mounting/Differentiator.h>
|
||||
#include <react/mounting/MountingTransaction.h>
|
||||
#include <react/mounting/ShadowTreeRevision.h>
|
||||
|
||||
#ifdef RN_SHADOW_TREE_INTROSPECTION
|
||||
#include <react/mounting/stubs.h>
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* Stores inside all non-mounted yet revisions of a shadow tree and coordinates
|
||||
* mounting. The object stores the most recent mounted revision and the most
|
||||
* recent committed one. Then when a new mounting transaction is requested the
|
||||
* object generates mutation instructions and returns it as a
|
||||
* `MountingTransaction`.
|
||||
*/
|
||||
class MountingCoordinator final {
|
||||
public:
|
||||
using Shared = std::shared_ptr<MountingCoordinator const>;
|
||||
|
||||
/*
|
||||
* The constructor is ment to be used only inside `ShadowTree`, and it's
|
||||
* `public` only to enable using with `std::make_shared<>`.
|
||||
*/
|
||||
MountingCoordinator(ShadowTreeRevision baseRevision);
|
||||
|
||||
/*
|
||||
* Returns the id of the surface that the coordinator belongs to.
|
||||
*/
|
||||
SurfaceId getSurfaceId() const;
|
||||
|
||||
/*
|
||||
* Computes a consequent mounting transaction and returns it.
|
||||
* The returning transaction can accumulate multiple recent revisions of a
|
||||
* shadow tree. Returns empty optional if there no new shadow tree revision to
|
||||
* mount.
|
||||
* The method is thread-safe and can be called from any thread.
|
||||
* However, a consumer should always call it on the same thread (e.g. on the
|
||||
* main thread) or ensure sequentiality of mount transactions separately.
|
||||
*/
|
||||
better::optional<MountingTransaction> pullTransaction(
|
||||
DifferentiatorMode differentiatorMode) const;
|
||||
|
||||
/*
|
||||
* Blocks the current thread until a new mounting transaction is available or
|
||||
* after the specified `timeout` duration.
|
||||
* Returns `false` if a timeout occurred before a new transaction available.
|
||||
* Call `pullTransaction` right after the method to retrieve the transaction.
|
||||
* Similarly to `pullTransaction` this method is thread-safe but the consumer
|
||||
* should call it on the same thread (e.g. on the main thread) or ensure
|
||||
* sequentiality of mount transactions separately.
|
||||
*/
|
||||
bool waitForTransaction(std::chrono::duration<double> timeout) const;
|
||||
|
||||
private:
|
||||
friend class ShadowTree;
|
||||
|
||||
/*
|
||||
* Methods from this section are meant to be used by `ShadowTree` only.
|
||||
*/
|
||||
void push(ShadowTreeRevision &&revision) const;
|
||||
|
||||
/*
|
||||
* Revokes the last pushed `ShadowTreeRevision`.
|
||||
* Generating a `MountingTransaction` requires some resources which the
|
||||
* `MountingCoordinator` does not own (e.g. `ComponentDescriptor`s). Revoking
|
||||
* committed revisions allows the owner (a Shadow Tree) to make sure that
|
||||
* those resources will not be accessed (e.g. by the Mouting Layer).
|
||||
*/
|
||||
void revoke() const;
|
||||
|
||||
private:
|
||||
SurfaceId const surfaceId_;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
mutable ShadowTreeRevision baseRevision_;
|
||||
mutable better::optional<ShadowTreeRevision> lastRevision_{};
|
||||
mutable MountingTransaction::Number number_{0};
|
||||
mutable std::condition_variable signal_;
|
||||
|
||||
#ifdef RN_SHADOW_TREE_INTROSPECTION
|
||||
mutable StubViewTree stubViewTree_; // Protected by `mutex_`.
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
117
node_modules/react-native/ReactCommon/fabric/mounting/MountingTelemetry.cpp
generated
vendored
Normal file
117
node_modules/react-native/ReactCommon/fabric/mounting/MountingTelemetry.cpp
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "MountingTelemetry.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void MountingTelemetry::willCommit() {
|
||||
assert(commitStartTime_ == kTelemetryUndefinedTimePoint);
|
||||
assert(commitEndTime_ == kTelemetryUndefinedTimePoint);
|
||||
commitStartTime_ = telemetryTimePointNow();
|
||||
commitNumber_++;
|
||||
}
|
||||
|
||||
void MountingTelemetry::didCommit() {
|
||||
assert(commitStartTime_ != kTelemetryUndefinedTimePoint);
|
||||
assert(commitEndTime_ == kTelemetryUndefinedTimePoint);
|
||||
commitEndTime_ = telemetryTimePointNow();
|
||||
}
|
||||
|
||||
void MountingTelemetry::willDiff() {
|
||||
assert(diffStartTime_ == kTelemetryUndefinedTimePoint);
|
||||
assert(diffEndTime_ == kTelemetryUndefinedTimePoint);
|
||||
diffStartTime_ = telemetryTimePointNow();
|
||||
}
|
||||
|
||||
void MountingTelemetry::didDiff() {
|
||||
assert(diffStartTime_ != kTelemetryUndefinedTimePoint);
|
||||
assert(diffEndTime_ == kTelemetryUndefinedTimePoint);
|
||||
diffEndTime_ = telemetryTimePointNow();
|
||||
}
|
||||
|
||||
void MountingTelemetry::willLayout() {
|
||||
assert(layoutStartTime_ == kTelemetryUndefinedTimePoint);
|
||||
assert(layoutEndTime_ == kTelemetryUndefinedTimePoint);
|
||||
layoutStartTime_ = telemetryTimePointNow();
|
||||
}
|
||||
|
||||
void MountingTelemetry::didLayout() {
|
||||
assert(layoutStartTime_ != kTelemetryUndefinedTimePoint);
|
||||
assert(layoutEndTime_ == kTelemetryUndefinedTimePoint);
|
||||
layoutEndTime_ = telemetryTimePointNow();
|
||||
}
|
||||
|
||||
void MountingTelemetry::willMount() {
|
||||
assert(mountStartTime_ == kTelemetryUndefinedTimePoint);
|
||||
assert(mountEndTime_ == kTelemetryUndefinedTimePoint);
|
||||
mountStartTime_ = telemetryTimePointNow();
|
||||
}
|
||||
|
||||
void MountingTelemetry::didMount() {
|
||||
assert(mountStartTime_ != kTelemetryUndefinedTimePoint);
|
||||
assert(mountEndTime_ == kTelemetryUndefinedTimePoint);
|
||||
mountEndTime_ = telemetryTimePointNow();
|
||||
}
|
||||
|
||||
TelemetryTimePoint MountingTelemetry::getDiffStartTime() const {
|
||||
assert(diffStartTime_ != kTelemetryUndefinedTimePoint);
|
||||
assert(diffEndTime_ != kTelemetryUndefinedTimePoint);
|
||||
return diffStartTime_;
|
||||
}
|
||||
|
||||
TelemetryTimePoint MountingTelemetry::getDiffEndTime() const {
|
||||
assert(diffStartTime_ != kTelemetryUndefinedTimePoint);
|
||||
assert(diffEndTime_ != kTelemetryUndefinedTimePoint);
|
||||
return diffEndTime_;
|
||||
}
|
||||
|
||||
TelemetryTimePoint MountingTelemetry::getCommitStartTime() const {
|
||||
assert(commitStartTime_ != kTelemetryUndefinedTimePoint);
|
||||
assert(commitEndTime_ != kTelemetryUndefinedTimePoint);
|
||||
return commitStartTime_;
|
||||
}
|
||||
|
||||
TelemetryTimePoint MountingTelemetry::getCommitEndTime() const {
|
||||
assert(commitStartTime_ != kTelemetryUndefinedTimePoint);
|
||||
assert(commitEndTime_ != kTelemetryUndefinedTimePoint);
|
||||
return commitEndTime_;
|
||||
}
|
||||
|
||||
TelemetryTimePoint MountingTelemetry::getLayoutStartTime() const {
|
||||
assert(layoutStartTime_ != kTelemetryUndefinedTimePoint);
|
||||
assert(layoutEndTime_ != kTelemetryUndefinedTimePoint);
|
||||
return layoutStartTime_;
|
||||
}
|
||||
|
||||
TelemetryTimePoint MountingTelemetry::getLayoutEndTime() const {
|
||||
assert(layoutStartTime_ != kTelemetryUndefinedTimePoint);
|
||||
assert(layoutEndTime_ != kTelemetryUndefinedTimePoint);
|
||||
return layoutEndTime_;
|
||||
}
|
||||
|
||||
TelemetryTimePoint MountingTelemetry::getMountStartTime() const {
|
||||
assert(mountStartTime_ != kTelemetryUndefinedTimePoint);
|
||||
assert(mountEndTime_ != kTelemetryUndefinedTimePoint);
|
||||
return mountStartTime_;
|
||||
}
|
||||
|
||||
TelemetryTimePoint MountingTelemetry::getMountEndTime() const {
|
||||
assert(mountStartTime_ != kTelemetryUndefinedTimePoint);
|
||||
assert(mountEndTime_ != kTelemetryUndefinedTimePoint);
|
||||
return mountEndTime_;
|
||||
}
|
||||
|
||||
int MountingTelemetry::getCommitNumber() const {
|
||||
return commitNumber_;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
64
node_modules/react-native/ReactCommon/fabric/mounting/MountingTelemetry.h
generated
vendored
Normal file
64
node_modules/react-native/ReactCommon/fabric/mounting/MountingTelemetry.h
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
#include <react/utils/Telemetry.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* Represent arbitrary telemetry data that can be associated with the
|
||||
* particular revision of `ShadowTree`.
|
||||
*/
|
||||
class MountingTelemetry final {
|
||||
public:
|
||||
/*
|
||||
* Signaling
|
||||
*/
|
||||
void willDiff();
|
||||
void didDiff();
|
||||
void willCommit();
|
||||
void didCommit();
|
||||
void willLayout();
|
||||
void didLayout();
|
||||
void willMount();
|
||||
void didMount();
|
||||
|
||||
/*
|
||||
* Reading
|
||||
*/
|
||||
TelemetryTimePoint getDiffStartTime() const;
|
||||
TelemetryTimePoint getDiffEndTime() const;
|
||||
TelemetryTimePoint getLayoutStartTime() const;
|
||||
TelemetryTimePoint getLayoutEndTime() const;
|
||||
TelemetryTimePoint getCommitStartTime() const;
|
||||
TelemetryTimePoint getCommitEndTime() const;
|
||||
TelemetryTimePoint getMountStartTime() const;
|
||||
TelemetryTimePoint getMountEndTime() const;
|
||||
|
||||
int getCommitNumber() const;
|
||||
|
||||
private:
|
||||
TelemetryTimePoint diffStartTime_{kTelemetryUndefinedTimePoint};
|
||||
TelemetryTimePoint diffEndTime_{kTelemetryUndefinedTimePoint};
|
||||
TelemetryTimePoint commitStartTime_{kTelemetryUndefinedTimePoint};
|
||||
TelemetryTimePoint commitEndTime_{kTelemetryUndefinedTimePoint};
|
||||
TelemetryTimePoint layoutStartTime_{kTelemetryUndefinedTimePoint};
|
||||
TelemetryTimePoint layoutEndTime_{kTelemetryUndefinedTimePoint};
|
||||
TelemetryTimePoint mountStartTime_{kTelemetryUndefinedTimePoint};
|
||||
TelemetryTimePoint mountEndTime_{kTelemetryUndefinedTimePoint};
|
||||
|
||||
int commitNumber_{0};
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
46
node_modules/react-native/ReactCommon/fabric/mounting/MountingTransaction.cpp
generated
vendored
Normal file
46
node_modules/react-native/ReactCommon/fabric/mounting/MountingTransaction.cpp
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "MountingTransaction.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using Number = MountingTransaction::Number;
|
||||
|
||||
MountingTransaction::MountingTransaction(
|
||||
SurfaceId surfaceId,
|
||||
Number number,
|
||||
ShadowViewMutationList &&mutations,
|
||||
MountingTelemetry telemetry)
|
||||
: surfaceId_(surfaceId),
|
||||
number_(number),
|
||||
mutations_(std::move(mutations)),
|
||||
telemetry_(std::move(telemetry)) {}
|
||||
|
||||
ShadowViewMutationList const &MountingTransaction::getMutations() const & {
|
||||
return mutations_;
|
||||
}
|
||||
|
||||
ShadowViewMutationList MountingTransaction::getMutations() && {
|
||||
return std::move(mutations_);
|
||||
}
|
||||
|
||||
MountingTelemetry const &MountingTransaction::getTelemetry() const {
|
||||
return telemetry_;
|
||||
}
|
||||
|
||||
SurfaceId MountingTransaction::getSurfaceId() const {
|
||||
return surfaceId_;
|
||||
}
|
||||
|
||||
Number MountingTransaction::getNumber() const {
|
||||
return number_;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
87
node_modules/react-native/ReactCommon/fabric/mounting/MountingTransaction.h
generated
vendored
Normal file
87
node_modules/react-native/ReactCommon/fabric/mounting/MountingTransaction.h
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <react/mounting/MountingTelemetry.h>
|
||||
#include <react/mounting/ShadowViewMutation.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* Encapsulates all artifacts of `ShadowTree` commit (or a series of them),
|
||||
* particularly list of mutations and meta-data associated with the commit.
|
||||
* Movable and copyable, but moving is strongly encouraged.
|
||||
* Beware: A moved-from object of this type has unspecified value and accessing
|
||||
* that is UB.
|
||||
*/
|
||||
class MountingTransaction final {
|
||||
public:
|
||||
/*
|
||||
* A Number (or revision) grows continuously starting from `1`. Value `0`
|
||||
* represents the state before the very first transaction happens.
|
||||
*/
|
||||
using Number = int64_t;
|
||||
|
||||
/*
|
||||
* Copying a list of `ShadowViewMutation` is expensive, so the constructor
|
||||
* accepts it as rvalue reference to discourage copying.
|
||||
*/
|
||||
MountingTransaction(
|
||||
SurfaceId surfaceId,
|
||||
Number number,
|
||||
ShadowViewMutationList &&mutations,
|
||||
MountingTelemetry telemetry);
|
||||
|
||||
/*
|
||||
* Copy semantic.
|
||||
* Copying of MountingTransaction is expensive, so copy-constructor is
|
||||
* explicit and copy-assignment is deleted to prevent accidental copying.
|
||||
*/
|
||||
explicit MountingTransaction(const MountingTransaction &mountingTransaction) =
|
||||
default;
|
||||
MountingTransaction &operator=(const MountingTransaction &other) = delete;
|
||||
|
||||
/*
|
||||
* Move semantic.
|
||||
*/
|
||||
MountingTransaction(MountingTransaction &&mountingTransaction) noexcept =
|
||||
default;
|
||||
MountingTransaction &operator=(MountingTransaction &&other) = default;
|
||||
|
||||
/*
|
||||
* Returns a list of mutations that represent the transaction. The list can be
|
||||
* empty (theoretically).
|
||||
*/
|
||||
ShadowViewMutationList const &getMutations() const &;
|
||||
ShadowViewMutationList getMutations() &&;
|
||||
|
||||
/*
|
||||
* Returns telemetry associated with this transaction.
|
||||
*/
|
||||
MountingTelemetry const &getTelemetry() const;
|
||||
|
||||
/*
|
||||
* Returns the id of the surface that the transaction belongs to.
|
||||
*/
|
||||
SurfaceId getSurfaceId() const;
|
||||
|
||||
/*
|
||||
* Returns a sequential number of the particular transaction.
|
||||
*/
|
||||
Number getNumber() const;
|
||||
|
||||
private:
|
||||
SurfaceId surfaceId_;
|
||||
Number number_;
|
||||
ShadowViewMutationList mutations_;
|
||||
MountingTelemetry telemetry_;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
12
node_modules/react-native/ReactCommon/fabric/mounting/MountingTransactionMetadata.cpp
generated
vendored
Normal file
12
node_modules/react-native/ReactCommon/fabric/mounting/MountingTransactionMetadata.cpp
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "MountingTransactionMetadata.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {} // namespace react
|
||||
} // namespace facebook
|
31
node_modules/react-native/ReactCommon/fabric/mounting/MountingTransactionMetadata.h
generated
vendored
Normal file
31
node_modules/react-native/ReactCommon/fabric/mounting/MountingTransactionMetadata.h
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <react/mounting/MountingTelemetry.h>
|
||||
#include <react/mounting/MountingTransaction.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* Contains all (meta)information related to a MountingTransaction except a list
|
||||
* of mutation instructions.
|
||||
* The class is meant to be used when a consumer should not have access to all
|
||||
* information about the transaction (incapsulation) but still needs to observe
|
||||
* it to produce some side-effects.
|
||||
*/
|
||||
class MountingTransactionMetadata final {
|
||||
public:
|
||||
SurfaceId surfaceId;
|
||||
MountingTransaction::Number number;
|
||||
MountingTelemetry telemetry;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
258
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTree.cpp
generated
vendored
Normal file
258
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTree.cpp
generated
vendored
Normal file
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "ShadowTree.h"
|
||||
|
||||
#include <react/components/root/RootComponentDescriptor.h>
|
||||
#include <react/components/view/ViewShadowNode.h>
|
||||
#include <react/core/LayoutContext.h>
|
||||
#include <react/core/LayoutPrimitives.h>
|
||||
#include <react/debug/SystraceSection.h>
|
||||
#include <react/mounting/MountingTelemetry.h>
|
||||
#include <react/mounting/ShadowTreeRevision.h>
|
||||
#include <react/mounting/ShadowViewMutation.h>
|
||||
|
||||
#include "ShadowTreeDelegate.h"
|
||||
#include "TreeStateReconciliation.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
static void updateMountedFlag(
|
||||
const SharedShadowNodeList &oldChildren,
|
||||
const SharedShadowNodeList &newChildren) {
|
||||
// This is a simplified version of Diffing algorithm that only updates
|
||||
// `mounted` flag on `ShadowNode`s. The algorithm sets "mounted" flag before
|
||||
// "unmounted" to allow `ShadowNode` detect a situation where the node was
|
||||
// remounted.
|
||||
|
||||
if (&oldChildren == &newChildren) {
|
||||
// Lists are identical, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldChildren.size() == 0 && newChildren.size() == 0) {
|
||||
// Both lists are empty, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
int index;
|
||||
|
||||
// Stage 1: Mount and unmount "updated" children.
|
||||
for (index = 0; index < oldChildren.size() && index < newChildren.size();
|
||||
index++) {
|
||||
const auto &oldChild = oldChildren[index];
|
||||
const auto &newChild = newChildren[index];
|
||||
|
||||
if (oldChild == newChild) {
|
||||
// Nodes are identical, skipping the subtree.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ShadowNode::sameFamily(*oldChild, *newChild)) {
|
||||
// Totally different nodes, updating is impossible.
|
||||
break;
|
||||
}
|
||||
|
||||
newChild->setMounted(true);
|
||||
oldChild->setMounted(false);
|
||||
|
||||
updateMountedFlag(oldChild->getChildren(), newChild->getChildren());
|
||||
}
|
||||
|
||||
int lastIndexAfterFirstStage = index;
|
||||
|
||||
// State 2: Mount new children.
|
||||
for (index = lastIndexAfterFirstStage; index < newChildren.size(); index++) {
|
||||
const auto &newChild = newChildren[index];
|
||||
newChild->setMounted(true);
|
||||
updateMountedFlag({}, newChild->getChildren());
|
||||
}
|
||||
|
||||
// State 3: Unmount old children.
|
||||
for (index = lastIndexAfterFirstStage; index < oldChildren.size(); index++) {
|
||||
const auto &oldChild = oldChildren[index];
|
||||
oldChild->setMounted(false);
|
||||
updateMountedFlag(oldChild->getChildren(), {});
|
||||
}
|
||||
}
|
||||
|
||||
ShadowTree::ShadowTree(
|
||||
SurfaceId surfaceId,
|
||||
LayoutConstraints const &layoutConstraints,
|
||||
LayoutContext const &layoutContext,
|
||||
RootComponentDescriptor const &rootComponentDescriptor,
|
||||
ShadowTreeDelegate const &delegate)
|
||||
: surfaceId_(surfaceId), delegate_(delegate) {
|
||||
const auto noopEventEmitter = std::make_shared<const ViewEventEmitter>(
|
||||
nullptr, -1, std::shared_ptr<const EventDispatcher>());
|
||||
|
||||
const auto props = std::make_shared<const RootProps>(
|
||||
*RootShadowNode::defaultSharedProps(), layoutConstraints, layoutContext);
|
||||
|
||||
auto family = rootComponentDescriptor.createFamily(
|
||||
ShadowNodeFamilyFragment{surfaceId, surfaceId, noopEventEmitter},
|
||||
nullptr);
|
||||
rootShadowNode_ = std::static_pointer_cast<const RootShadowNode>(
|
||||
rootComponentDescriptor.createShadowNode(
|
||||
ShadowNodeFragment{
|
||||
/* .props = */ props,
|
||||
},
|
||||
family));
|
||||
|
||||
mountingCoordinator_ = std::make_shared<MountingCoordinator const>(
|
||||
ShadowTreeRevision{rootShadowNode_, 0, {}});
|
||||
}
|
||||
|
||||
ShadowTree::~ShadowTree() {
|
||||
mountingCoordinator_->revoke();
|
||||
}
|
||||
|
||||
Tag ShadowTree::getSurfaceId() const {
|
||||
return surfaceId_;
|
||||
}
|
||||
|
||||
MountingCoordinator::Shared ShadowTree::getMountingCoordinator() const {
|
||||
return mountingCoordinator_;
|
||||
}
|
||||
|
||||
void ShadowTree::commit(
|
||||
ShadowTreeCommitTransaction transaction,
|
||||
bool enableStateReconciliation) const {
|
||||
SystraceSection s("ShadowTree::commit");
|
||||
|
||||
int attempts = 0;
|
||||
|
||||
while (true) {
|
||||
attempts++;
|
||||
if (tryCommit(transaction, enableStateReconciliation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// After multiple attempts, we failed to commit the transaction.
|
||||
// Something internally went terribly wrong.
|
||||
assert(attempts < 1024);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShadowTree::tryCommit(
|
||||
ShadowTreeCommitTransaction transaction,
|
||||
bool enableStateReconciliation) const {
|
||||
SystraceSection s("ShadowTree::tryCommit");
|
||||
|
||||
auto telemetry = MountingTelemetry{};
|
||||
telemetry.willCommit();
|
||||
|
||||
RootShadowNode::Shared oldRootShadowNode;
|
||||
|
||||
{
|
||||
// Reading `rootShadowNode_` in shared manner.
|
||||
std::shared_lock<better::shared_mutex> lock(commitMutex_);
|
||||
oldRootShadowNode = rootShadowNode_;
|
||||
}
|
||||
|
||||
RootShadowNode::Unshared newRootShadowNode = transaction(oldRootShadowNode);
|
||||
|
||||
if (!newRootShadowNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare state revisions of old and new root
|
||||
// Children of the root node may be mutated in-place
|
||||
if (enableStateReconciliation) {
|
||||
UnsharedShadowNode reconciledNode =
|
||||
reconcileStateWithTree(newRootShadowNode.get(), oldRootShadowNode);
|
||||
if (reconciledNode != nullptr) {
|
||||
newRootShadowNode = std::make_shared<RootShadowNode>(
|
||||
*reconciledNode, ShadowNodeFragment{});
|
||||
}
|
||||
}
|
||||
|
||||
// Layout nodes
|
||||
std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
|
||||
affectedLayoutableNodes.reserve(1024);
|
||||
|
||||
telemetry.willLayout();
|
||||
newRootShadowNode->layoutIfNeeded(&affectedLayoutableNodes);
|
||||
telemetry.didLayout();
|
||||
|
||||
// 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.
|
||||
std::unique_lock<better::shared_mutex> lock(commitMutex_);
|
||||
|
||||
if (rootShadowNode_ != oldRootShadowNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rootShadowNode_ = newRootShadowNode;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> dispatchLock(EventEmitter::DispatchMutex());
|
||||
|
||||
updateMountedFlag(
|
||||
oldRootShadowNode->getChildren(), newRootShadowNode->getChildren());
|
||||
}
|
||||
|
||||
revisionNumber_++;
|
||||
revisionNumber = revisionNumber_;
|
||||
}
|
||||
|
||||
emitLayoutEvents(affectedLayoutableNodes);
|
||||
|
||||
telemetry.didCommit();
|
||||
|
||||
mountingCoordinator_->push(
|
||||
ShadowTreeRevision{newRootShadowNode, revisionNumber, telemetry});
|
||||
|
||||
delegate_.shadowTreeDidFinishTransaction(*this, mountingCoordinator_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShadowTree::commitEmptyTree() const {
|
||||
commit(
|
||||
[](RootShadowNode::Shared const &oldRootShadowNode)
|
||||
-> RootShadowNode::Unshared {
|
||||
return std::make_shared<RootShadowNode>(
|
||||
*oldRootShadowNode,
|
||||
ShadowNodeFragment{
|
||||
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
|
||||
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void ShadowTree::emitLayoutEvents(
|
||||
std::vector<LayoutableShadowNode const *> &affectedLayoutableNodes) const {
|
||||
SystraceSection s("ShadowTree::emitLayoutEvents");
|
||||
|
||||
for (auto const *layoutableNode : affectedLayoutableNodes) {
|
||||
// Only instances of `ViewShadowNode` (and subclasses) are supported.
|
||||
auto const &viewShadowNode =
|
||||
static_cast<ViewShadowNode const &>(*layoutableNode);
|
||||
auto const &viewEventEmitter = static_cast<ViewEventEmitter const &>(
|
||||
*viewShadowNode.getEventEmitter());
|
||||
|
||||
// Checking if the `onLayout` event was requested for the particular Shadow
|
||||
// Node.
|
||||
auto const &viewProps =
|
||||
static_cast<ViewProps const &>(*viewShadowNode.getProps());
|
||||
if (!viewProps.onLayout) {
|
||||
continue;
|
||||
}
|
||||
|
||||
viewEventEmitter.onLayout(layoutableNode->getLayoutMetrics());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
94
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTree.h
generated
vendored
Normal file
94
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTree.h
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <better/mutex.h>
|
||||
#include <memory>
|
||||
|
||||
#include <react/components/root/RootComponentDescriptor.h>
|
||||
#include <react/components/root/RootShadowNode.h>
|
||||
#include <react/core/LayoutConstraints.h>
|
||||
#include <react/core/ReactPrimitives.h>
|
||||
#include <react/core/ShadowNode.h>
|
||||
#include <react/mounting/MountingCoordinator.h>
|
||||
#include <react/mounting/ShadowTreeDelegate.h>
|
||||
#include <react/mounting/ShadowTreeRevision.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using ShadowTreeCommitTransaction = std::function<RootShadowNode::Unshared(
|
||||
RootShadowNode::Shared const &oldRootShadowNode)>;
|
||||
|
||||
/*
|
||||
* Represents the shadow tree and its lifecycle.
|
||||
*/
|
||||
class ShadowTree final {
|
||||
public:
|
||||
/*
|
||||
* Creates a new shadow tree instance.
|
||||
*/
|
||||
ShadowTree(
|
||||
SurfaceId surfaceId,
|
||||
LayoutConstraints const &layoutConstraints,
|
||||
LayoutContext const &layoutContext,
|
||||
RootComponentDescriptor const &rootComponentDescriptor,
|
||||
ShadowTreeDelegate const &delegate);
|
||||
|
||||
~ShadowTree();
|
||||
|
||||
/*
|
||||
* Returns the `SurfaceId` associated with the shadow tree.
|
||||
*/
|
||||
SurfaceId getSurfaceId() const;
|
||||
|
||||
/*
|
||||
* Performs commit calling `transaction` function with a `oldRootShadowNode`
|
||||
* and expecting a `newRootShadowNode` as a return value.
|
||||
* The `transaction` function can abort commit returning `nullptr`.
|
||||
* Returns `true` if the operation finished successfully.
|
||||
*/
|
||||
bool tryCommit(
|
||||
ShadowTreeCommitTransaction transaction,
|
||||
bool enableStateReconciliation = false) const;
|
||||
|
||||
/*
|
||||
* Calls `tryCommit` in a loop until it finishes successfully.
|
||||
*/
|
||||
void commit(
|
||||
ShadowTreeCommitTransaction transaction,
|
||||
bool enableStateReconciliation = false) const;
|
||||
|
||||
/*
|
||||
* Commit an empty tree (a new `RootShadowNode` with no children).
|
||||
*/
|
||||
void commitEmptyTree() const;
|
||||
|
||||
MountingCoordinator::Shared getMountingCoordinator() const;
|
||||
|
||||
private:
|
||||
RootShadowNode::Unshared cloneRootShadowNode(
|
||||
RootShadowNode::Shared const &oldRootShadowNode,
|
||||
LayoutConstraints const &layoutConstraints,
|
||||
LayoutContext const &layoutContext) const;
|
||||
|
||||
void emitLayoutEvents(
|
||||
std::vector<LayoutableShadowNode const *> &affectedLayoutableNodes) const;
|
||||
|
||||
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_`.
|
||||
MountingCoordinator::Shared mountingCoordinator_;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
33
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTreeDelegate.h
generated
vendored
Normal file
33
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTreeDelegate.h
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <react/mounting/MountingCoordinator.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class ShadowTree;
|
||||
|
||||
/*
|
||||
* Abstract class for ShadowTree's delegate.
|
||||
*/
|
||||
class ShadowTreeDelegate {
|
||||
public:
|
||||
/*
|
||||
* Called right after Shadow Tree commit a new state of the tree.
|
||||
*/
|
||||
virtual void shadowTreeDidFinishTransaction(
|
||||
ShadowTree const &shadowTree,
|
||||
MountingCoordinator::Shared const &mountingCoordinator) const = 0;
|
||||
|
||||
virtual ~ShadowTreeDelegate() noexcept = default;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
64
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTreeRegistry.cpp
generated
vendored
Normal file
64
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTreeRegistry.cpp
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "ShadowTreeRegistry.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
ShadowTreeRegistry::~ShadowTreeRegistry() {
|
||||
assert(
|
||||
registry_.size() == 0 &&
|
||||
"Deallocation of non-empty `ShadowTreeRegistry`.");
|
||||
}
|
||||
|
||||
void ShadowTreeRegistry::add(std::unique_ptr<ShadowTree> &&shadowTree) const {
|
||||
std::unique_lock<better::shared_mutex> lock(mutex_);
|
||||
|
||||
registry_.emplace(shadowTree->getSurfaceId(), std::move(shadowTree));
|
||||
}
|
||||
|
||||
std::unique_ptr<ShadowTree> ShadowTreeRegistry::remove(
|
||||
SurfaceId surfaceId) const {
|
||||
std::unique_lock<better::shared_mutex> lock(mutex_);
|
||||
|
||||
auto iterator = registry_.find(surfaceId);
|
||||
auto shadowTree = std::unique_ptr<ShadowTree>(iterator->second.release());
|
||||
registry_.erase(iterator);
|
||||
return shadowTree;
|
||||
}
|
||||
|
||||
bool ShadowTreeRegistry::visit(
|
||||
SurfaceId surfaceId,
|
||||
std::function<void(const ShadowTree &shadowTree)> callback) const {
|
||||
std::shared_lock<better::shared_mutex> lock(mutex_);
|
||||
|
||||
auto iterator = registry_.find(surfaceId);
|
||||
|
||||
if (iterator == registry_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
callback(*iterator->second);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShadowTreeRegistry::enumerate(
|
||||
std::function<void(const ShadowTree &shadowTree, bool &stop)> callback)
|
||||
const {
|
||||
std::shared_lock<better::shared_mutex> lock(mutex_);
|
||||
bool stop = false;
|
||||
for (auto const &pair : registry_) {
|
||||
callback(*pair.second, stop);
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
69
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTreeRegistry.h
generated
vendored
Normal file
69
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTreeRegistry.h
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <better/map.h>
|
||||
#include <better/mutex.h>
|
||||
|
||||
#include <react/core/ReactPrimitives.h>
|
||||
#include <react/mounting/ShadowTree.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* Owning registry of `ShadowTree`s.
|
||||
*/
|
||||
class ShadowTreeRegistry final {
|
||||
public:
|
||||
ShadowTreeRegistry() = default;
|
||||
~ShadowTreeRegistry();
|
||||
|
||||
/*
|
||||
* Adds a `ShadowTree` instance to the registry.
|
||||
* The ownership of the instance is also transferred to the registry.
|
||||
* Can be called from any thread.
|
||||
*/
|
||||
void add(std::unique_ptr<ShadowTree> &&shadowTree) const;
|
||||
|
||||
/*
|
||||
* Removes a `ShadowTree` instance with given `surfaceId` from the registry
|
||||
* and returns it as a result.
|
||||
* The ownership of the instance is also transferred to the caller.
|
||||
* Can be called from any thread.
|
||||
*/
|
||||
std::unique_ptr<ShadowTree> remove(SurfaceId surfaceId) const;
|
||||
|
||||
/*
|
||||
* Finds a `ShadowTree` instance with a given `surfaceId` in the registry and
|
||||
* synchronously calls the `callback` with a reference to the instance while
|
||||
* the mutex is being acquired.
|
||||
* Returns `true` if the registry has `ShadowTree` instance with corresponding
|
||||
* `surfaceId`, otherwise returns `false` without calling the `callback`.
|
||||
* Can be called from any thread.
|
||||
*/
|
||||
bool visit(
|
||||
SurfaceId surfaceId,
|
||||
std::function<void(const ShadowTree &shadowTree)> callback) const;
|
||||
|
||||
/*
|
||||
* Enumerates all stored shadow trees.
|
||||
* Set `stop` to `true` to interrupt the enumeration.
|
||||
* Can be called from any thread.
|
||||
*/
|
||||
void enumerate(std::function<void(const ShadowTree &shadowTree, bool &stop)>
|
||||
callback) const;
|
||||
|
||||
private:
|
||||
mutable better::shared_mutex mutex_;
|
||||
mutable better::map<SurfaceId, std::unique_ptr<ShadowTree>>
|
||||
registry_; // Protected by `mutex_`.
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
34
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTreeRevision.cpp
generated
vendored
Normal file
34
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTreeRevision.cpp
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "ShadowTreeRevision.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using Number = ShadowTreeRevision::Number;
|
||||
|
||||
ShadowTreeRevision::ShadowTreeRevision(
|
||||
ShadowNode::Shared const &rootShadowNode,
|
||||
Number number,
|
||||
MountingTelemetry telemetry)
|
||||
: rootShadowNode_(rootShadowNode), number_(number), telemetry_(telemetry) {}
|
||||
|
||||
MountingTelemetry const &ShadowTreeRevision::getTelemetry() const {
|
||||
return telemetry_;
|
||||
}
|
||||
|
||||
ShadowNode const &ShadowTreeRevision::getRootShadowNode() {
|
||||
return *rootShadowNode_;
|
||||
}
|
||||
|
||||
Number ShadowTreeRevision::getNumber() const {
|
||||
return number_;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
62
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTreeRevision.h
generated
vendored
Normal file
62
node_modules/react-native/ReactCommon/fabric/mounting/ShadowTreeRevision.h
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <better/optional.h>
|
||||
|
||||
#include <react/mounting/MountingTelemetry.h>
|
||||
#include <react/mounting/MountingTransaction.h>
|
||||
#include <react/mounting/ShadowViewMutation.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* Represent a particular committed state of a shadow tree. The object contains
|
||||
* a pointer to a root shadow node, a sequential number of commit and telemetry.
|
||||
*/
|
||||
class ShadowTreeRevision final {
|
||||
public:
|
||||
/*
|
||||
* Sequential number of the commit that created this revision of a shadow
|
||||
* tree.
|
||||
*/
|
||||
using Number = int64_t;
|
||||
|
||||
/*
|
||||
* Creates the object with given root shadow node, revision number and
|
||||
* telemetry.
|
||||
*/
|
||||
ShadowTreeRevision(
|
||||
ShadowNode::Shared const &rootShadowNode,
|
||||
Number number,
|
||||
MountingTelemetry telemetry);
|
||||
|
||||
/*
|
||||
* Returns telemetry associated with this revision.
|
||||
*/
|
||||
MountingTelemetry const &getTelemetry() const;
|
||||
|
||||
private:
|
||||
friend class MountingCoordinator;
|
||||
|
||||
/*
|
||||
* Methods from this section are meant to be used by `MountingCoordinator`
|
||||
* only.
|
||||
*/
|
||||
ShadowNode const &getRootShadowNode();
|
||||
Number getNumber() const;
|
||||
|
||||
private:
|
||||
ShadowNode::Shared rootShadowNode_;
|
||||
Number number_;
|
||||
MountingTelemetry telemetry_;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
81
node_modules/react-native/ReactCommon/fabric/mounting/ShadowView.cpp
generated
vendored
Normal file
81
node_modules/react-native/ReactCommon/fabric/mounting/ShadowView.cpp
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "ShadowView.h"
|
||||
|
||||
#include <react/core/LayoutableShadowNode.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
static LayoutMetrics layoutMetricsFromShadowNode(ShadowNode const &shadowNode) {
|
||||
auto layotableShadowNode =
|
||||
traitCast<LayoutableShadowNode const *>(&shadowNode);
|
||||
return layotableShadowNode ? layotableShadowNode->getLayoutMetrics()
|
||||
: EmptyLayoutMetrics;
|
||||
}
|
||||
|
||||
ShadowView::ShadowView(const ShadowNode &shadowNode)
|
||||
: componentName(shadowNode.getComponentName()),
|
||||
componentHandle(shadowNode.getComponentHandle()),
|
||||
tag(shadowNode.getTag()),
|
||||
props(shadowNode.getProps()),
|
||||
eventEmitter(shadowNode.getEventEmitter()),
|
||||
layoutMetrics(layoutMetricsFromShadowNode(shadowNode)),
|
||||
state(shadowNode.getState()) {}
|
||||
|
||||
bool ShadowView::operator==(const ShadowView &rhs) const {
|
||||
return std::tie(
|
||||
this->tag,
|
||||
this->componentName,
|
||||
this->props,
|
||||
this->eventEmitter,
|
||||
this->layoutMetrics,
|
||||
this->state) ==
|
||||
std::tie(
|
||||
rhs.tag,
|
||||
rhs.componentName,
|
||||
rhs.props,
|
||||
rhs.eventEmitter,
|
||||
rhs.layoutMetrics,
|
||||
rhs.state);
|
||||
}
|
||||
|
||||
bool ShadowView::operator!=(const ShadowView &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
#if RN_DEBUG_STRING_CONVERTIBLE
|
||||
|
||||
std::string getDebugName(ShadowView const &object) {
|
||||
return object.componentHandle == 0 ? "Invalid" : object.componentName;
|
||||
}
|
||||
|
||||
std::vector<DebugStringConvertibleObject> getDebugProps(
|
||||
ShadowView const &object,
|
||||
DebugStringConvertibleOptions options) {
|
||||
return {
|
||||
{"tag", getDebugDescription(object.tag, options)},
|
||||
{"props", getDebugDescription(object.props, options)},
|
||||
{"eventEmitter", getDebugDescription(object.eventEmitter, options)},
|
||||
{"layoutMetrics", getDebugDescription(object.layoutMetrics, options)},
|
||||
{"state", getDebugDescription(object.state, options)},
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool ShadowViewNodePair::operator==(const ShadowViewNodePair &rhs) const {
|
||||
return this->shadowNode == rhs.shadowNode;
|
||||
}
|
||||
|
||||
bool ShadowViewNodePair::operator!=(const ShadowViewNodePair &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
93
node_modules/react-native/ReactCommon/fabric/mounting/ShadowView.h
generated
vendored
Normal file
93
node_modules/react-native/ReactCommon/fabric/mounting/ShadowView.h
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <better/small_vector.h>
|
||||
#include <folly/Hash.h>
|
||||
#include <react/core/EventEmitter.h>
|
||||
#include <react/core/LayoutMetrics.h>
|
||||
#include <react/core/Props.h>
|
||||
#include <react/core/ReactPrimitives.h>
|
||||
#include <react/core/ShadowNode.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* Describes a view that can be mounted.
|
||||
*/
|
||||
struct ShadowView final {
|
||||
ShadowView() = default;
|
||||
ShadowView(ShadowView const &shadowView) = default;
|
||||
ShadowView(ShadowView &&shadowView) noexcept = default;
|
||||
|
||||
/*
|
||||
* Constructs a `ShadowView` from given `ShadowNode`.
|
||||
*/
|
||||
explicit ShadowView(ShadowNode const &shadowNode);
|
||||
|
||||
ShadowView &operator=(ShadowView const &other) = default;
|
||||
ShadowView &operator=(ShadowView &&other) = default;
|
||||
|
||||
bool operator==(ShadowView const &rhs) const;
|
||||
bool operator!=(ShadowView const &rhs) const;
|
||||
|
||||
ComponentName componentName{};
|
||||
ComponentHandle componentHandle{};
|
||||
Tag tag{};
|
||||
Props::Shared props{};
|
||||
EventEmitter::Shared eventEmitter{};
|
||||
LayoutMetrics layoutMetrics{EmptyLayoutMetrics};
|
||||
State::Shared state{};
|
||||
};
|
||||
|
||||
#if RN_DEBUG_STRING_CONVERTIBLE
|
||||
|
||||
std::string getDebugName(ShadowView const &object);
|
||||
std::vector<DebugStringConvertibleObject> getDebugProps(
|
||||
ShadowView const &object,
|
||||
DebugStringConvertibleOptions options);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Describes pair of a `ShadowView` and a `ShadowNode`.
|
||||
*/
|
||||
struct ShadowViewNodePair final {
|
||||
using List = better::
|
||||
small_vector<ShadowViewNodePair, kShadowNodeChildrenSmallVectorSize>;
|
||||
|
||||
ShadowView shadowView;
|
||||
ShadowNode const *shadowNode;
|
||||
|
||||
/*
|
||||
* The stored pointer to `ShadowNode` represents an indentity of the pair.
|
||||
*/
|
||||
bool operator==(const ShadowViewNodePair &rhs) const;
|
||||
bool operator!=(const ShadowViewNodePair &rhs) const;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<facebook::react::ShadowView> {
|
||||
size_t operator()(const facebook::react::ShadowView &shadowView) const {
|
||||
return folly::hash::hash_combine(
|
||||
0,
|
||||
shadowView.componentHandle,
|
||||
shadowView.tag,
|
||||
shadowView.props,
|
||||
shadowView.eventEmitter,
|
||||
shadowView.state);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
123
node_modules/react-native/ReactCommon/fabric/mounting/ShadowViewMutation.cpp
generated
vendored
Normal file
123
node_modules/react-native/ReactCommon/fabric/mounting/ShadowViewMutation.cpp
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "ShadowViewMutation.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
ShadowViewMutation ShadowViewMutation::CreateMutation(ShadowView shadowView) {
|
||||
return {
|
||||
/* .type = */ Create,
|
||||
/* .parentShadowView = */ {},
|
||||
/* .oldChildShadowView = */ {},
|
||||
/* .newChildShadowView = */ shadowView,
|
||||
/* .index = */ -1,
|
||||
};
|
||||
}
|
||||
|
||||
ShadowViewMutation ShadowViewMutation::DeleteMutation(ShadowView shadowView) {
|
||||
return {
|
||||
/* .type = */ Delete,
|
||||
/* .parentShadowView = */ {},
|
||||
/* .oldChildShadowView = */ shadowView,
|
||||
/* .newChildShadowView = */ {},
|
||||
/* .index = */ -1,
|
||||
};
|
||||
}
|
||||
|
||||
ShadowViewMutation ShadowViewMutation::InsertMutation(
|
||||
ShadowView parentShadowView,
|
||||
ShadowView childShadowView,
|
||||
int index) {
|
||||
return {
|
||||
/* .type = */ Insert,
|
||||
/* .parentShadowView = */ parentShadowView,
|
||||
/* .oldChildShadowView = */ {},
|
||||
/* .newChildShadowView = */ childShadowView,
|
||||
/* .index = */ index,
|
||||
};
|
||||
}
|
||||
|
||||
ShadowViewMutation ShadowViewMutation::RemoveMutation(
|
||||
ShadowView parentShadowView,
|
||||
ShadowView childShadowView,
|
||||
int index) {
|
||||
return {
|
||||
/* .type = */ Remove,
|
||||
/* .parentShadowView = */ parentShadowView,
|
||||
/* .oldChildShadowView = */ childShadowView,
|
||||
/* .newChildShadowView = */ {},
|
||||
/* .index = */ index,
|
||||
};
|
||||
}
|
||||
|
||||
ShadowViewMutation ShadowViewMutation::UpdateMutation(
|
||||
ShadowView parentShadowView,
|
||||
ShadowView oldChildShadowView,
|
||||
ShadowView newChildShadowView,
|
||||
int index) {
|
||||
return {
|
||||
/* .type = */ Update,
|
||||
/* .parentShadowView = */ parentShadowView,
|
||||
/* .oldChildShadowView = */ oldChildShadowView,
|
||||
/* .newChildShadowView = */ newChildShadowView,
|
||||
/* .index = */ index,
|
||||
};
|
||||
}
|
||||
|
||||
#if RN_DEBUG_STRING_CONVERTIBLE
|
||||
|
||||
std::string getDebugName(ShadowViewMutation const &mutation) {
|
||||
switch (mutation.type) {
|
||||
case ShadowViewMutation::Create:
|
||||
return "Create";
|
||||
case ShadowViewMutation::Delete:
|
||||
return "Delete";
|
||||
case ShadowViewMutation::Insert:
|
||||
return "Insert";
|
||||
case ShadowViewMutation::Remove:
|
||||
return "Remove";
|
||||
case ShadowViewMutation::Update:
|
||||
return "Update";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DebugStringConvertibleObject> getDebugProps(
|
||||
ShadowViewMutation const &mutation,
|
||||
DebugStringConvertibleOptions options) {
|
||||
return {
|
||||
mutation.oldChildShadowView.componentHandle
|
||||
? DebugStringConvertibleObject{"oldChild",
|
||||
getDebugDescription(
|
||||
mutation.oldChildShadowView,
|
||||
options)}
|
||||
: DebugStringConvertibleObject{},
|
||||
mutation.newChildShadowView.componentHandle
|
||||
? DebugStringConvertibleObject{"newChild",
|
||||
getDebugDescription(
|
||||
mutation.newChildShadowView,
|
||||
options)}
|
||||
: DebugStringConvertibleObject{},
|
||||
mutation.parentShadowView.componentHandle
|
||||
? DebugStringConvertibleObject{"parent",
|
||||
getDebugDescription(
|
||||
mutation.parentShadowView,
|
||||
options)}
|
||||
: DebugStringConvertibleObject{},
|
||||
mutation.index != -1
|
||||
? DebugStringConvertibleObject{"index",
|
||||
getDebugDescription(
|
||||
mutation.index, options)}
|
||||
: DebugStringConvertibleObject{},
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
88
node_modules/react-native/ReactCommon/fabric/mounting/ShadowViewMutation.h
generated
vendored
Normal file
88
node_modules/react-native/ReactCommon/fabric/mounting/ShadowViewMutation.h
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <react/mounting/ShadowView.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* Describes a single native view tree mutation which may contain
|
||||
* pointers to an old shadow view, a new shadow view, a parent shadow view and
|
||||
* final index of inserted or updated view.
|
||||
* Use static methods to instantiate mutations of different types.
|
||||
*/
|
||||
struct ShadowViewMutation final {
|
||||
using List = std::vector<ShadowViewMutation>;
|
||||
|
||||
#pragma mark - Designated Initializers
|
||||
|
||||
/*
|
||||
* Creates and returns an `Create` mutation.
|
||||
*/
|
||||
static ShadowViewMutation CreateMutation(ShadowView shadowView);
|
||||
|
||||
/*
|
||||
* Creates and returns an `Delete` mutation.
|
||||
*/
|
||||
static ShadowViewMutation DeleteMutation(ShadowView shadowView);
|
||||
|
||||
/*
|
||||
* Creates and returns an `Insert` mutation.
|
||||
*/
|
||||
static ShadowViewMutation InsertMutation(
|
||||
ShadowView parentShadowView,
|
||||
ShadowView childShadowView,
|
||||
int index);
|
||||
|
||||
/*
|
||||
* Creates and returns a `Remove` mutation.
|
||||
*/
|
||||
static ShadowViewMutation RemoveMutation(
|
||||
ShadowView parentShadowView,
|
||||
ShadowView childShadowView,
|
||||
int index);
|
||||
|
||||
/*
|
||||
* Creates and returns an `Update` mutation.
|
||||
*/
|
||||
static ShadowViewMutation UpdateMutation(
|
||||
ShadowView parentShadowView,
|
||||
ShadowView oldChildShadowView,
|
||||
ShadowView newChildShadowView,
|
||||
int index);
|
||||
|
||||
#pragma mark - Type
|
||||
|
||||
enum Type { Create, Delete, Insert, Remove, Update };
|
||||
|
||||
#pragma mark - Fields
|
||||
|
||||
Type type = {Create};
|
||||
ShadowView parentShadowView = {};
|
||||
ShadowView oldChildShadowView = {};
|
||||
ShadowView newChildShadowView = {};
|
||||
int index = {};
|
||||
};
|
||||
|
||||
using ShadowViewMutationList = std::vector<ShadowViewMutation>;
|
||||
|
||||
#if RN_DEBUG_STRING_CONVERTIBLE
|
||||
|
||||
std::string getDebugName(ShadowViewMutation const &object);
|
||||
std::vector<DebugStringConvertibleObject> getDebugProps(
|
||||
ShadowViewMutation const &object,
|
||||
DebugStringConvertibleOptions options);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
94
node_modules/react-native/ReactCommon/fabric/mounting/TreeStateReconciliation.cpp
generated
vendored
Normal file
94
node_modules/react-native/ReactCommon/fabric/mounting/TreeStateReconciliation.cpp
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "TreeStateReconciliation.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using ChangedShadowNodePairs =
|
||||
std::vector<std::pair<ShadowNode::Shared, ShadowNode::Unshared>>;
|
||||
|
||||
/**
|
||||
* Clones any children in the subtree that need to be cloned, and adds those to
|
||||
* the `changedPairs` vector argument.
|
||||
*/
|
||||
static ChangedShadowNodePairs reconcileStateWithChildren(
|
||||
SharedShadowNodeList const &newChildren,
|
||||
SharedShadowNodeList const &oldChildren) {
|
||||
ChangedShadowNodePairs changedPairs;
|
||||
// Find children that are the same family in both trees.
|
||||
// We only want to find nodes that existing in the new tree - if they
|
||||
// don't exist in the new tree, they're being deleted; if they don't exist
|
||||
// in the old tree, they're new. We don't need to deal with either of those
|
||||
// cases here.
|
||||
// Currently we use a naive double loop - this could be improved, but we need
|
||||
// to be able to handle cases where nodes are entirely reordered, for
|
||||
// instance.
|
||||
for (auto const &child : newChildren) {
|
||||
auto const oldChild = std::find_if(
|
||||
oldChildren.begin(), oldChildren.end(), [&](const auto &el) {
|
||||
return ShadowNode::sameFamily(*child, *el);
|
||||
});
|
||||
|
||||
if (oldChild != oldChildren.end()) {
|
||||
UnsharedShadowNode newChild =
|
||||
reconcileStateWithTree(child.get(), *oldChild);
|
||||
if (newChild != nullptr) {
|
||||
changedPairs.push_back(std::make_pair(child, newChild));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return changedPairs;
|
||||
}
|
||||
|
||||
UnsharedShadowNode reconcileStateWithTree(
|
||||
ShadowNode const *newNode,
|
||||
SharedShadowNode committedNode) {
|
||||
// If the revisions on the node are the same, we can finish here.
|
||||
// Subtrees are guaranteed to be identical at this point, too.
|
||||
if (committedNode->getStateRevision() <= newNode->getStateRevision()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If we got this fair, we're guaranteed that the state of 1) this node,
|
||||
// and/or 2) some descendant node is out-of-date and must be reconciled.
|
||||
// This requires traversing all children, and we must at *least* clone
|
||||
// this node, whether or not we clone and update any children.
|
||||
auto const &newChildren = newNode->getChildren();
|
||||
auto const &oldChildren = committedNode->getChildren();
|
||||
auto const changedPairs =
|
||||
reconcileStateWithChildren(newChildren, oldChildren);
|
||||
|
||||
ShadowNode::SharedListOfShared clonedChildren =
|
||||
ShadowNodeFragment::childrenPlaceholder();
|
||||
|
||||
// If any children were cloned, we need to recreate the child list.
|
||||
// This won't cause any children to be cloned that weren't already cloned -
|
||||
// it just collects all children, cloned or uncloned, into a new list.
|
||||
if (!changedPairs.empty()) {
|
||||
ShadowNode::UnsharedListOfShared newList =
|
||||
std::make_shared<ShadowNode::ListOfShared>();
|
||||
for (std::size_t i = 0, j = 0; i < newChildren.size(); ++i) {
|
||||
if (j < changedPairs.size() && changedPairs[j].first == newChildren[i]) {
|
||||
newList->push_back(changedPairs[j].second);
|
||||
++j;
|
||||
} else {
|
||||
newList->push_back(newChildren[i]);
|
||||
}
|
||||
}
|
||||
clonedChildren = newList;
|
||||
}
|
||||
|
||||
return newNode->clone({/* .props = */ ShadowNodeFragment::propsPlaceholder(),
|
||||
/* .children = */ clonedChildren,
|
||||
/* .state = */ newNode->getMostRecentState()});
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
33
node_modules/react-native/ReactCommon/fabric/mounting/TreeStateReconciliation.h
generated
vendored
Normal file
33
node_modules/react-native/ReactCommon/fabric/mounting/TreeStateReconciliation.h
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <react/core/ShadowNode.h>
|
||||
#include <react/core/ShadowNodeFragment.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/**
|
||||
* Problem Description: because of C++ State, the React Native C++ ShadowTree
|
||||
* can diverge from the ReactJS ShadowTree; ReactJS communicates all tree
|
||||
* changes to C++, but C++ state commits are not propagated to ReactJS (ReactJS
|
||||
* may or may not clone nodes with state changes, but it has no way of knowing
|
||||
* if it /should/ clone those nodes; so those clones may never happen). This
|
||||
* causes a number of problems. This function resolves the problem by taking a
|
||||
* candidate tree being committed, and sees if any State changes need to be
|
||||
* applied to it. If any changes need to be made, a new ShadowNode is returned;
|
||||
* otherwise, nullptr is returned if the node is already consistent with the
|
||||
* latest tree, including all state changes.
|
||||
*
|
||||
* This should be called during the commit phase, pre-layout and pre-diff.
|
||||
*/
|
||||
UnsharedShadowNode reconcileStateWithTree(
|
||||
ShadowNode const *newNode,
|
||||
SharedShadowNode committedNode);
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
65
node_modules/react-native/ReactCommon/fabric/mounting/stubs/StubView.cpp
generated
vendored
Normal file
65
node_modules/react-native/ReactCommon/fabric/mounting/stubs/StubView.cpp
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "StubView.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void StubView::update(ShadowView const &shadowView) {
|
||||
componentName = shadowView.componentName;
|
||||
componentHandle = shadowView.componentHandle;
|
||||
tag = shadowView.tag;
|
||||
props = shadowView.props;
|
||||
eventEmitter = shadowView.eventEmitter;
|
||||
layoutMetrics = shadowView.layoutMetrics;
|
||||
state = shadowView.state;
|
||||
}
|
||||
|
||||
bool operator==(StubView const &lhs, StubView const &rhs) {
|
||||
return std::tie(lhs.props, lhs.layoutMetrics) ==
|
||||
std::tie(rhs.props, rhs.layoutMetrics);
|
||||
}
|
||||
|
||||
bool operator!=(StubView const &lhs, StubView const &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
#if RN_DEBUG_STRING_CONVERTIBLE
|
||||
|
||||
std::string getDebugName(StubView const &stubView) {
|
||||
return std::string{"Stub"} +
|
||||
std::string{stubView.componentHandle ? stubView.componentName
|
||||
: "[invalid]"};
|
||||
}
|
||||
|
||||
std::vector<DebugStringConvertibleObject> getDebugProps(
|
||||
StubView const &stubView,
|
||||
DebugStringConvertibleOptions options) {
|
||||
return {
|
||||
{"tag", getDebugDescription(stubView.tag, options)},
|
||||
{"props", getDebugDescription(stubView.props, options)},
|
||||
{"eventEmitter", getDebugDescription(stubView.eventEmitter, options)},
|
||||
{"layoutMetrics", getDebugDescription(stubView.layoutMetrics, options)},
|
||||
{"state", getDebugDescription(stubView.state, options)},
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<StubView> getDebugChildren(
|
||||
StubView const &stubView,
|
||||
DebugStringConvertibleOptions options) {
|
||||
std::vector<StubView> result;
|
||||
for (auto const &child : stubView.children) {
|
||||
result.push_back(*child);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
57
node_modules/react-native/ReactCommon/fabric/mounting/stubs/StubView.h
generated
vendored
Normal file
57
node_modules/react-native/ReactCommon/fabric/mounting/stubs/StubView.h
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <react/core/LayoutMetrics.h>
|
||||
#include <react/core/State.h>
|
||||
#include <react/debug/debugStringConvertibleUtils.h>
|
||||
#include <react/mounting/ShadowView.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class StubView final {
|
||||
public:
|
||||
using Shared = std::shared_ptr<StubView>;
|
||||
|
||||
StubView() = default;
|
||||
StubView(StubView const &stubView) = default;
|
||||
|
||||
void update(ShadowView const &shadowView);
|
||||
|
||||
ComponentName componentName;
|
||||
ComponentHandle componentHandle;
|
||||
Tag tag;
|
||||
SharedProps props;
|
||||
SharedEventEmitter eventEmitter;
|
||||
LayoutMetrics layoutMetrics;
|
||||
State::Shared state;
|
||||
std::vector<StubView::Shared> children;
|
||||
};
|
||||
|
||||
bool operator==(StubView const &lhs, StubView const &rhs);
|
||||
bool operator!=(StubView const &lhs, StubView const &rhs);
|
||||
|
||||
#if RN_DEBUG_STRING_CONVERTIBLE
|
||||
|
||||
std::string getDebugName(StubView const &stubView);
|
||||
|
||||
std::vector<DebugStringConvertibleObject> getDebugProps(
|
||||
StubView const &stubView,
|
||||
DebugStringConvertibleOptions options);
|
||||
std::vector<StubView> getDebugChildren(
|
||||
StubView const &stubView,
|
||||
DebugStringConvertibleOptions options);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
111
node_modules/react-native/ReactCommon/fabric/mounting/stubs/StubViewTree.cpp
generated
vendored
Normal file
111
node_modules/react-native/ReactCommon/fabric/mounting/stubs/StubViewTree.cpp
generated
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "StubViewTree.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
StubViewTree::StubViewTree(ShadowView const &shadowView) {
|
||||
auto view = std::make_shared<StubView>();
|
||||
view->update(shadowView);
|
||||
rootTag = shadowView.tag;
|
||||
registry[shadowView.tag] = view;
|
||||
}
|
||||
|
||||
StubView const &StubViewTree::getRootStubView() const {
|
||||
return *registry.at(rootTag);
|
||||
}
|
||||
|
||||
void StubViewTree::mutate(ShadowViewMutationList const &mutations) {
|
||||
for (auto const &mutation : mutations) {
|
||||
switch (mutation.type) {
|
||||
case ShadowViewMutation::Create: {
|
||||
assert(mutation.parentShadowView == ShadowView{});
|
||||
assert(mutation.oldChildShadowView == ShadowView{});
|
||||
auto stubView = std::make_shared<StubView>();
|
||||
auto tag = mutation.newChildShadowView.tag;
|
||||
assert(registry.find(tag) == registry.end());
|
||||
registry[tag] = stubView;
|
||||
break;
|
||||
}
|
||||
|
||||
case ShadowViewMutation::Delete: {
|
||||
assert(mutation.parentShadowView == ShadowView{});
|
||||
assert(mutation.newChildShadowView == ShadowView{});
|
||||
auto tag = mutation.oldChildShadowView.tag;
|
||||
assert(registry.find(tag) != registry.end());
|
||||
registry.erase(tag);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShadowViewMutation::Insert: {
|
||||
assert(mutation.oldChildShadowView == ShadowView{});
|
||||
auto parentTag = mutation.parentShadowView.tag;
|
||||
assert(registry.find(parentTag) != registry.end());
|
||||
auto parentStubView = registry[parentTag];
|
||||
auto childTag = mutation.newChildShadowView.tag;
|
||||
assert(registry.find(childTag) != registry.end());
|
||||
auto childStubView = registry[childTag];
|
||||
childStubView->update(mutation.newChildShadowView);
|
||||
parentStubView->children.insert(
|
||||
parentStubView->children.begin() + mutation.index, childStubView);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShadowViewMutation::Remove: {
|
||||
assert(mutation.newChildShadowView == ShadowView{});
|
||||
auto parentTag = mutation.parentShadowView.tag;
|
||||
assert(registry.find(parentTag) != registry.end());
|
||||
auto parentStubView = registry[parentTag];
|
||||
auto childTag = mutation.oldChildShadowView.tag;
|
||||
assert(registry.find(childTag) != registry.end());
|
||||
auto childStubView = registry[childTag];
|
||||
assert(
|
||||
parentStubView->children[mutation.index]->tag ==
|
||||
childStubView->tag);
|
||||
parentStubView->children.erase(
|
||||
parentStubView->children.begin() + mutation.index);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShadowViewMutation::Update: {
|
||||
assert(
|
||||
mutation.newChildShadowView.tag == mutation.oldChildShadowView.tag);
|
||||
assert(
|
||||
registry.find(mutation.newChildShadowView.tag) != registry.end());
|
||||
auto stubView = registry[mutation.newChildShadowView.tag];
|
||||
stubView->update(mutation.newChildShadowView);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(StubViewTree const &lhs, StubViewTree const &rhs) {
|
||||
if (lhs.registry.size() != rhs.registry.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto const &pair : lhs.registry) {
|
||||
auto &lhsStubView = *lhs.registry.at(pair.first);
|
||||
auto &rhsStubView = *rhs.registry.at(pair.first);
|
||||
|
||||
if (lhsStubView != rhsStubView) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(StubViewTree const &lhs, StubViewTree const &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
36
node_modules/react-native/ReactCommon/fabric/mounting/stubs/StubViewTree.h
generated
vendored
Normal file
36
node_modules/react-native/ReactCommon/fabric/mounting/stubs/StubViewTree.h
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <react/mounting/ShadowViewMutation.h>
|
||||
#include <react/mounting/StubView.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class StubViewTree {
|
||||
public:
|
||||
StubViewTree() = default;
|
||||
StubViewTree(ShadowView const &shadowView);
|
||||
|
||||
void mutate(ShadowViewMutationList const &mutations);
|
||||
|
||||
StubView const &getRootStubView() const;
|
||||
|
||||
Tag rootTag;
|
||||
std::unordered_map<Tag, StubView::Shared> registry{};
|
||||
};
|
||||
|
||||
bool operator==(StubViewTree const &lhs, StubViewTree const &rhs);
|
||||
bool operator!=(StubViewTree const &lhs, StubViewTree const &rhs);
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
62
node_modules/react-native/ReactCommon/fabric/mounting/stubs/stubs.cpp
generated
vendored
Normal file
62
node_modules/react-native/ReactCommon/fabric/mounting/stubs/stubs.cpp
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "stubs.h"
|
||||
|
||||
#include <react/core/LayoutableShadowNode.h>
|
||||
#include <react/core/ShadowNodeFragment.h>
|
||||
#include <react/mounting/Differentiator.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* Generates `create` and `insert` instructions recursively traversing a shadow
|
||||
* tree.
|
||||
* This is a trivial implementation of diffing algorithm that can only "diff"
|
||||
* an empty tree with some other one.
|
||||
*/
|
||||
static void calculateShadowViewMutationsForNewTree(
|
||||
ShadowViewMutation::List &mutations,
|
||||
ShadowView const &parentShadowView,
|
||||
ShadowViewNodePair::List const &newChildPairs) {
|
||||
for (auto index = 0; index < newChildPairs.size(); index++) {
|
||||
auto const &newChildPair = newChildPairs[index];
|
||||
|
||||
mutations.push_back(
|
||||
ShadowViewMutation::CreateMutation(newChildPair.shadowView));
|
||||
mutations.push_back(ShadowViewMutation::InsertMutation(
|
||||
parentShadowView, newChildPair.shadowView, index));
|
||||
|
||||
auto const newGrandChildPairs =
|
||||
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
|
||||
|
||||
calculateShadowViewMutationsForNewTree(
|
||||
mutations, newChildPair.shadowView, newGrandChildPairs);
|
||||
}
|
||||
}
|
||||
|
||||
StubViewTree stubViewTreeFromShadowNode(ShadowNode const &rootShadowNode) {
|
||||
auto mutations = ShadowViewMutation::List{};
|
||||
mutations.reserve(256);
|
||||
|
||||
calculateShadowViewMutationsForNewTree(
|
||||
mutations,
|
||||
ShadowView(rootShadowNode),
|
||||
sliceChildShadowNodeViewPairs(rootShadowNode));
|
||||
|
||||
auto emptyRootShadowNode = rootShadowNode.clone(
|
||||
ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
|
||||
ShadowNode::emptySharedShadowNodeSharedList()});
|
||||
|
||||
auto stubViewTree = StubViewTree(ShadowView(*emptyRootShadowNode));
|
||||
stubViewTree.mutate(mutations);
|
||||
return stubViewTree;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
20
node_modules/react-native/ReactCommon/fabric/mounting/stubs/stubs.h
generated
vendored
Normal file
20
node_modules/react-native/ReactCommon/fabric/mounting/stubs/stubs.h
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <react/core/ShadowNode.h>
|
||||
#include "StubView.h"
|
||||
#include "StubViewTree.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
StubViewTree stubViewTreeFromShadowNode(ShadowNode const &rootShadowNode);
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
131
node_modules/react-native/ReactCommon/fabric/mounting/tests/Entropy.h
generated
vendored
Normal file
131
node_modules/react-native/ReactCommon/fabric/mounting/tests/Entropy.h
generated
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* The source of pseudo-random numbers and some problem-oriented tools built on
|
||||
* top of that. We need this class to maintain a reproducible stream of random
|
||||
* numbers and abstract away complex math of and C++ STL API behind that.
|
||||
*/
|
||||
class Entropy final {
|
||||
public:
|
||||
using Generator = std::mt19937;
|
||||
|
||||
/*
|
||||
* Creates an instance seeded with a real, not pseudo-random, number.
|
||||
*/
|
||||
Entropy() {
|
||||
std::random_device device;
|
||||
seed_ = device();
|
||||
generator_ = std::mt19937(seed_);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates an instance seeded with a given number.
|
||||
*/
|
||||
Entropy(uint_fast32_t seed) {
|
||||
seed_ = seed;
|
||||
generator_ = std::mt19937(seed_);
|
||||
}
|
||||
|
||||
uint_fast32_t getSeed() const {
|
||||
return seed_;
|
||||
}
|
||||
|
||||
/*
|
||||
* Family of methods that return uniformly distributed instances of a type
|
||||
* within a specified range.
|
||||
*/
|
||||
template <typename T>
|
||||
bool random() const {
|
||||
T result;
|
||||
generateRandomValue(generator_, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename Arg1>
|
||||
T random(Arg1 arg1) const {
|
||||
T result;
|
||||
generateRandomValue(generator_, result, arg1);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename Arg1, typename Arg2>
|
||||
T random(Arg1 arg1, Arg2 arg2) const {
|
||||
T result;
|
||||
generateRandomValue(generator_, result, arg1, arg2);
|
||||
return result;
|
||||
}
|
||||
|
||||
void generateRandomValue(
|
||||
Generator &generator,
|
||||
bool &result,
|
||||
double ratio = 0.5) const {
|
||||
result = generator() % 10000 < 10000 * ratio;
|
||||
}
|
||||
|
||||
void generateRandomValue(Generator &generator, int &result, int min, int max)
|
||||
const {
|
||||
std::uniform_int_distribution<int> distribution(min, max);
|
||||
result = distribution(generator);
|
||||
}
|
||||
|
||||
/*
|
||||
* Shuffles `std::vector` in place.
|
||||
*/
|
||||
template <typename T>
|
||||
void shuffle(T array) const {
|
||||
std::shuffle(array.begin(), array.end(), generator_);
|
||||
}
|
||||
|
||||
/*
|
||||
* Distribute items from a given array into buckets using a normal
|
||||
* distribution and given `deviation`.
|
||||
*/
|
||||
template <typename T>
|
||||
std::vector<std::vector<T>> distribute(std::vector<T> items, double deviation)
|
||||
const {
|
||||
std::normal_distribution<> distribution{0, deviation};
|
||||
|
||||
auto deviationLimit = int(deviation * 10);
|
||||
auto spreadResult = std::vector<std::vector<T>>(deviationLimit * 2);
|
||||
std::fill(spreadResult.begin(), spreadResult.end(), std::vector<T>{});
|
||||
|
||||
for (auto const &item : items) {
|
||||
auto position = int(distribution(generator_) + deviationLimit);
|
||||
position = std::max(0, std::min(position, deviationLimit * 2));
|
||||
|
||||
if (position < spreadResult.size()) {
|
||||
spreadResult[position].push_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
auto result = std::vector<std::vector<T>>{};
|
||||
for (auto const &chunk : spreadResult) {
|
||||
if (chunk.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
result.push_back(chunk);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::mt19937 generator_;
|
||||
uint_fast32_t seed_;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
103
node_modules/react-native/ReactCommon/fabric/mounting/tests/MountingTelemetryTest.cpp
generated
vendored
Normal file
103
node_modules/react-native/ReactCommon/fabric/mounting/tests/MountingTelemetryTest.cpp
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <react/mounting/MountingTelemetry.h>
|
||||
#include <react/utils/Telemetry.h>
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
#define EXPECT_EQ_WITH_THRESHOLD(a, b, threshold) \
|
||||
EXPECT_TRUE((a >= b - threshold) && (a <= b + threshold))
|
||||
|
||||
TEST(MountingTelemetryTest, timepoints) {
|
||||
auto threshold = int64_t{100};
|
||||
|
||||
auto timepointA = telemetryTimePointNow();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
auto timepointB = telemetryTimePointNow();
|
||||
|
||||
auto duration = telemetryDurationToMilliseconds(timepointB - timepointA);
|
||||
|
||||
EXPECT_EQ_WITH_THRESHOLD(duration, 100, threshold);
|
||||
}
|
||||
|
||||
TEST(MountingTelemetryTest, normalUseCase) {
|
||||
auto threshold = int64_t{100};
|
||||
auto telemetry = MountingTelemetry{};
|
||||
|
||||
telemetry.willCommit();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
telemetry.willLayout();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
telemetry.didLayout();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
telemetry.didCommit();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
|
||||
telemetry.willMount();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
telemetry.didMount();
|
||||
|
||||
auto commitDuration = telemetryDurationToMilliseconds(
|
||||
telemetry.getCommitEndTime() - telemetry.getCommitStartTime());
|
||||
auto layoutDuration = telemetryDurationToMilliseconds(
|
||||
telemetry.getLayoutEndTime() - telemetry.getLayoutStartTime());
|
||||
auto mountDuration = telemetryDurationToMilliseconds(
|
||||
telemetry.getMountEndTime() - telemetry.getMountStartTime());
|
||||
|
||||
EXPECT_EQ_WITH_THRESHOLD(commitDuration, 400, threshold * 2);
|
||||
EXPECT_EQ_WITH_THRESHOLD(layoutDuration, 200, threshold);
|
||||
EXPECT_EQ_WITH_THRESHOLD(mountDuration, 100, threshold);
|
||||
}
|
||||
|
||||
TEST(MountingTelemetryTest, abnormalUseCases) {
|
||||
// Calling `did` before `will` should crash.
|
||||
EXPECT_DEATH_IF_SUPPORTED(
|
||||
{
|
||||
auto telemetry = MountingTelemetry{};
|
||||
telemetry.didDiff();
|
||||
},
|
||||
"diffStartTime_");
|
||||
|
||||
EXPECT_DEATH_IF_SUPPORTED(
|
||||
{
|
||||
auto telemetry = MountingTelemetry{};
|
||||
telemetry.didCommit();
|
||||
},
|
||||
"commitStartTime_");
|
||||
|
||||
EXPECT_DEATH_IF_SUPPORTED(
|
||||
{
|
||||
auto telemetry = MountingTelemetry{};
|
||||
telemetry.didMount();
|
||||
},
|
||||
"mountStartTime_");
|
||||
|
||||
// Getting `start` *or* `end` timepoints before a pair of `will` and `did`
|
||||
// should crash.
|
||||
EXPECT_DEATH_IF_SUPPORTED(
|
||||
{
|
||||
auto telemetry = MountingTelemetry{};
|
||||
telemetry.willCommit();
|
||||
telemetry.getCommitStartTime();
|
||||
},
|
||||
"commitEndTime_");
|
||||
|
||||
EXPECT_DEATH_IF_SUPPORTED(
|
||||
{
|
||||
auto telemetry = MountingTelemetry{};
|
||||
telemetry.willCommit();
|
||||
telemetry.getCommitEndTime();
|
||||
},
|
||||
"commitEndTime_");
|
||||
}
|
325
node_modules/react-native/ReactCommon/fabric/mounting/tests/MountingTest.cpp
generated
vendored
Normal file
325
node_modules/react-native/ReactCommon/fabric/mounting/tests/MountingTest.cpp
generated
vendored
Normal file
@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <react/components/root/RootComponentDescriptor.h>
|
||||
#include <react/components/view/ViewComponentDescriptor.h>
|
||||
#include <react/mounting/Differentiator.h>
|
||||
#include <react/mounting/stubs.h>
|
||||
|
||||
#include "shadowTreeGeneration.h"
|
||||
|
||||
#include <Glog/logging.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
static ShadowNode::Shared makeNode(
|
||||
ComponentDescriptor const &componentDescriptor,
|
||||
int tag,
|
||||
ShadowNode::ListOfShared children) {
|
||||
auto props = generateDefaultProps(componentDescriptor);
|
||||
|
||||
// Make sure node is layoutable by giving it dimensions and making it
|
||||
// accessible This is an implementation detail and subject to change.
|
||||
folly::dynamic dynamic = folly::dynamic::object();
|
||||
dynamic["position"] = "absolute";
|
||||
dynamic["top"] = 0;
|
||||
dynamic["left"] = 0;
|
||||
dynamic["width"] = 100;
|
||||
dynamic["height"] = 100;
|
||||
dynamic["nativeId"] = tag;
|
||||
dynamic["accessible"] = true;
|
||||
|
||||
auto newProps = componentDescriptor.cloneProps(props, RawProps(dynamic));
|
||||
|
||||
return componentDescriptor.createShadowNode(
|
||||
ShadowNodeFragment{newProps,
|
||||
std::make_shared<SharedShadowNodeList>(children)},
|
||||
componentDescriptor.createFamily({tag, SurfaceId(1), nullptr}, nullptr));
|
||||
}
|
||||
|
||||
TEST(MountingTest, testMinimalInstructionGeneration) {
|
||||
auto eventDispatcher = EventDispatcher::Shared{};
|
||||
auto contextContainer = std::make_shared<ContextContainer>();
|
||||
auto componentDescriptorParameters =
|
||||
ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr};
|
||||
auto viewComponentDescriptor =
|
||||
ViewComponentDescriptor(componentDescriptorParameters);
|
||||
auto rootComponentDescriptor =
|
||||
RootComponentDescriptor(componentDescriptorParameters);
|
||||
|
||||
auto rootFamily = rootComponentDescriptor.createFamily(
|
||||
{Tag(1), SurfaceId(1), nullptr}, nullptr);
|
||||
|
||||
// Creating an initial root shadow node.
|
||||
auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
|
||||
std::static_pointer_cast<RootShadowNode const>(
|
||||
rootComponentDescriptor.createShadowNode(
|
||||
ShadowNodeFragment{RootShadowNode::defaultSharedProps()},
|
||||
rootFamily)));
|
||||
|
||||
// Applying size constraints.
|
||||
emptyRootNode = emptyRootNode->clone(
|
||||
LayoutConstraints{Size{512, 0},
|
||||
Size{512, std::numeric_limits<Float>::infinity()}},
|
||||
LayoutContext{});
|
||||
|
||||
auto childA = makeNode(viewComponentDescriptor, 100, {});
|
||||
auto childB = makeNode(viewComponentDescriptor, 101, {});
|
||||
auto childC = makeNode(viewComponentDescriptor, 102, {});
|
||||
auto childD = makeNode(viewComponentDescriptor, 103, {});
|
||||
auto childE = makeNode(viewComponentDescriptor, 104, {});
|
||||
auto childF = makeNode(viewComponentDescriptor, 105, {});
|
||||
|
||||
auto family = viewComponentDescriptor.createFamily(
|
||||
{10, SurfaceId(1), nullptr}, nullptr);
|
||||
|
||||
// Construct "identical" shadow nodes: they differ only in children.
|
||||
auto shadowNodeV1 = viewComponentDescriptor.createShadowNode(
|
||||
ShadowNodeFragment{generateDefaultProps(viewComponentDescriptor),
|
||||
std::make_shared<SharedShadowNodeList>(
|
||||
SharedShadowNodeList{childB, childC, childD})},
|
||||
family);
|
||||
auto shadowNodeV2 = shadowNodeV1->clone(ShadowNodeFragment{
|
||||
generateDefaultProps(viewComponentDescriptor),
|
||||
std::make_shared<SharedShadowNodeList>(
|
||||
SharedShadowNodeList{childA, childB, childC, childD})});
|
||||
auto shadowNodeV3 = shadowNodeV2->clone(
|
||||
ShadowNodeFragment{generateDefaultProps(viewComponentDescriptor),
|
||||
std::make_shared<SharedShadowNodeList>(
|
||||
SharedShadowNodeList{childB, childC, childD})});
|
||||
auto shadowNodeV4 = shadowNodeV3->clone(
|
||||
ShadowNodeFragment{generateDefaultProps(viewComponentDescriptor),
|
||||
std::make_shared<SharedShadowNodeList>(
|
||||
SharedShadowNodeList{childB, childD, childE})});
|
||||
auto shadowNodeV5 = shadowNodeV4->clone(ShadowNodeFragment{
|
||||
generateDefaultProps(viewComponentDescriptor),
|
||||
std::make_shared<SharedShadowNodeList>(
|
||||
SharedShadowNodeList{childB, childA, childE, childC})});
|
||||
auto shadowNodeV6 = shadowNodeV5->clone(ShadowNodeFragment{
|
||||
generateDefaultProps(viewComponentDescriptor),
|
||||
std::make_shared<SharedShadowNodeList>(SharedShadowNodeList{
|
||||
childB, childA, childD, childF, childE, childC})});
|
||||
|
||||
// Injecting a tree into the root node.
|
||||
auto rootNodeV1 = std::static_pointer_cast<RootShadowNode const>(
|
||||
emptyRootNode->ShadowNode::clone(
|
||||
ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
|
||||
std::make_shared<SharedShadowNodeList>(
|
||||
SharedShadowNodeList{shadowNodeV1})}));
|
||||
auto rootNodeV2 = std::static_pointer_cast<RootShadowNode const>(
|
||||
rootNodeV1->ShadowNode::clone(
|
||||
ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
|
||||
std::make_shared<SharedShadowNodeList>(
|
||||
SharedShadowNodeList{shadowNodeV2})}));
|
||||
auto rootNodeV3 = std::static_pointer_cast<RootShadowNode const>(
|
||||
rootNodeV2->ShadowNode::clone(
|
||||
ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
|
||||
std::make_shared<SharedShadowNodeList>(
|
||||
SharedShadowNodeList{shadowNodeV3})}));
|
||||
auto rootNodeV4 = std::static_pointer_cast<RootShadowNode const>(
|
||||
rootNodeV3->ShadowNode::clone(
|
||||
ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
|
||||
std::make_shared<SharedShadowNodeList>(
|
||||
SharedShadowNodeList{shadowNodeV4})}));
|
||||
auto rootNodeV5 = std::static_pointer_cast<RootShadowNode const>(
|
||||
rootNodeV4->ShadowNode::clone(
|
||||
ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
|
||||
std::make_shared<SharedShadowNodeList>(
|
||||
SharedShadowNodeList{shadowNodeV5})}));
|
||||
auto rootNodeV6 = std::static_pointer_cast<RootShadowNode const>(
|
||||
rootNodeV5->ShadowNode::clone(
|
||||
ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
|
||||
std::make_shared<SharedShadowNodeList>(
|
||||
SharedShadowNodeList{shadowNodeV6})}));
|
||||
|
||||
// Layout and diff
|
||||
std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV1{};
|
||||
affectedLayoutableNodesV1.reserve(1024);
|
||||
std::const_pointer_cast<RootShadowNode>(rootNodeV1)
|
||||
->layoutIfNeeded(&affectedLayoutableNodesV1);
|
||||
rootNodeV1->sealRecursive();
|
||||
|
||||
std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV2{};
|
||||
affectedLayoutableNodesV2.reserve(1024);
|
||||
std::const_pointer_cast<RootShadowNode>(rootNodeV2)
|
||||
->layoutIfNeeded(&affectedLayoutableNodesV2);
|
||||
rootNodeV2->sealRecursive();
|
||||
|
||||
std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV3{};
|
||||
affectedLayoutableNodesV3.reserve(1024);
|
||||
std::const_pointer_cast<RootShadowNode>(rootNodeV3)
|
||||
->layoutIfNeeded(&affectedLayoutableNodesV3);
|
||||
rootNodeV3->sealRecursive();
|
||||
|
||||
std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV4{};
|
||||
affectedLayoutableNodesV4.reserve(1024);
|
||||
std::const_pointer_cast<RootShadowNode>(rootNodeV4)
|
||||
->layoutIfNeeded(&affectedLayoutableNodesV4);
|
||||
rootNodeV4->sealRecursive();
|
||||
|
||||
std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV5{};
|
||||
affectedLayoutableNodesV5.reserve(1024);
|
||||
std::const_pointer_cast<RootShadowNode>(rootNodeV5)
|
||||
->layoutIfNeeded(&affectedLayoutableNodesV5);
|
||||
rootNodeV5->sealRecursive();
|
||||
|
||||
std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV6{};
|
||||
affectedLayoutableNodesV6.reserve(1024);
|
||||
std::const_pointer_cast<RootShadowNode>(rootNodeV6)
|
||||
->layoutIfNeeded(&affectedLayoutableNodesV6);
|
||||
rootNodeV6->sealRecursive();
|
||||
|
||||
// This block displays all the mutations for debugging purposes.
|
||||
/*
|
||||
LOG(ERROR) << "Num mutations: " << mutations.size();
|
||||
for (auto const &mutation : mutations) {
|
||||
switch (mutation.type) {
|
||||
case ShadowViewMutation::Create: {
|
||||
LOG(ERROR) << "CREATE " << mutation.newChildShadowView.tag;
|
||||
break;
|
||||
}
|
||||
case ShadowViewMutation::Delete: {
|
||||
LOG(ERROR) << "DELETE " << mutation.oldChildShadowView.tag;
|
||||
break;
|
||||
}
|
||||
case ShadowViewMutation::Remove: {
|
||||
LOG(ERROR) << "REMOVE " << mutation.oldChildShadowView.tag << " " <<
|
||||
mutation.index; break;
|
||||
}
|
||||
case ShadowViewMutation::Insert: {
|
||||
LOG(ERROR) << "INSERT " << mutation.newChildShadowView.tag << " " <<
|
||||
mutation.index; break;
|
||||
}
|
||||
case ShadowViewMutation::Update: {
|
||||
LOG(ERROR) << "UPDATE " << mutation.newChildShadowView.tag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// Calculating mutations.
|
||||
auto mutations1 = calculateShadowViewMutations(
|
||||
DifferentiatorMode::OptimizedMoves, *rootNodeV1, *rootNodeV2);
|
||||
|
||||
// The order and exact mutation instructions here may change at any time.
|
||||
// This test just ensures that any changes are intentional.
|
||||
// This test, in particular, ensures that inserting a node at the beginning
|
||||
// produces a single "Insert" instruction, and no remove/insert (move)
|
||||
// operations. All these nodes are laid out with absolute positioning, so
|
||||
// moving them around does not change layout.
|
||||
assert(mutations1.size() == 2);
|
||||
assert(mutations1[0].type == ShadowViewMutation::Create);
|
||||
assert(mutations1[0].newChildShadowView.tag == 100);
|
||||
assert(mutations1[1].type == ShadowViewMutation::Insert);
|
||||
assert(mutations1[1].newChildShadowView.tag == 100);
|
||||
assert(mutations1[1].index == 0);
|
||||
|
||||
// Calculating mutations.
|
||||
auto mutations2 = calculateShadowViewMutations(
|
||||
DifferentiatorMode::OptimizedMoves, *rootNodeV2, *rootNodeV3);
|
||||
|
||||
// The order and exact mutation instructions here may change at any time.
|
||||
// This test just ensures that any changes are intentional.
|
||||
// This test, in particular, ensures that removing a node at the beginning
|
||||
// produces a single remove (and delete) instruction, and no remove/insert
|
||||
// (move) operations. All these nodes are laid out with absolute positioning,
|
||||
// so moving them around does not change layout.
|
||||
assert(mutations2.size() == 2);
|
||||
assert(mutations2[0].type == ShadowViewMutation::Remove);
|
||||
assert(mutations2[0].oldChildShadowView.tag == 100);
|
||||
assert(mutations2[0].index == 0);
|
||||
assert(mutations2[1].type == ShadowViewMutation::Delete);
|
||||
assert(mutations2[1].oldChildShadowView.tag == 100);
|
||||
|
||||
// Calculating mutations.
|
||||
auto mutations3 = calculateShadowViewMutations(
|
||||
DifferentiatorMode::OptimizedMoves, *rootNodeV3, *rootNodeV4);
|
||||
|
||||
// The order and exact mutation instructions here may change at any time.
|
||||
// This test just ensures that any changes are intentional.
|
||||
// This test, in particular, ensures that removing a node in the middle
|
||||
// produces a single remove (and delete) instruction, and no remove/insert
|
||||
// (move) operations; and that simultaneously, we can insert a node at the
|
||||
// end. NOTE: This list of mutations has some unexpected "Update"
|
||||
// instructions, due to layout issues (some LayoutMetrics are 0). Not sure
|
||||
// why, but the point of this test is to make sure there aren't unnecessary
|
||||
// insert/deletes, so we can ignore for now.
|
||||
assert(mutations3.size() == 7);
|
||||
assert(mutations3[0].type == ShadowViewMutation::Update);
|
||||
assert(mutations3[1].type == ShadowViewMutation::Update);
|
||||
assert(mutations3[2].type == ShadowViewMutation::Update);
|
||||
assert(mutations3[3].type == ShadowViewMutation::Remove);
|
||||
assert(mutations3[3].oldChildShadowView.tag == 102);
|
||||
assert(mutations3[3].index == 1);
|
||||
assert(mutations3[4].type == ShadowViewMutation::Delete);
|
||||
assert(mutations3[4].oldChildShadowView.tag == 102);
|
||||
assert(mutations3[5].type == ShadowViewMutation::Create);
|
||||
assert(mutations3[5].newChildShadowView.tag == 104);
|
||||
assert(mutations3[6].type == ShadowViewMutation::Insert);
|
||||
assert(mutations3[6].newChildShadowView.tag == 104);
|
||||
assert(mutations3[6].index == 2);
|
||||
|
||||
// Calculating mutations.
|
||||
auto mutations4 = calculateShadowViewMutations(
|
||||
DifferentiatorMode::OptimizedMoves, *rootNodeV4, *rootNodeV5);
|
||||
|
||||
// The order and exact mutation instructions here may change at any time.
|
||||
// This test just ensures that any changes are intentional.
|
||||
// This test, in particular, ensures that inserting a child at the middle, and
|
||||
// at the end, and removing a node in the middle, produces the minimal set of
|
||||
// instructions. All these nodes are laid out with absolute positioning, so
|
||||
// moving them around does not change layout. NOTE: This list of mutations has
|
||||
// some unexpected "Update" instructions, due to layout issues (some
|
||||
// LayoutMetrics are 0). Not sure why, but the point of this test is to make
|
||||
// sure there aren't unnecessary insert/deletes, so we can ignore for now.
|
||||
assert(mutations4.size() == 9);
|
||||
assert(mutations4[0].type == ShadowViewMutation::Update);
|
||||
assert(mutations4[1].type == ShadowViewMutation::Update);
|
||||
assert(mutations4[2].type == ShadowViewMutation::Update);
|
||||
assert(mutations4[3].type == ShadowViewMutation::Remove);
|
||||
assert(mutations4[3].oldChildShadowView.tag == 103);
|
||||
assert(mutations4[3].index == 1);
|
||||
assert(mutations4[4].type == ShadowViewMutation::Delete);
|
||||
assert(mutations4[4].oldChildShadowView.tag == 103);
|
||||
assert(mutations4[5].type == ShadowViewMutation::Create);
|
||||
assert(mutations4[5].newChildShadowView.tag == 100);
|
||||
assert(mutations4[6].type == ShadowViewMutation::Create);
|
||||
assert(mutations4[6].newChildShadowView.tag == 102);
|
||||
assert(mutations4[7].type == ShadowViewMutation::Insert);
|
||||
assert(mutations4[7].newChildShadowView.tag == 100);
|
||||
assert(mutations4[7].index == 1);
|
||||
assert(mutations4[8].type == ShadowViewMutation::Insert);
|
||||
assert(mutations4[8].newChildShadowView.tag == 102);
|
||||
assert(mutations4[8].index == 3);
|
||||
|
||||
auto mutations5 = calculateShadowViewMutations(
|
||||
DifferentiatorMode::OptimizedMoves, *rootNodeV5, *rootNodeV6);
|
||||
|
||||
// The order and exact mutation instructions here may change at any time.
|
||||
// This test just ensures that any changes are intentional.
|
||||
// This test, in particular, ensures that inserting TWO children in the middle
|
||||
// produces the minimal set of instructions. All these nodes are laid out with
|
||||
// absolute positioning, so moving them around does not change layout.
|
||||
assert(mutations5.size() == 4);
|
||||
assert(mutations5[0].type == ShadowViewMutation::Create);
|
||||
assert(mutations5[0].newChildShadowView.tag == 103);
|
||||
assert(mutations5[1].type == ShadowViewMutation::Create);
|
||||
assert(mutations5[1].newChildShadowView.tag == 105);
|
||||
assert(mutations5[2].type == ShadowViewMutation::Insert);
|
||||
assert(mutations5[2].newChildShadowView.tag == 103);
|
||||
assert(mutations5[2].index == 2);
|
||||
assert(mutations5[3].type == ShadowViewMutation::Insert);
|
||||
assert(mutations5[3].newChildShadowView.tag == 105);
|
||||
assert(mutations5[3].index == 3);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
181
node_modules/react-native/ReactCommon/fabric/mounting/tests/ShadowTreeLifeCycleTest.cpp
generated
vendored
Normal file
181
node_modules/react-native/ReactCommon/fabric/mounting/tests/ShadowTreeLifeCycleTest.cpp
generated
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <react/components/root/RootComponentDescriptor.h>
|
||||
#include <react/components/view/ViewComponentDescriptor.h>
|
||||
#include <react/mounting/Differentiator.h>
|
||||
#include <react/mounting/stubs.h>
|
||||
|
||||
#include "Entropy.h"
|
||||
#include "shadowTreeGeneration.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
static void testShadowNodeTreeLifeCycle(
|
||||
DifferentiatorMode differentiatorMode,
|
||||
uint_fast32_t seed,
|
||||
int treeSize,
|
||||
int repeats,
|
||||
int stages) {
|
||||
auto entropy = seed == 0 ? Entropy() : Entropy(seed);
|
||||
|
||||
auto eventDispatcher = EventDispatcher::Shared{};
|
||||
auto contextContainer = std::make_shared<ContextContainer>();
|
||||
auto componentDescriptorParameters =
|
||||
ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr};
|
||||
auto viewComponentDescriptor =
|
||||
ViewComponentDescriptor(componentDescriptorParameters);
|
||||
auto rootComponentDescriptor =
|
||||
RootComponentDescriptor(componentDescriptorParameters);
|
||||
auto noopEventEmitter =
|
||||
std::make_shared<ViewEventEmitter const>(nullptr, -1, eventDispatcher);
|
||||
|
||||
auto allNodes = std::vector<ShadowNode::Shared>{};
|
||||
|
||||
for (int i = 0; i < repeats; i++) {
|
||||
allNodes.clear();
|
||||
|
||||
auto family = rootComponentDescriptor.createFamily(
|
||||
{Tag(1), SurfaceId(1), nullptr}, nullptr);
|
||||
|
||||
// Creating an initial root shadow node.
|
||||
auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
|
||||
std::static_pointer_cast<RootShadowNode const>(
|
||||
rootComponentDescriptor.createShadowNode(
|
||||
ShadowNodeFragment{RootShadowNode::defaultSharedProps()},
|
||||
family)));
|
||||
|
||||
// Applying size constraints.
|
||||
emptyRootNode = emptyRootNode->clone(
|
||||
LayoutConstraints{Size{512, 0},
|
||||
Size{512, std::numeric_limits<Float>::infinity()}},
|
||||
LayoutContext{});
|
||||
|
||||
// Generation of a random tree.
|
||||
auto singleRootChildNode =
|
||||
generateShadowNodeTree(entropy, viewComponentDescriptor, treeSize);
|
||||
|
||||
// Injecting a tree into the root node.
|
||||
auto currentRootNode = std::static_pointer_cast<RootShadowNode const>(
|
||||
emptyRootNode->ShadowNode::clone(ShadowNodeFragment{
|
||||
ShadowNodeFragment::propsPlaceholder(),
|
||||
std::make_shared<SharedShadowNodeList>(
|
||||
SharedShadowNodeList{singleRootChildNode})}));
|
||||
|
||||
// Building an initial view hierarchy.
|
||||
auto viewTree = stubViewTreeFromShadowNode(*emptyRootNode);
|
||||
viewTree.mutate(calculateShadowViewMutations(
|
||||
differentiatorMode, *emptyRootNode, *currentRootNode));
|
||||
|
||||
for (int j = 0; j < stages; j++) {
|
||||
auto nextRootNode = currentRootNode;
|
||||
|
||||
// Mutating the tree.
|
||||
alterShadowTree(
|
||||
entropy,
|
||||
nextRootNode,
|
||||
{
|
||||
&messWithChildren,
|
||||
&messWithYogaStyles,
|
||||
&messWithLayotableOnlyFlag,
|
||||
});
|
||||
|
||||
std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
|
||||
affectedLayoutableNodes.reserve(1024);
|
||||
|
||||
// Laying out the tree.
|
||||
std::const_pointer_cast<RootShadowNode>(nextRootNode)
|
||||
->layoutIfNeeded(&affectedLayoutableNodes);
|
||||
|
||||
nextRootNode->sealRecursive();
|
||||
allNodes.push_back(nextRootNode);
|
||||
|
||||
// Calculating mutations.
|
||||
auto mutations = calculateShadowViewMutations(
|
||||
differentiatorMode, *currentRootNode, *nextRootNode);
|
||||
|
||||
// Mutating the view tree.
|
||||
viewTree.mutate(mutations);
|
||||
|
||||
// Building a view tree to compare with.
|
||||
auto rebuiltViewTree = stubViewTreeFromShadowNode(*nextRootNode);
|
||||
|
||||
// Comparing the newly built tree with the updated one.
|
||||
if (rebuiltViewTree != viewTree) {
|
||||
// Something went wrong.
|
||||
|
||||
LOG(ERROR) << "Entropy seed: " << entropy.getSeed() << "\n";
|
||||
|
||||
LOG(ERROR) << "Shadow Tree before: \n"
|
||||
<< currentRootNode->getDebugDescription();
|
||||
LOG(ERROR) << "Shadow Tree after: \n"
|
||||
<< nextRootNode->getDebugDescription();
|
||||
|
||||
LOG(ERROR) << "View Tree before: \n"
|
||||
<< getDebugDescription(viewTree.getRootStubView(), {});
|
||||
LOG(ERROR) << "View Tree after: \n"
|
||||
<< getDebugDescription(
|
||||
rebuiltViewTree.getRootStubView(), {});
|
||||
|
||||
LOG(ERROR) << "Mutations:"
|
||||
<< "\n"
|
||||
<< getDebugDescription(mutations, {});
|
||||
|
||||
FAIL();
|
||||
}
|
||||
|
||||
currentRootNode = nextRootNode;
|
||||
}
|
||||
}
|
||||
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
TEST(MountingTest, stableBiggerTreeFewerIterationsClassic) {
|
||||
testShadowNodeTreeLifeCycle(
|
||||
DifferentiatorMode::Classic,
|
||||
/* seed */ 1,
|
||||
/* size */ 512,
|
||||
/* repeats */ 32,
|
||||
/* stages */ 32);
|
||||
}
|
||||
|
||||
TEST(MountingTest, stableSmallerTreeMoreIterationsClassic) {
|
||||
testShadowNodeTreeLifeCycle(
|
||||
DifferentiatorMode::Classic,
|
||||
/* seed */ 1,
|
||||
/* size */ 16,
|
||||
/* repeats */ 512,
|
||||
/* stages */ 32);
|
||||
}
|
||||
|
||||
TEST(MountingTest, stableBiggerTreeFewerIterationsOptimizedMoves) {
|
||||
testShadowNodeTreeLifeCycle(
|
||||
DifferentiatorMode::OptimizedMoves,
|
||||
/* seed */ 1,
|
||||
/* size */ 512,
|
||||
/* repeats */ 32,
|
||||
/* stages */ 32);
|
||||
}
|
||||
|
||||
TEST(MountingTest, stableSmallerTreeMoreIterationsOptimizedMoves) {
|
||||
testShadowNodeTreeLifeCycle(
|
||||
DifferentiatorMode::OptimizedMoves,
|
||||
/* seed */ 1,
|
||||
/* size */ 16,
|
||||
/* repeats */ 512,
|
||||
/* stages */ 32);
|
||||
}
|
259
node_modules/react-native/ReactCommon/fabric/mounting/tests/shadowTreeGeneration.h
generated
vendored
Normal file
259
node_modules/react-native/ReactCommon/fabric/mounting/tests/shadowTreeGeneration.h
generated
vendored
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
|
||||
#include <react/mounting/Differentiator.h>
|
||||
#include <react/mounting/stubs.h>
|
||||
|
||||
#include <react/components/root/RootComponentDescriptor.h>
|
||||
#include <react/components/view/ViewComponentDescriptor.h>
|
||||
|
||||
#include "Entropy.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
static Tag generateReactTag() {
|
||||
static Tag tag = 1000;
|
||||
return tag++;
|
||||
}
|
||||
|
||||
class ShadowTreeEdge final {
|
||||
public:
|
||||
ShadowNode::Shared shadowNode{nullptr};
|
||||
ShadowNode::Shared parentShadowNode{nullptr};
|
||||
int index{0};
|
||||
};
|
||||
|
||||
static bool traverseShadowTree(
|
||||
ShadowNode::Shared const &parentShadowNode,
|
||||
std::function<void(ShadowTreeEdge const &edge, bool &stop)> const
|
||||
&callback) {
|
||||
auto index = int{0};
|
||||
for (auto const &childNode : parentShadowNode->getChildren()) {
|
||||
auto stop = bool{false};
|
||||
|
||||
callback(ShadowTreeEdge{childNode, parentShadowNode, index}, stop);
|
||||
|
||||
if (stop) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (traverseShadowTree(childNode, callback)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int countShadowNodes(ShadowNode::Shared const &rootShadowNode) {
|
||||
auto counter = int{0};
|
||||
|
||||
traverseShadowTree(
|
||||
rootShadowNode,
|
||||
[&](ShadowTreeEdge const &edge, bool &stop) { counter++; });
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
static ShadowTreeEdge findShadowNodeWithIndex(
|
||||
ShadowNode::Shared const &rootNode,
|
||||
int index) {
|
||||
auto counter = int{0};
|
||||
auto result = ShadowTreeEdge{};
|
||||
|
||||
traverseShadowTree(rootNode, [&](ShadowTreeEdge const &edge, bool &stop) {
|
||||
if (index == counter) {
|
||||
result = edge;
|
||||
}
|
||||
|
||||
counter++;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ShadowTreeEdge findRandomShadowNode(
|
||||
Entropy const &entropy,
|
||||
ShadowNode::Shared const &rootShadowNode) {
|
||||
auto count = countShadowNodes(rootShadowNode);
|
||||
return findShadowNodeWithIndex(
|
||||
rootShadowNode,
|
||||
entropy.random<int>(1 /* Excluding a root node */, count - 1));
|
||||
}
|
||||
|
||||
static ShadowNode::ListOfShared cloneSharedShadowNodeList(
|
||||
ShadowNode::ListOfShared const &list) {
|
||||
auto result = ShadowNode::ListOfShared{};
|
||||
result.reserve(list.size());
|
||||
for (auto const &shadowNode : list) {
|
||||
result.push_back(shadowNode->clone({}));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline ShadowNode::Unshared messWithChildren(
|
||||
Entropy const &entropy,
|
||||
ShadowNode const &shadowNode) {
|
||||
auto children = shadowNode.getChildren();
|
||||
children = cloneSharedShadowNodeList(children);
|
||||
entropy.shuffle(children);
|
||||
return shadowNode.clone(
|
||||
{ShadowNodeFragment::propsPlaceholder(),
|
||||
std::make_shared<ShadowNode::ListOfShared const>(children)});
|
||||
}
|
||||
|
||||
static inline ShadowNode::Unshared messWithLayotableOnlyFlag(
|
||||
Entropy const &entropy,
|
||||
ShadowNode const &shadowNode) {
|
||||
auto oldProps = shadowNode.getProps();
|
||||
auto newProps = shadowNode.getComponentDescriptor().cloneProps(
|
||||
oldProps, RawProps(folly::dynamic::object()));
|
||||
|
||||
auto &viewProps =
|
||||
const_cast<ViewProps &>(static_cast<ViewProps const &>(*newProps));
|
||||
|
||||
if (entropy.random<bool>(0.1)) {
|
||||
viewProps.nativeId = entropy.random<bool>() ? "42" : "";
|
||||
}
|
||||
|
||||
if (entropy.random<bool>(0.1)) {
|
||||
viewProps.backgroundColor =
|
||||
entropy.random<bool>() ? SharedColor() : whiteColor();
|
||||
}
|
||||
|
||||
if (entropy.random<bool>(0.1)) {
|
||||
viewProps.foregroundColor =
|
||||
entropy.random<bool>() ? SharedColor() : blackColor();
|
||||
}
|
||||
|
||||
if (entropy.random<bool>(0.1)) {
|
||||
viewProps.shadowColor =
|
||||
entropy.random<bool>() ? SharedColor() : blackColor();
|
||||
}
|
||||
|
||||
if (entropy.random<bool>(0.1)) {
|
||||
viewProps.accessible = entropy.random<bool>();
|
||||
}
|
||||
|
||||
if (entropy.random<bool>(0.1)) {
|
||||
viewProps.zIndex = entropy.random<bool>() ? 1 : 0;
|
||||
}
|
||||
|
||||
if (entropy.random<bool>(0.1)) {
|
||||
viewProps.pointerEvents = entropy.random<bool>() ? PointerEventsMode::Auto
|
||||
: PointerEventsMode::None;
|
||||
}
|
||||
|
||||
if (entropy.random<bool>(0.1)) {
|
||||
viewProps.transform = entropy.random<bool>() ? Transform::Identity()
|
||||
: Transform::Perspective(42);
|
||||
}
|
||||
|
||||
return shadowNode.clone({newProps});
|
||||
}
|
||||
|
||||
static inline ShadowNode::Unshared messWithYogaStyles(
|
||||
Entropy const &entropy,
|
||||
ShadowNode const &shadowNode) {
|
||||
folly::dynamic dynamic = folly::dynamic::object();
|
||||
|
||||
if (entropy.random<bool>()) {
|
||||
dynamic["flexDirection"] = entropy.random<bool>() ? "row" : "column";
|
||||
}
|
||||
|
||||
std::vector<std::string> properties = {
|
||||
"flex", "flexGrow", "flexShrink", "flexBasis",
|
||||
"left", "top", "marginLeft", "marginTop",
|
||||
"marginRight", "marginBottom", "paddingLeft", "paddingTop",
|
||||
"paddingRight", "paddingBottom", "width", "height",
|
||||
"maxWidth", "maxHeight", "minWidth", "minHeight",
|
||||
};
|
||||
|
||||
for (auto const &property : properties) {
|
||||
if (entropy.random<bool>(0.1)) {
|
||||
dynamic[property] = entropy.random<int>(0, 1024);
|
||||
}
|
||||
}
|
||||
|
||||
auto oldProps = shadowNode.getProps();
|
||||
auto newProps = shadowNode.getComponentDescriptor().cloneProps(
|
||||
oldProps, RawProps(dynamic));
|
||||
return shadowNode.clone({newProps});
|
||||
}
|
||||
|
||||
using ShadowNodeAlteration = std::function<
|
||||
ShadowNode::Unshared(Entropy const &entropy, ShadowNode const &shadowNode)>;
|
||||
|
||||
static inline void alterShadowTree(
|
||||
Entropy const &entropy,
|
||||
RootShadowNode::Shared &rootShadowNode,
|
||||
ShadowNodeAlteration alteration) {
|
||||
auto edge = findRandomShadowNode(entropy, rootShadowNode);
|
||||
|
||||
rootShadowNode =
|
||||
std::static_pointer_cast<RootShadowNode>(rootShadowNode->cloneTree(
|
||||
edge.shadowNode->getFamily(), [&](ShadowNode const &oldShadowNode) {
|
||||
return alteration(entropy, oldShadowNode);
|
||||
}));
|
||||
}
|
||||
|
||||
static inline void alterShadowTree(
|
||||
Entropy const &entropy,
|
||||
RootShadowNode::Shared &rootShadowNode,
|
||||
std::vector<ShadowNodeAlteration> alterations) {
|
||||
auto i = entropy.random<int>(0, alterations.size() - 1);
|
||||
alterShadowTree(entropy, rootShadowNode, alterations[i]);
|
||||
}
|
||||
|
||||
static SharedViewProps generateDefaultProps(
|
||||
ComponentDescriptor const &componentDescriptor) {
|
||||
return std::static_pointer_cast<ViewProps const>(
|
||||
componentDescriptor.cloneProps(nullptr, RawProps{}));
|
||||
}
|
||||
|
||||
static inline ShadowNode::Shared generateShadowNodeTree(
|
||||
Entropy const &entropy,
|
||||
ComponentDescriptor const &componentDescriptor,
|
||||
int size,
|
||||
int deviation = 3) {
|
||||
if (size <= 1) {
|
||||
auto family = componentDescriptor.createFamily(
|
||||
{generateReactTag(), SurfaceId(1), nullptr}, nullptr);
|
||||
return componentDescriptor.createShadowNode(
|
||||
ShadowNodeFragment{generateDefaultProps(componentDescriptor)}, family);
|
||||
}
|
||||
|
||||
auto items = std::vector<int>(size);
|
||||
std::fill(items.begin(), items.end(), 1);
|
||||
auto chunks = entropy.distribute(items, deviation);
|
||||
auto children = ShadowNode::ListOfShared{};
|
||||
|
||||
for (auto const &chunk : chunks) {
|
||||
children.push_back(
|
||||
generateShadowNodeTree(entropy, componentDescriptor, chunk.size()));
|
||||
}
|
||||
|
||||
auto family = componentDescriptor.createFamily(
|
||||
{generateReactTag(), SurfaceId(1), nullptr}, nullptr);
|
||||
return componentDescriptor.createShadowNode(
|
||||
ShadowNodeFragment{generateDefaultProps(componentDescriptor),
|
||||
std::make_shared<SharedShadowNodeList>(children)},
|
||||
family);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
Reference in New Issue
Block a user