Clone children only during layout, allow mixing shared + owned children

Summary:
@public

Limit child cloning to layout calculation. This also allows for mixing shared and owned children.

Rationale:
We do allow for shared children if the caller manages themselves. The single known use case is React Native.

So far, we have cloned children eagerly whenever child lists are mutated, or layout is run. This was to allow for a quick check of the owner of any first child, assuming that either *all* or *no* child of a node are shared.

For Yoga/Java, we want to get rid of global weak JNI refs, and these are also used to invoke clone callbacks. We can achieve that goal by switching to an alternative approach, passing additional data to the layout pass. This additional data has to be passed to any configured cloning callback. Therefore, it is desirable to **only call cloning functions during the layout pass.**

The obvious solution seems to be to not uphold the invariant of the first child determining shared/owned state of all siblings, and allow for a mix of shared and own children.

Reviewed By: shergin

Differential Revision: D14136223

fbshipit-source-id: 34490cfeeb2170c99d6ed1b9bdcbcedb316813af
This commit is contained in:
David Aurelio
2019-02-20 11:52:38 -08:00
committed by Facebook Github Bot
parent 367a93de88
commit b1c749075d
4 changed files with 82 additions and 84 deletions

View File

@@ -6,6 +6,7 @@
*/
#include <gtest/gtest.h>
#include <yoga/Yoga.h>
#include <yoga/YGNode.h>
TEST(YogaTest, cloning_shared_root) {
const YGConfigRef config = YGConfigNew();
@@ -104,7 +105,7 @@ TEST(YogaTest, cloning_shared_root) {
YGConfigFree(config);
}
TEST(YogaTest, mutating_children_of_a_clone_clones) {
TEST(YogaTest, mutating_children_of_a_clone_clones_only_after_layout) {
const YGConfigRef config = YGConfigNew();
const YGNodeRef root = YGNodeNewWithConfig(config);
@@ -129,7 +130,7 @@ TEST(YogaTest, mutating_children_of_a_clone_clones) {
ASSERT_EQ(1, YGNodeGetChildCount(root2));
ASSERT_EQ(2, YGNodeGetChildCount(root3));
ASSERT_EQ(root3_child1, YGNodeGetChild(root3, 1));
ASSERT_NE(YGNodeGetChild(root2, 0), YGNodeGetChild(root3, 0));
ASSERT_EQ(YGNodeGetChild(root2, 0), YGNodeGetChild(root3, 0));
const YGNodeRef root4 = YGNodeClone(root3);
ASSERT_EQ(root3_child1, YGNodeGetChild(root4, 1));
@@ -137,7 +138,12 @@ TEST(YogaTest, mutating_children_of_a_clone_clones) {
YGNodeRemoveChild(root4, root3_child1);
ASSERT_EQ(2, YGNodeGetChildCount(root3));
ASSERT_EQ(1, YGNodeGetChildCount(root4));
ASSERT_EQ(YGNodeGetChild(root3, 0), YGNodeGetChild(root4, 0));
YGNodeCalculateLayout(root4, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_NE(YGNodeGetChild(root3, 0), YGNodeGetChild(root4, 0));
YGNodeCalculateLayout(root3, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_NE(YGNodeGetChild(root2, 0), YGNodeGetChild(root3, 0));
YGNodeFreeRecursive(root4);
YGNodeFreeRecursive(root3);
@@ -245,3 +251,34 @@ TEST(YogaTest, cloning_and_freeing) {
ASSERT_EQ(initialInstanceCount, YGNodeGetInstanceCount());
}
TEST(YogaTest, mixed_shared_and_owned_children) {
// Don't try this at home!
YGNodeRef root0 = YGNodeNew();
YGNodeRef root1 = YGNodeNew();
YGNodeRef root0_child0 = YGNodeNew();
YGNodeRef root0_child0_0 = YGNodeNew();
YGNodeInsertChild(root0, root0_child0, 0);
YGNodeInsertChild(root0_child0, root0_child0_0, 0);
YGNodeRef root1_child0 = YGNodeNew();
YGNodeRef root1_child2 = YGNodeNew();
YGNodeInsertChild(root1, root1_child0, 0);
YGNodeInsertChild(root1, root1_child2, 1);
auto children = root1->getChildren();
children.insert(children.begin() + 1, root0_child0);
root1->setChildren(children);
auto secondChild = YGNodeGetChild(root1, 1);
ASSERT_EQ(secondChild, YGNodeGetChild(root0, 0));
ASSERT_EQ(YGNodeGetChild(secondChild, 0), YGNodeGetChild(root0_child0, 0));
YGNodeCalculateLayout(root1, YGUndefined, YGUndefined, YGDirectionLTR);
secondChild = YGNodeGetChild(root1, 1);
ASSERT_NE(secondChild, YGNodeGetChild(root0, 0));
ASSERT_EQ(YGNodeGetOwner(secondChild), root1);
ASSERT_NE(YGNodeGetChild(secondChild, 0), YGNodeGetChild(root0_child0, 0));
ASSERT_EQ(YGNodeGetOwner(YGNodeGetChild(secondChild, 0)), secondChild);
}