mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Better ShadowNode memory safety (#36258)
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
This commit is contained in:
committed by
Facebook GitHub Bot
parent
de4ed0ddfc
commit
c2a089fddf
@@ -11,6 +11,7 @@
|
||||
#include <react/renderer/components/text/RawTextShadowNode.h>
|
||||
#include <react/renderer/components/text/TextProps.h>
|
||||
#include <react/renderer/components/text/TextShadowNode.h>
|
||||
#include <react/renderer/core/TraitCast.h>
|
||||
#include <react/renderer/mounting/ShadowView.h>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <react/renderer/attributedstring/AttributedStringBox.h>
|
||||
#include <react/renderer/components/view/ViewShadowNode.h>
|
||||
#include <react/renderer/components/view/conversions.h>
|
||||
#include <react/renderer/core/TraitCast.h>
|
||||
#include <react/renderer/graphics/rounding.h>
|
||||
#include <react/renderer/telemetry/TransactionTelemetry.h>
|
||||
|
||||
@@ -205,8 +206,8 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) {
|
||||
paragraphShadowNode =
|
||||
static_cast<ParagraphShadowNode *>(paragraphOwningShadowNode.get());
|
||||
|
||||
auto &layoutableShadowNode = const_cast<LayoutableShadowNode &>(
|
||||
traitCast<LayoutableShadowNode const &>(*clonedShadowNode));
|
||||
auto &layoutableShadowNode =
|
||||
traitCast<LayoutableShadowNode &>(*clonedShadowNode);
|
||||
|
||||
auto attachmentFrame = measurement.attachments[i].frame;
|
||||
auto attachmentSize = roundToPixel<&ceil>(
|
||||
|
||||
@@ -29,34 +29,13 @@ class RawTextShadowNode : public ConcreteShadowNode<
|
||||
using ConcreteShadowNode::ConcreteShadowNode;
|
||||
static ShadowNodeTraits BaseTraits() {
|
||||
auto traits = ConcreteShadowNode::BaseTraits();
|
||||
traits.set(ShadowNodeTraits::Trait::RawText);
|
||||
traits.set(IdentifierTrait());
|
||||
return traits;
|
||||
}
|
||||
static ShadowNodeTraits::Trait IdentifierTrait() {
|
||||
return ShadowNodeTraits::Trait::RawText;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
inline RawTextShadowNode const &traitCast<RawTextShadowNode const &>(
|
||||
ShadowNode const &shadowNode) {
|
||||
bool castable =
|
||||
shadowNode.getTraits().check(ShadowNodeTraits::Trait::RawText);
|
||||
react_native_assert(castable);
|
||||
(void)castable;
|
||||
return static_cast<RawTextShadowNode const &>(shadowNode);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline RawTextShadowNode const *traitCast<RawTextShadowNode const *>(
|
||||
ShadowNode const *shadowNode) {
|
||||
if (!shadowNode) {
|
||||
return nullptr;
|
||||
}
|
||||
bool castable =
|
||||
shadowNode->getTraits().check(ShadowNodeTraits::Trait::RawText);
|
||||
if (!castable) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<RawTextShadowNode const *>(shadowNode);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
||||
@@ -34,11 +34,15 @@ class TextShadowNode : public ConcreteShadowNode<
|
||||
#ifdef ANDROID
|
||||
traits.set(ShadowNodeTraits::Trait::FormsView);
|
||||
#endif
|
||||
traits.set(ShadowNodeTraits::Trait::Text);
|
||||
traits.set(IdentifierTrait());
|
||||
|
||||
return traits;
|
||||
}
|
||||
|
||||
static ShadowNodeTraits::Trait IdentifierTrait() {
|
||||
return ShadowNodeTraits::Trait::Text;
|
||||
}
|
||||
|
||||
using ConcreteShadowNode::ConcreteShadowNode;
|
||||
|
||||
#ifdef ANDROID
|
||||
@@ -58,27 +62,5 @@ class TextShadowNode : public ConcreteShadowNode<
|
||||
#endif
|
||||
};
|
||||
|
||||
template <>
|
||||
inline TextShadowNode const &traitCast<TextShadowNode const &>(
|
||||
ShadowNode const &shadowNode) {
|
||||
bool castable = shadowNode.getTraits().check(ShadowNodeTraits::Trait::Text);
|
||||
react_native_assert(castable);
|
||||
(void)castable;
|
||||
return static_cast<TextShadowNode const &>(shadowNode);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline TextShadowNode const *traitCast<TextShadowNode const *>(
|
||||
ShadowNode const *shadowNode) {
|
||||
if (!shadowNode) {
|
||||
return nullptr;
|
||||
}
|
||||
bool castable = shadowNode->getTraits().check(ShadowNodeTraits::Trait::Text);
|
||||
if (!castable) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<TextShadowNode const *>(shadowNode);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <react/renderer/components/view/conversions.h>
|
||||
#include <react/renderer/core/LayoutConstraints.h>
|
||||
#include <react/renderer/core/LayoutContext.h>
|
||||
#include <react/renderer/core/TraitCast.h>
|
||||
#include <react/renderer/debug/DebugStringConvertibleItem.h>
|
||||
#include <react/renderer/debug/SystraceSection.h>
|
||||
#include <yoga/Yoga.h>
|
||||
@@ -61,10 +62,14 @@ thread_local LayoutContext threadLocalLayoutContext;
|
||||
|
||||
ShadowNodeTraits YogaLayoutableShadowNode::BaseTraits() {
|
||||
auto traits = LayoutableShadowNode::BaseTraits();
|
||||
traits.set(ShadowNodeTraits::Trait::YogaLayoutableKind);
|
||||
traits.set(IdentifierTrait());
|
||||
return traits;
|
||||
}
|
||||
|
||||
ShadowNodeTraits::Trait YogaLayoutableShadowNode::IdentifierTrait() {
|
||||
return ShadowNodeTraits::Trait::YogaLayoutableKind;
|
||||
}
|
||||
|
||||
YogaLayoutableShadowNode::YogaLayoutableShadowNode(
|
||||
ShadowNodeFragment const &fragment,
|
||||
ShadowNodeFamily::Shared const &family,
|
||||
@@ -205,7 +210,7 @@ void YogaLayoutableShadowNode::adoptYogaChild(size_t index) {
|
||||
// TODO: At this point, React has wrong reference to the node. (T138668036)
|
||||
auto clonedChildNode = childNode.clone({});
|
||||
auto &layoutableClonedChildNode =
|
||||
traitCast<YogaLayoutableShadowNode const &>(*clonedChildNode);
|
||||
traitCast<YogaLayoutableShadowNode &>(*clonedChildNode);
|
||||
|
||||
// The owner must be nullptr for a newly cloned node.
|
||||
react_native_assert(
|
||||
@@ -298,7 +303,7 @@ void YogaLayoutableShadowNode::updateYogaChildren() {
|
||||
adoptYogaChild(i);
|
||||
|
||||
if (isClean) {
|
||||
auto &oldYogaChildNode = *oldYogaChildren[i];
|
||||
auto &oldYogaChildNode = *oldYogaChildren.at(i);
|
||||
auto &newYogaChildNode =
|
||||
traitCast<YogaLayoutableShadowNode const &>(*getChildren().at(i))
|
||||
.yogaNode_;
|
||||
@@ -550,8 +555,7 @@ void YogaLayoutableShadowNode::layout(LayoutContext layoutContext) {
|
||||
|
||||
auto contentFrame = Rect{};
|
||||
for (auto childYogaNode : yogaNode_.getChildren()) {
|
||||
auto &childNode =
|
||||
*static_cast<YogaLayoutableShadowNode *>(childYogaNode->getContext());
|
||||
auto &childNode = shadowNodeFromContext(childYogaNode);
|
||||
|
||||
// Verifying that the Yoga node belongs to the ShadowNode.
|
||||
react_native_assert(&childNode.yogaNode_ == childYogaNode);
|
||||
@@ -635,17 +639,15 @@ YGNode *YogaLayoutableShadowNode::yogaNodeCloneCallbackConnector(
|
||||
|
||||
// At this point it is guaranteed that all shadow nodes associated with yoga
|
||||
// nodes are `YogaLayoutableShadowNode` subclasses.
|
||||
auto parentNode =
|
||||
static_cast<YogaLayoutableShadowNode *>(parentYogaNode->getContext());
|
||||
auto oldNode =
|
||||
static_cast<YogaLayoutableShadowNode *>(oldYogaNode->getContext());
|
||||
auto &parentNode = shadowNodeFromContext(parentYogaNode);
|
||||
auto &oldNode = shadowNodeFromContext(oldYogaNode);
|
||||
|
||||
auto clonedNode = oldNode->clone(
|
||||
auto clonedNode = oldNode.clone(
|
||||
{ShadowNodeFragment::propsPlaceholder(),
|
||||
ShadowNodeFragment::childrenPlaceholder(),
|
||||
oldNode->getState()});
|
||||
parentNode->replaceChild(*oldNode, clonedNode, childIndex);
|
||||
return &static_cast<YogaLayoutableShadowNode &>(*clonedNode).yogaNode_;
|
||||
oldNode.getState()});
|
||||
parentNode.replaceChild(oldNode, clonedNode, childIndex);
|
||||
return &traitCast<YogaLayoutableShadowNode &>(*clonedNode).yogaNode_;
|
||||
}
|
||||
|
||||
YGSize YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector(
|
||||
@@ -657,8 +659,7 @@ YGSize YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector(
|
||||
SystraceSection s(
|
||||
"YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector");
|
||||
|
||||
auto shadowNodeRawPtr =
|
||||
static_cast<YogaLayoutableShadowNode *>(yogaNode->getContext());
|
||||
auto &shadowNode = shadowNodeFromContext(yogaNode);
|
||||
|
||||
auto minimumSize = Size{0, 0};
|
||||
auto maximumSize = Size{
|
||||
@@ -689,13 +690,19 @@ YGSize YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector(
|
||||
break;
|
||||
}
|
||||
|
||||
auto size = shadowNodeRawPtr->measureContent(
|
||||
auto size = shadowNode.measureContent(
|
||||
threadLocalLayoutContext, {minimumSize, maximumSize});
|
||||
|
||||
return YGSize{
|
||||
yogaFloatFromFloat(size.width), yogaFloatFromFloat(size.height)};
|
||||
}
|
||||
|
||||
YogaLayoutableShadowNode &YogaLayoutableShadowNode::shadowNodeFromContext(
|
||||
YGNode *yogaNode) {
|
||||
return traitCast<YogaLayoutableShadowNode &>(
|
||||
*static_cast<ShadowNode *>(yogaNode->getContext()));
|
||||
}
|
||||
|
||||
YGConfig &YogaLayoutableShadowNode::initializeYogaConfig(YGConfig &config) {
|
||||
config.setCloneNodeCallback(
|
||||
YogaLayoutableShadowNode::yogaNodeCloneCallbackConnector);
|
||||
|
||||
@@ -31,6 +31,7 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode {
|
||||
kShadowNodeChildrenSmallVectorSize>;
|
||||
|
||||
static ShadowNodeTraits BaseTraits();
|
||||
static ShadowNodeTraits::Trait IdentifierTrait();
|
||||
|
||||
#pragma mark - Constructors
|
||||
|
||||
@@ -139,6 +140,7 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode {
|
||||
YGMeasureMode widthMode,
|
||||
float height,
|
||||
YGMeasureMode heightMode);
|
||||
static YogaLayoutableShadowNode &shadowNodeFromContext(YGNode *yogaNode);
|
||||
|
||||
#pragma mark - RTL Legacy Autoflip
|
||||
|
||||
@@ -190,29 +192,5 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode {
|
||||
void ensureYogaChildrenLookFine() const;
|
||||
};
|
||||
|
||||
template <>
|
||||
inline YogaLayoutableShadowNode const &
|
||||
traitCast<YogaLayoutableShadowNode const &>(ShadowNode const &shadowNode) {
|
||||
bool castable =
|
||||
shadowNode.getTraits().check(ShadowNodeTraits::Trait::YogaLayoutableKind);
|
||||
react_native_assert(castable);
|
||||
(void)castable;
|
||||
return static_cast<YogaLayoutableShadowNode const &>(shadowNode);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline YogaLayoutableShadowNode const *
|
||||
traitCast<YogaLayoutableShadowNode const *>(ShadowNode const *shadowNode) {
|
||||
if (!shadowNode) {
|
||||
return nullptr;
|
||||
}
|
||||
bool castable = shadowNode->getTraits().check(
|
||||
ShadowNodeTraits::Trait::YogaLayoutableKind);
|
||||
if (!castable) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<YogaLayoutableShadowNode const *>(shadowNode);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <react/renderer/core/LayoutContext.h>
|
||||
#include <react/renderer/core/LayoutMetrics.h>
|
||||
#include <react/renderer/core/ShadowNode.h>
|
||||
#include <react/renderer/core/TraitCast.h>
|
||||
#include <react/renderer/debug/DebugStringConvertibleItem.h>
|
||||
#include <react/renderer/graphics/conversions.h>
|
||||
|
||||
@@ -221,10 +222,14 @@ LayoutMetrics LayoutableShadowNode::computeRelativeLayoutMetrics(
|
||||
|
||||
ShadowNodeTraits LayoutableShadowNode::BaseTraits() {
|
||||
auto traits = ShadowNodeTraits{};
|
||||
traits.set(ShadowNodeTraits::Trait::LayoutableKind);
|
||||
traits.set(IdentifierTrait());
|
||||
return traits;
|
||||
}
|
||||
|
||||
ShadowNodeTraits::Trait LayoutableShadowNode::IdentifierTrait() {
|
||||
return ShadowNodeTraits::Trait::LayoutableKind;
|
||||
}
|
||||
|
||||
LayoutMetrics LayoutableShadowNode::getLayoutMetrics() const {
|
||||
return layoutMetrics_;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ class LayoutableShadowNode : public ShadowNode {
|
||||
ShadowNodeFragment const &fragment);
|
||||
|
||||
static ShadowNodeTraits BaseTraits();
|
||||
static ShadowNodeTraits::Trait IdentifierTrait();
|
||||
|
||||
struct LayoutInspectingPolicy {
|
||||
bool includeTransform{true};
|
||||
@@ -164,29 +165,5 @@ class LayoutableShadowNode : public ShadowNode {
|
||||
LayoutMetrics layoutMetrics_;
|
||||
};
|
||||
|
||||
template <>
|
||||
inline LayoutableShadowNode const &traitCast<LayoutableShadowNode const &>(
|
||||
ShadowNode const &shadowNode) {
|
||||
bool castable =
|
||||
shadowNode.getTraits().check(ShadowNodeTraits::Trait::LayoutableKind);
|
||||
react_native_assert(castable);
|
||||
(void)castable;
|
||||
return static_cast<LayoutableShadowNode const &>(shadowNode);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline LayoutableShadowNode const *traitCast<LayoutableShadowNode const *>(
|
||||
ShadowNode const *shadowNode) {
|
||||
if (!shadowNode) {
|
||||
return nullptr;
|
||||
}
|
||||
bool castable =
|
||||
shadowNode->getTraits().check(ShadowNodeTraits::Trait::LayoutableKind);
|
||||
if (!castable) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<LayoutableShadowNode const *>(shadowNode);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
||||
@@ -224,21 +224,5 @@ class ShadowNode : public Sealable, public DebugStringConvertible {
|
||||
ShadowNodeTraits traits_;
|
||||
};
|
||||
|
||||
/*
|
||||
* Template declarations for future specializations in concrete classes.
|
||||
* `traitCast` checks for a trait that corresponds to the provided type and
|
||||
* performs `static_cast`. Practically, the behavior is identical to
|
||||
* `dynamic_cast` with very little runtime overhead.
|
||||
*/
|
||||
template <typename ShadowNodeReferenceT>
|
||||
ShadowNodeReferenceT traitCast(ShadowNode const &shadowNode);
|
||||
|
||||
template <typename ShadowNodePointerT>
|
||||
ShadowNodePointerT traitCast(ShadowNode const *shadowNode);
|
||||
|
||||
template <typename ShadowNodePointerT>
|
||||
std::shared_ptr<ShadowNodePointerT const> traitCast(
|
||||
std::shared_ptr<ShadowNode const> shadowNode);
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
||||
@@ -92,17 +92,8 @@ class ShadowNodeTraits {
|
||||
// Temporary (?) to indicate MapBuffer support on Android
|
||||
AndroidMapBufferPropsSupported = 1 << 15,
|
||||
|
||||
// Reserved
|
||||
ReservedTrait0 = 1 << 16,
|
||||
ReservedTrait1 = 1 << 17,
|
||||
ReservedTrait2 = 1 << 18,
|
||||
ReservedTrait3 = 1 << 19,
|
||||
ReservedTrait4 = 1 << 20,
|
||||
ReservedTrait5 = 1 << 21,
|
||||
ReservedTrait6 = 1 << 22,
|
||||
|
||||
// Unserved - alias these for local usage
|
||||
UnreservedTrait1 = 1 << 23
|
||||
// Inherits 'ArtBaseShadowNode' (previously built into RN)
|
||||
Art = 1 << 16,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -120,6 +111,10 @@ class ShadowNodeTraits {
|
||||
return ShadowNodeTraits::Trait(traits_ & traits) == traits;
|
||||
}
|
||||
|
||||
inline Trait get() const {
|
||||
return traits_;
|
||||
}
|
||||
|
||||
private:
|
||||
Trait traits_{Trait::None};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include <react/renderer/core/ShadowNode.h>
|
||||
#include <memory>
|
||||
|
||||
namespace facebook::react::details {
|
||||
template <typename ShadowNodePointerT, typename ParamT>
|
||||
ShadowNodePointerT traitCastPointer(ParamT shadowNode) {
|
||||
auto expectedIdentifier =
|
||||
std::remove_pointer_t<ShadowNodePointerT>::IdentifierTrait();
|
||||
if (!shadowNode || !shadowNode->getTraits().check(expectedIdentifier)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return static_cast<ShadowNodePointerT>(shadowNode);
|
||||
}
|
||||
|
||||
template <typename ShadowNodeRefT, typename ParamT>
|
||||
ShadowNodeRefT traitCastRef(ParamT &&shadowNode) {
|
||||
auto expectedIdentifier =
|
||||
std::remove_reference_t<ShadowNodeRefT>::IdentifierTrait();
|
||||
if (!shadowNode.getTraits().check(expectedIdentifier)) {
|
||||
LOG(FATAL) << "Invalid ShadowNode cast\n"
|
||||
<< "Expected identifier: " << std::hex
|
||||
<< static_cast<int32_t>(expectedIdentifier) << "\n"
|
||||
<< "Actual traits: " << std::hex
|
||||
<< static_cast<int32_t>(shadowNode.getTraits().get()) << "\n";
|
||||
}
|
||||
|
||||
return static_cast<ShadowNodeRefT>(shadowNode);
|
||||
}
|
||||
|
||||
template <typename ShadowNodeT, typename ParamT>
|
||||
std::shared_ptr<ShadowNodeT> traitCastShared(
|
||||
const std::shared_ptr<ParamT> &shadowNode) {
|
||||
auto expectedIdentifier = ShadowNodeT::IdentifierTrait();
|
||||
if (!shadowNode || !shadowNode->getTraits().check(expectedIdentifier)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::static_pointer_cast<ShadowNodeT>(shadowNode);
|
||||
}
|
||||
} // namespace facebook::react::details
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
// Cast from one ShadowNode reference to another, terminating if the cast is
|
||||
// invalid.
|
||||
template <typename ShadowNodeReferenceT>
|
||||
ShadowNodeReferenceT traitCast(ShadowNode const &shadowNode) {
|
||||
return details::traitCastRef<ShadowNodeReferenceT>(shadowNode);
|
||||
}
|
||||
template <typename ShadowNodeReferenceT>
|
||||
ShadowNodeReferenceT traitCast(ShadowNode &shadowNode) {
|
||||
return details::traitCastRef<ShadowNodeReferenceT>(shadowNode);
|
||||
}
|
||||
|
||||
// Cast from one ShadowNode pointer to another, returning nullptr if the cast is
|
||||
// invalid.
|
||||
template <typename ShadowNodePointerT>
|
||||
ShadowNodePointerT traitCast(ShadowNode const *shadowNode) {
|
||||
return details::traitCastPointer<ShadowNodePointerT>(shadowNode);
|
||||
}
|
||||
template <typename ShadowNodePointerT>
|
||||
ShadowNodePointerT traitCast(ShadowNode *shadowNode) {
|
||||
return details::traitCastPointer<ShadowNodePointerT>(shadowNode);
|
||||
}
|
||||
|
||||
// Cast from one ShadowNode shared_ptr to another, returning nullptr if the
|
||||
// cast is invalid.
|
||||
template <typename ShadowNodeT, typename ParamT>
|
||||
std::shared_ptr<ShadowNodeT> traitCast(
|
||||
const std::shared_ptr<ParamT> &shadowNode) {
|
||||
return details::traitCastShared<ShadowNodeT>(shadowNode);
|
||||
}
|
||||
template <typename ShadowNodeT, typename ParamT>
|
||||
std::shared_ptr<ShadowNodeT const> traitCast(
|
||||
const std::shared_ptr<ParamT const> &shadowNode) {
|
||||
return details::traitCastShared<ShadowNodeT const>(shadowNode);
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
@@ -12,6 +12,7 @@
|
||||
#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>
|
||||
@@ -51,8 +52,13 @@ TEST(traitCastTest, testOne) {
|
||||
std::shared_ptr<ShadowNode> shadowNodeForTextShadowNode{textShadowNode};
|
||||
|
||||
// Casting `nullptr` returns `nullptrs`.
|
||||
EXPECT_FALSE(traitCast<LayoutableShadowNode const *>(nullptr));
|
||||
EXPECT_FALSE(traitCast<YogaLayoutableShadowNode const *>(nullptr));
|
||||
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()));
|
||||
@@ -62,6 +68,10 @@ TEST(traitCastTest, testOne) {
|
||||
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`.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <react/renderer/core/LayoutMetrics.h>
|
||||
#include <react/renderer/core/LayoutableShadowNode.h>
|
||||
#include <react/renderer/core/TraitCast.h>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <react/renderer/core/DynamicPropsUtilities.h>
|
||||
#include <react/renderer/core/PropsParserContext.h>
|
||||
#include <react/renderer/core/ShadowNodeFragment.h>
|
||||
#include <react/renderer/core/TraitCast.h>
|
||||
#include <react/renderer/debug/SystraceSection.h>
|
||||
#include <react/renderer/uimanager/SurfaceRegistryBinding.h>
|
||||
#include <react/renderer/uimanager/UIManagerBinding.h>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <jsi/JSIDynamic.h>
|
||||
#include <react/debug/react_native_assert.h>
|
||||
#include <react/renderer/core/LayoutableShadowNode.h>
|
||||
#include <react/renderer/core/TraitCast.h>
|
||||
#include <react/renderer/debug/SystraceSection.h>
|
||||
#include <react/renderer/runtimescheduler/RuntimeSchedulerBinding.h>
|
||||
#include <react/renderer/uimanager/primitives.h>
|
||||
|
||||
Reference in New Issue
Block a user