Files
react-native/ReactCommon/react/renderer/components/text/BaseTextShadowNode.cpp
T
Nick Gerleman c2a089fddf 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
2023-02-27 10:07:51 -08:00

77 lines
2.8 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 "BaseTextShadowNode.h"
#include <react/renderer/components/text/RawTextProps.h>
#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 {
inline ShadowView shadowViewFromShadowNode(ShadowNode const &shadowNode) {
auto shadowView = ShadowView{shadowNode};
// Clearing `props` and `state` (which we don't use) allows avoiding retain
// cycles.
shadowView.props = nullptr;
shadowView.state = nullptr;
return shadowView;
}
void BaseTextShadowNode::buildAttributedString(
TextAttributes const &baseTextAttributes,
ShadowNode const &parentNode,
AttributedString &outAttributedString,
Attachments &outAttachments) {
for (auto const &childNode : parentNode.getChildren()) {
// RawShadowNode
auto rawTextShadowNode =
traitCast<RawTextShadowNode const *>(childNode.get());
if (rawTextShadowNode != nullptr) {
auto fragment = AttributedString::Fragment{};
fragment.string = rawTextShadowNode->getConcreteProps().text;
fragment.textAttributes = baseTextAttributes;
// Storing a retaining pointer to `ParagraphShadowNode` inside
// `attributedString` causes a retain cycle (besides that fact that we
// don't need it at all). Storing a `ShadowView` instance instead of
// `ShadowNode` should properly fix this problem.
fragment.parentShadowView = shadowViewFromShadowNode(parentNode);
outAttributedString.appendFragment(fragment);
continue;
}
// TextShadowNode
auto textShadowNode = traitCast<TextShadowNode const *>(childNode.get());
if (textShadowNode != nullptr) {
auto localTextAttributes = baseTextAttributes;
localTextAttributes.apply(
textShadowNode->getConcreteProps().textAttributes);
buildAttributedString(
localTextAttributes,
*textShadowNode,
outAttributedString,
outAttachments);
continue;
}
// Any *other* kind of ShadowNode
auto fragment = AttributedString::Fragment{};
fragment.string = AttributedString::Fragment::AttachmentCharacter();
fragment.parentShadowView = shadowViewFromShadowNode(*childNode);
fragment.textAttributes = baseTextAttributes;
outAttributedString.appendFragment(fragment);
outAttachments.push_back(Attachment{
childNode.get(), outAttributedString.getFragments().size() - 1});
}
}
} // namespace facebook::react