mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
c2a089fddf
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/36258 This fixes a few instances where YogaLayoutableShadowNode (or general shadownode casting) could offer better memory safety. 1. The reference form of traitCast() now terminates on invalid cast, instead of debug assert, since it is better to crash in production than to corrupt memory (which will crash somewhere later, in a much more confusing way). 2. We use traitCast() in more places where we previously would static_cast. This means needing to formally add a mutable version. 3. We bounds-check yoga children access in a single place by using `std::vector` `at()` instead of `[]`. 4. Removed `Trait::UnreservedTrait1` API, since multiple libraries using it can collide and we lose the memory safety benefits of `traitCast`. This change is in response to a bug where `YogaLayoutableShadowNode` may perform an invalid `static_cast` of `RawTextShadowNode` if a text or number is rendered directly inside of a `<View>` (instead of a `<Text>` element). This does not yet fix the underlying logic of YogaLayoutableShadowNode to act gracefully when a RawTextShadowNode makes its way into children. We just terminate, instead of corrupting memory. Changelog: [General][Breaking] - Better Fabric ShadowNode Memory Safety (Removes `Trait::UnreservedTrait` API) Reviewed By: javache Differential Revision: D43271779 fbshipit-source-id: 727c1230f72664bf4d261871c66ca61ddf0d5ffa
147 lines
6.2 KiB
C++
147 lines
6.2 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <react/renderer/components/scrollview/ScrollViewComponentDescriptor.h>
|
|
#include <react/renderer/components/text/ParagraphComponentDescriptor.h>
|
|
#include <react/renderer/components/text/RawTextComponentDescriptor.h>
|
|
#include <react/renderer/components/text/TextComponentDescriptor.h>
|
|
#include <react/renderer/components/view/ViewComponentDescriptor.h>
|
|
#include <react/renderer/core/TraitCast.h>
|
|
|
|
#include <react/renderer/element/Element.h>
|
|
#include <react/renderer/element/testUtils.h>
|
|
|
|
using namespace facebook::react;
|
|
|
|
TEST(traitCastTest, testOne) {
|
|
auto builder = simpleComponentBuilder();
|
|
|
|
auto viewShadowNode = std::shared_ptr<ViewShadowNode>{};
|
|
auto scrollViewShadowNode = std::shared_ptr<ScrollViewShadowNode>{};
|
|
auto paragraphShadowNode = std::shared_ptr<ParagraphShadowNode>{};
|
|
auto textShadowNode = std::shared_ptr<TextShadowNode>{};
|
|
auto rawTextShadowNode = std::shared_ptr<RawTextShadowNode>{};
|
|
|
|
// clang-format off
|
|
auto element =
|
|
Element<ScrollViewShadowNode>()
|
|
.reference(scrollViewShadowNode)
|
|
.children({
|
|
Element<ParagraphShadowNode>()
|
|
.reference(paragraphShadowNode)
|
|
.children({
|
|
Element<TextShadowNode>()
|
|
.reference(textShadowNode),
|
|
Element<RawTextShadowNode>()
|
|
.reference(rawTextShadowNode)
|
|
}),
|
|
Element<ViewShadowNode>()
|
|
.reference(viewShadowNode),
|
|
});
|
|
// clang-format on
|
|
|
|
auto rootShadowNode = builder.build(element);
|
|
|
|
std::shared_ptr<ShadowNode> shadowNodeForRawTextShadowNode{rawTextShadowNode};
|
|
std::shared_ptr<ShadowNode> shadowNodeForTextShadowNode{textShadowNode};
|
|
|
|
// Casting `nullptr` returns `nullptrs`.
|
|
ShadowNode *nullShadowNode = nullptr;
|
|
EXPECT_FALSE(traitCast<LayoutableShadowNode const *>(nullShadowNode));
|
|
EXPECT_FALSE(traitCast<YogaLayoutableShadowNode const *>(nullShadowNode));
|
|
EXPECT_FALSE(traitCast<LayoutableShadowNode const *>(nullShadowNode));
|
|
EXPECT_FALSE(traitCast<LayoutableShadowNode *>(nullShadowNode));
|
|
EXPECT_FALSE(traitCast<LayoutableShadowNode>(
|
|
std::shared_ptr<ShadowNode>(nullShadowNode)));
|
|
|
|
// `ViewShadowNode` is `LayoutableShadowNode` and `YogaLayoutableShadowNode`.
|
|
EXPECT_TRUE(traitCast<LayoutableShadowNode const *>(viewShadowNode.get()));
|
|
EXPECT_TRUE(
|
|
traitCast<YogaLayoutableShadowNode const *>(viewShadowNode.get()));
|
|
EXPECT_NO_FATAL_FAILURE(
|
|
traitCast<LayoutableShadowNode const &>(*viewShadowNode));
|
|
EXPECT_NO_FATAL_FAILURE(
|
|
traitCast<YogaLayoutableShadowNode const &>(*viewShadowNode));
|
|
EXPECT_NO_FATAL_FAILURE(
|
|
traitCast<YogaLayoutableShadowNode &>(*viewShadowNode));
|
|
EXPECT_TRUE(traitCast<LayoutableShadowNode *>(viewShadowNode.get()));
|
|
EXPECT_TRUE(traitCast<LayoutableShadowNode>(viewShadowNode));
|
|
|
|
// `ScrollViewShadowNode` is `LayoutableShadowNode` and
|
|
// `YogaLayoutableShadowNode`.
|
|
EXPECT_TRUE(
|
|
traitCast<LayoutableShadowNode const *>(scrollViewShadowNode.get()));
|
|
EXPECT_TRUE(
|
|
traitCast<YogaLayoutableShadowNode const *>(scrollViewShadowNode.get()));
|
|
EXPECT_NO_FATAL_FAILURE(
|
|
traitCast<LayoutableShadowNode const &>(*scrollViewShadowNode));
|
|
EXPECT_NO_FATAL_FAILURE(
|
|
traitCast<YogaLayoutableShadowNode const &>(*scrollViewShadowNode));
|
|
|
|
// `ParagraphShadowNode` is `LayoutableShadowNode` and
|
|
// `YogaLayoutableShadowNode`.
|
|
EXPECT_TRUE(
|
|
traitCast<LayoutableShadowNode const *>(paragraphShadowNode.get()));
|
|
EXPECT_TRUE(
|
|
traitCast<YogaLayoutableShadowNode const *>(paragraphShadowNode.get()));
|
|
EXPECT_NO_FATAL_FAILURE(
|
|
traitCast<LayoutableShadowNode const &>(*paragraphShadowNode));
|
|
EXPECT_NO_FATAL_FAILURE(
|
|
traitCast<YogaLayoutableShadowNode const &>(*paragraphShadowNode));
|
|
|
|
// `TextShadowNode` is *not* `LayoutableShadowNode` nor
|
|
// `YogaLayoutableShadowNode`.
|
|
EXPECT_FALSE(traitCast<LayoutableShadowNode const *>(textShadowNode.get()));
|
|
EXPECT_FALSE(
|
|
traitCast<YogaLayoutableShadowNode const *>(textShadowNode.get()));
|
|
EXPECT_DEATH_IF_SUPPORTED(
|
|
traitCast<LayoutableShadowNode const &>(*textShadowNode), "");
|
|
EXPECT_DEATH_IF_SUPPORTED(
|
|
traitCast<YogaLayoutableShadowNode const &>(*textShadowNode), "");
|
|
|
|
// `RawTextShadowNode` is *not* `LayoutableShadowNode` nor
|
|
// `YogaLayoutableShadowNode`.
|
|
EXPECT_FALSE(
|
|
traitCast<LayoutableShadowNode const *>(rawTextShadowNode.get()));
|
|
EXPECT_FALSE(
|
|
traitCast<YogaLayoutableShadowNode const *>(rawTextShadowNode.get()));
|
|
EXPECT_DEATH_IF_SUPPORTED(
|
|
traitCast<LayoutableShadowNode const &>(*rawTextShadowNode), "");
|
|
EXPECT_DEATH_IF_SUPPORTED(
|
|
traitCast<YogaLayoutableShadowNode const &>(*rawTextShadowNode), "");
|
|
|
|
// trait cast to `RawTextShadowNode` works on `RawTextShadowNode`
|
|
// and not on TextShadowNode or ViewShadowNode
|
|
EXPECT_TRUE(traitCast<RawTextShadowNode const *>(
|
|
shadowNodeForRawTextShadowNode.get()));
|
|
EXPECT_NO_FATAL_FAILURE(
|
|
traitCast<RawTextShadowNode const &>(*shadowNodeForRawTextShadowNode));
|
|
EXPECT_FALSE(
|
|
traitCast<RawTextShadowNode const *>(shadowNodeForTextShadowNode.get()));
|
|
EXPECT_DEATH_IF_SUPPORTED(
|
|
traitCast<RawTextShadowNode const &>(*shadowNodeForTextShadowNode), "");
|
|
EXPECT_FALSE(traitCast<RawTextShadowNode const *>(viewShadowNode.get()));
|
|
EXPECT_DEATH_IF_SUPPORTED(
|
|
traitCast<RawTextShadowNode const &>(*viewShadowNode), "");
|
|
|
|
// trait cast to `TextShadowNode` works on `TextShadowNode`
|
|
// and not on RawTextShadowNode or ViewShadowNode
|
|
EXPECT_TRUE(
|
|
traitCast<TextShadowNode const *>(shadowNodeForTextShadowNode.get()));
|
|
EXPECT_NO_FATAL_FAILURE(
|
|
traitCast<TextShadowNode const &>(*shadowNodeForTextShadowNode));
|
|
EXPECT_FALSE(
|
|
traitCast<TextShadowNode const *>(shadowNodeForRawTextShadowNode.get()));
|
|
EXPECT_DEATH_IF_SUPPORTED(
|
|
traitCast<TextShadowNode const &>(*shadowNodeForRawTextShadowNode), "");
|
|
EXPECT_FALSE(traitCast<TextShadowNode const *>(viewShadowNode.get()));
|
|
EXPECT_DEATH_IF_SUPPORTED(
|
|
traitCast<TextShadowNode const &>(*viewShadowNode), "");
|
|
}
|