Skip to content

Commit

Permalink
C++ Cleanup 3/N: Reorganize YGNode (facebook#1350)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: facebook#1350

X-link: facebook/react-native#39170

## This diff

This diff adds a top level `node` directory for code related to Yoga nodes and data structures on them (inc moving `YGLayout` to `LayoutResults`).

The public API for config handles is `YGNodeRef`, which is forward declared to be a pointer to a struct named `YGNode`. The existing `YGNode` is split into `yoga::Node`, as the private C++ implementation, inheriting from `YGNode`, a marker type represented as an empty struct. The public API continues to accept `YGNodeRef`, which continues to be `YGNode *`, but it must be cast to its concrete internal representation at the API boundary before doing work on it.

This change ends up needing to touch quite a bit, due to the amount of code that mixed and matched private and public APIs. Don't be scared though, because these changes are very mechanical, and Phabricator's line-count is 3x the actual amount due to mirrors and dirsyncs.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D48712710

fbshipit-source-id: 95225082e31bf7a059098188d84be090c408ac42
  • Loading branch information
NickGerleman authored and facebook-github-bot committed Aug 30, 2023
1 parent ac41f17 commit 645db91
Show file tree
Hide file tree
Showing 31 changed files with 625 additions and 578 deletions.
6 changes: 3 additions & 3 deletions java/jni/YGJNIVanilla.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

// TODO: Reconcile missing layoutContext functionality from callbacks in the C
// API and use that
#include <yoga/YGNode.h>
#include <yoga/node/Node.h>

using namespace facebook;
using namespace facebook::yoga;
Expand Down Expand Up @@ -688,7 +688,7 @@ static void jni_YGNodeSetHasMeasureFuncJNI(
jobject /*obj*/,
jlong nativePointer,
jboolean hasMeasureFunc) {
_jlong2YGNodeRef(nativePointer)
static_cast<yoga::Node*>(_jlong2YGNodeRef(nativePointer))
->setMeasureFunc(hasMeasureFunc ? YGJNIMeasureFunc : nullptr);
}

Expand All @@ -715,7 +715,7 @@ static void jni_YGNodeSetHasBaselineFuncJNI(
jobject /*obj*/,
jlong nativePointer,
jboolean hasBaselineFunc) {
_jlong2YGNodeRef(nativePointer)
static_cast<yoga::Node*>(_jlong2YGNodeRef(nativePointer))
->setBaselineFunc(hasBaselineFunc ? YGJNIBaselineFunc : nullptr);
}

Expand Down
11 changes: 6 additions & 5 deletions java/jni/YGJTypesVanilla.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
#include "jni.h"

class PtrJNodeMapVanilla {
std::map<YGNodeRef, size_t> ptrsToIdxs_;
jobjectArray javaNodes_;
std::map<YGNodeConstRef, size_t> ptrsToIdxs_{};
jobjectArray javaNodes_{};

public:
PtrJNodeMapVanilla() : ptrsToIdxs_{}, javaNodes_{} {}
PtrJNodeMapVanilla() = default;

PtrJNodeMapVanilla(jlongArray javaNativePointers, jobjectArray javaNodes)
: javaNodes_{javaNodes} {
using namespace facebook::yoga::vanillajni;
Expand All @@ -30,11 +31,11 @@ class PtrJNodeMapVanilla {
javaNativePointers, 0, nativePointersSize, nativePointers.data());

for (size_t i = 0; i < nativePointersSize; ++i) {
ptrsToIdxs_[(YGNodeRef) nativePointers[i]] = i;
ptrsToIdxs_[(YGNodeConstRef) nativePointers[i]] = i;
}
}

facebook::yoga::vanillajni::ScopedLocalRef<jobject> ref(YGNodeRef node) {
facebook::yoga::vanillajni::ScopedLocalRef<jobject> ref(YGNodeConstRef node) {
using namespace facebook::yoga::vanillajni;

JNIEnv* env = getCurrentEnv();
Expand Down
16 changes: 9 additions & 7 deletions tests/EventsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <yoga/event/event.h>
#include <yoga/Yoga.h>
#include <yoga/YGEnums.h>
#include <yoga/YGNode.h>

#include <algorithm>
#include <functional>
Expand All @@ -30,7 +29,7 @@ struct TypedEventTestData<Event::LayoutPassEnd> {
};

struct EventArgs {
const YGNode* node;
const YGNodeConstRef node;
Event::Type type;
std::unique_ptr<void, std::function<void(void*)>> dataPtr;
std::unique_ptr<void, std::function<void(void*)>> eventTestDataPtr;
Expand All @@ -48,7 +47,7 @@ struct EventArgs {

class EventTest : public ::testing::Test {
ScopedEventSubscription subscription = {&EventTest::listen};
static void listen(const YGNode&, Event::Type, Event::Data);
static void listen(YGNodeConstRef, Event::Type, Event::Data);

public:
static std::vector<EventArgs> events;
Expand Down Expand Up @@ -284,16 +283,16 @@ TEST_F(EventTest, baseline_functions_get_wrapped) {
namespace {

template <Event::Type E>
EventArgs createArgs(const YGNode& node, const Event::Data data) {
EventArgs createArgs(YGNodeConstRef node, const Event::Data data) {
using Data = Event::TypedData<E>;
auto deleteData = [](void* x) { delete static_cast<Data*>(x); };

return {&node, E, {new Data{(data.get<E>())}, deleteData}, nullptr};
return {node, E, {new Data{(data.get<E>())}, deleteData}, nullptr};
}

template <Event::Type E>
EventArgs createArgs(
const YGNode& node,
YGNodeConstRef node,
const Event::Data data,
TypedEventTestData<E> eventTestData) {
using EventTestData = TypedEventTestData<E>;
Expand All @@ -309,7 +308,10 @@ EventArgs createArgs(

} // namespace

void EventTest::listen(const YGNode& node, Event::Type type, Event::Data data) {
void EventTest::listen(
YGNodeConstRef node,
Event::Type type,
Event::Data data) {
switch (type) {
case Event::NodeAllocation:
events.push_back(createArgs<Event::NodeAllocation>(node, data));
Expand Down
21 changes: 10 additions & 11 deletions tests/YGAlignBaselineTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

#include <gtest/gtest.h>
#include <yoga/YGNode.h>
#include <yoga/Yoga.h>

static float _baselineFunc(
Expand Down Expand Up @@ -197,7 +196,7 @@ TEST(YogaTest, align_baseline_parent_using_child_in_column_as_reference) {

const YGNodeRef root_child1_child1 =
createYGNode(config, YGFlexDirectionColumn, 500, 400, false);
root_child1_child1->setBaselineFunc(_baselineFunc);
YGNodeSetBaselineFunc(root_child1_child1, _baselineFunc);
YGNodeSetIsReferenceBaseline(root_child1_child1, true);
YGNodeInsertChild(root_child1, root_child1_child1, 1);

Expand Down Expand Up @@ -242,7 +241,7 @@ TEST(

const YGNodeRef root_child1_child1 =
createYGNode(config, YGFlexDirectionColumn, 500, 400, false);
root_child1_child1->setBaselineFunc(_baselineFunc);
YGNodeSetBaselineFunc(root_child1_child1, _baselineFunc);
YGNodeSetIsReferenceBaseline(root_child1_child1, true);
YGNodeStyleSetPadding(root_child1_child1, YGEdgeLeft, 100);
YGNodeStyleSetPadding(root_child1_child1, YGEdgeRight, 100);
Expand Down Expand Up @@ -295,7 +294,7 @@ TEST(

const YGNodeRef root_child1_child1 =
createYGNode(config, YGFlexDirectionColumn, 500, 400, false);
root_child1_child1->setBaselineFunc(_baselineFunc);
YGNodeSetBaselineFunc(root_child1_child1, _baselineFunc);
YGNodeSetIsReferenceBaseline(root_child1_child1, true);
YGNodeInsertChild(root_child1, root_child1_child1, 1);

Expand Down Expand Up @@ -344,7 +343,7 @@ TEST(

const YGNodeRef root_child1_child1 =
createYGNode(config, YGFlexDirectionColumn, 500, 400, false);
root_child1_child1->setBaselineFunc(_baselineFunc);
YGNodeSetBaselineFunc(root_child1_child1, _baselineFunc);
YGNodeSetIsReferenceBaseline(root_child1_child1, true);
YGNodeInsertChild(root_child1, root_child1_child1, 1);

Expand Down Expand Up @@ -389,7 +388,7 @@ TEST(

const YGNodeRef root_child1_child1 =
createYGNode(config, YGFlexDirectionColumn, 500, 400, false);
root_child1_child1->setBaselineFunc(_baselineFunc);
YGNodeSetBaselineFunc(root_child1_child1, _baselineFunc);
YGNodeSetIsReferenceBaseline(root_child1_child1, true);
YGNodeStyleSetMargin(root_child1_child1, YGEdgeLeft, 100);
YGNodeStyleSetMargin(root_child1_child1, YGEdgeRight, 100);
Expand Down Expand Up @@ -436,7 +435,7 @@ TEST(YogaTest, align_baseline_parent_using_child_in_row_as_reference) {

const YGNodeRef root_child1_child1 =
createYGNode(config, YGFlexDirectionColumn, 500, 400, false);
root_child1_child1->setBaselineFunc(_baselineFunc);
YGNodeSetBaselineFunc(root_child1_child1, _baselineFunc);
YGNodeSetIsReferenceBaseline(root_child1_child1, true);
YGNodeInsertChild(root_child1, root_child1_child1, 1);

Expand Down Expand Up @@ -481,7 +480,7 @@ TEST(

const YGNodeRef root_child1_child1 =
createYGNode(config, YGFlexDirectionColumn, 500, 400, false);
root_child1_child1->setBaselineFunc(_baselineFunc);
YGNodeSetBaselineFunc(root_child1_child1, _baselineFunc);
YGNodeSetIsReferenceBaseline(root_child1_child1, true);
YGNodeStyleSetPadding(root_child1_child1, YGEdgeLeft, 100);
YGNodeStyleSetPadding(root_child1_child1, YGEdgeRight, 100);
Expand Down Expand Up @@ -530,7 +529,7 @@ TEST(

const YGNodeRef root_child1_child1 =
createYGNode(config, YGFlexDirectionColumn, 500, 400, false);
root_child1_child1->setBaselineFunc(_baselineFunc);
YGNodeSetBaselineFunc(root_child1_child1, _baselineFunc);
YGNodeSetIsReferenceBaseline(root_child1_child1, true);
YGNodeStyleSetMargin(root_child1_child1, YGEdgeLeft, 100);
YGNodeStyleSetMargin(root_child1_child1, YGEdgeRight, 100);
Expand Down Expand Up @@ -670,7 +669,7 @@ TEST(

const YGNodeRef root_child1_child1 =
createYGNode(config, YGFlexDirectionColumn, 500, 400, false);
root_child1_child1->setBaselineFunc(_baselineFunc);
YGNodeSetBaselineFunc(root_child1_child1, _baselineFunc);
YGNodeSetIsReferenceBaseline(root_child1_child1, true);
YGNodeInsertChild(root_child1, root_child1_child1, 1);

Expand Down Expand Up @@ -721,7 +720,7 @@ TEST(

const YGNodeRef root_child1_child1 =
createYGNode(config, YGFlexDirectionColumn, 500, 400, false);
root_child1_child1->setBaselineFunc(_baselineFunc);
YGNodeSetBaselineFunc(root_child1_child1, _baselineFunc);
YGNodeSetIsReferenceBaseline(root_child1_child1, true);
YGNodeInsertChild(root_child1, root_child1_child1, 1);

Expand Down
3 changes: 1 addition & 2 deletions tests/YGAspectRatioTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

#include <gtest/gtest.h>
#include <yoga/YGNode.h>
#include <yoga/Yoga.h>

static YGSize _measure(
Expand Down Expand Up @@ -449,7 +448,7 @@ TEST(YogaTest, aspect_ratio_with_measure_func) {
YGNodeStyleSetHeight(root, 100);

const YGNodeRef root_child0 = YGNodeNew();
root_child0->setMeasureFunc(_measure);
YGNodeSetMeasureFunc(root_child0, _measure);
YGNodeStyleSetAspectRatio(root_child0, 1);
YGNodeInsertChild(root, root_child0, 0);

Expand Down
7 changes: 3 additions & 4 deletions tests/YGBaselineFuncTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@
*/

#include <gtest/gtest.h>
#include <yoga/YGNode.h>
#include <yoga/Yoga.h>

static float _baseline(
YGNodeRef node,
const float /*width*/,
const float /*height*/) {
float* baseline = (float*) node->getContext();
float* baseline = (float*) YGNodeGetContext(node);
return *baseline;
}

Expand All @@ -36,9 +35,9 @@ TEST(YogaTest, align_baseline_customer_func) {

float baselineValue = 10;
const YGNodeRef root_child1_child0 = YGNodeNew();
root_child1_child0->setContext(&baselineValue);
YGNodeSetContext(root_child1_child0, &baselineValue);
YGNodeStyleSetWidth(root_child1_child0, 50);
root_child1_child0->setBaselineFunc(_baseline);
YGNodeSetBaselineFunc(root_child1_child0, _baseline);
YGNodeStyleSetHeight(root_child1_child0, 20);
YGNodeInsertChild(root_child1, root_child1_child0, 0);
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
Expand Down
33 changes: 18 additions & 15 deletions tests/YGDirtiedTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
*/

#include <gtest/gtest.h>
#include <yoga/YGNode.h>
#include <yoga/Yoga.h>
#include <yoga/node/Node.h>

using namespace facebook;

static void _dirtied(YGNodeRef node) {
int* dirtiedCount = (int*) node->getContext();
int* dirtiedCount = (int*) YGNodeGetContext(node);
(*dirtiedCount)++;
}

Expand All @@ -22,17 +25,17 @@ TEST(YogaTest, dirtied) {
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);

int dirtiedCount = 0;
root->setContext(&dirtiedCount);
root->setDirtiedFunc(_dirtied);
YGNodeSetContext(root, &dirtiedCount);
YGNodeSetDirtiedFunc(root, _dirtied);

ASSERT_EQ(0, dirtiedCount);

// `_dirtied` MUST be called in case of explicit dirtying.
root->setDirty(true);
static_cast<yoga::Node*>(root)->setDirty(true);
ASSERT_EQ(1, dirtiedCount);

// `_dirtied` MUST be called ONCE.
root->setDirty(true);
static_cast<yoga::Node*>(root)->setDirty(true);
ASSERT_EQ(1, dirtiedCount);
}

Expand All @@ -55,17 +58,17 @@ TEST(YogaTest, dirtied_propagation) {
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);

int dirtiedCount = 0;
root->setContext(&dirtiedCount);
root->setDirtiedFunc(_dirtied);
YGNodeSetContext(root, &dirtiedCount);
YGNodeSetDirtiedFunc(root, _dirtied);

ASSERT_EQ(0, dirtiedCount);

// `_dirtied` MUST be called for the first time.
root_child0->markDirtyAndPropagate();
static_cast<yoga::Node*>(root_child0)->markDirtyAndPropagate();
ASSERT_EQ(1, dirtiedCount);

// `_dirtied` must NOT be called for the second time.
root_child0->markDirtyAndPropagate();
static_cast<yoga::Node*>(root_child0)->markDirtyAndPropagate();
ASSERT_EQ(1, dirtiedCount);
}

Expand All @@ -88,20 +91,20 @@ TEST(YogaTest, dirtied_hierarchy) {
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);

int dirtiedCount = 0;
root_child0->setContext(&dirtiedCount);
root_child0->setDirtiedFunc(_dirtied);
YGNodeSetContext(root_child0, &dirtiedCount);
YGNodeSetDirtiedFunc(root_child0, _dirtied);

ASSERT_EQ(0, dirtiedCount);

// `_dirtied` must NOT be called for descendants.
root->markDirtyAndPropagate();
static_cast<yoga::Node*>(root)->markDirtyAndPropagate();
ASSERT_EQ(0, dirtiedCount);

// `_dirtied` must NOT be called for the sibling node.
root_child1->markDirtyAndPropagate();
static_cast<yoga::Node*>(root_child1)->markDirtyAndPropagate();
ASSERT_EQ(0, dirtiedCount);

// `_dirtied` MUST be called in case of explicit dirtying.
root_child0->markDirtyAndPropagate();
static_cast<yoga::Node*>(root_child0)->markDirtyAndPropagate();
ASSERT_EQ(1, dirtiedCount);
}
Loading

0 comments on commit 645db91

Please sign in to comment.