/
githubmr
/
facebook-react-native
Обзор
Документация
Войти
/
githubmr
/
facebook-react-native
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp
527 строк
16 KB
Christoph Purrer
Replace ContextContainer::Shared with std::shared_ptr<const ContextContainer> 2/2 (#52750)
22 июл 2025, 22:40
22 июл 2025, 22:40
7b5307d
Код
Авторство
О чём код?
/* * 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 "ShadowNode.h" #include "DynamicPropsUtilities.h" #include "ShadowNodeFragment.h" #include <react/debug/react_native_assert.h> #include <react/featureflags/ReactNativeFeatureFlags.h> #include <react/renderer/core/ComponentDescriptor.h> #include <react/renderer/core/ShadowNodeFragment.h> #include <react/renderer/debug/DebugStringConvertible.h> #include <react/renderer/debug/debugStringConvertibleUtils.h> #include <utility> namespace facebook::react { /* * Runtime shadow node reference updates should only run from one thread at all * times to avoid having more than one shadow tree updating the current fiber * tree simultaneously. This thread_local flag allows enabling the updates for * choses threads. */ thread_local bool useRuntimeShadowNodeReferenceUpdateOnThread{false}; // NOLINT /* static */ void ShadowNode::setUseRuntimeShadowNodeReferenceUpdateOnThread( bool isEnabled) { useRuntimeShadowNodeReferenceUpdateOnThread = isEnabled; } ShadowNode::SharedListOfShared ShadowNode::emptySharedShadowNodeSharedList() { static const auto emptySharedShadowNodeSharedList = std::make_shared<std::vector<std::shared_ptr<const ShadowNode>>>(); return emptySharedShadowNodeSharedList; } /* * On iOS, this method returns `props` if provided, `sourceShadowNode`'s props * otherwise. On Android, we forward props in case `sourceShadowNode` hasn't * been mounted. `Props::rawProps` are merged from `props` to a copy of * `sourceShadowNode.props_` and returned. This is necessary to enable * Background Executor and should be removed once reimplementation of JNI layer * is finished. */ Props::Shared ShadowNode::propsForClonedShadowNode( const ShadowNode& sourceShadowNode, const Props::Shared& props) { #ifdef RN_SERIALIZABLE_STATE bool hasBeenMounted = sourceShadowNode.hasBeenMounted_; bool sourceNodeHasRawProps = !sourceShadowNode.getProps()->rawProps.empty(); if (!hasBeenMounted && sourceNodeHasRawProps && props) { auto& castedProps = const_cast<Props&>(*props); castedProps.rawProps = mergeDynamicProps( sourceShadowNode.getProps()->rawProps, props->rawProps, NullValueStrategy::Override); return props; } #endif return props ? props : sourceShadowNode.getProps(); } bool ShadowNode::sameFamily(const ShadowNode& first, const ShadowNode& second) { return first.family_ == second.family_; } #pragma mark - Constructors ShadowNode::ShadowNode( const ShadowNodeFragment& fragment, ShadowNodeFamily::Shared family, ShadowNodeTraits traits) : #if RN_DEBUG_STRING_CONVERTIBLE revision_(1), #endif props_(fragment.props), children_( fragment.children ? fragment.children : emptySharedShadowNodeSharedList()), state_(fragment.state), orderIndex_(0), family_(std::move(family)), traits_(traits) { react_native_assert(props_); react_native_assert(children_); traits_.set(ShadowNodeTraits::Trait::ChildrenAreShared); for (const auto& child : *children_) { child->family_->setParent(family_); } updateTraitsIfNeccessary(); // The first node of the family gets its state committed automatically. family_->setMostRecentState(state_); } ShadowNode::ShadowNode( const ShadowNode& sourceShadowNode, const ShadowNodeFragment& fragment) : #if RN_DEBUG_STRING_CONVERTIBLE revision_(sourceShadowNode.revision_ + 1), #endif props_(propsForClonedShadowNode(sourceShadowNode, fragment.props)), children_( fragment.children ? fragment.children : sourceShadowNode.children_), state_( fragment.state ? fragment.state : (ReactNativeFeatureFlags::useShadowNodeStateOnClone() ? sourceShadowNode.state_ : sourceShadowNode.getMostRecentState())), orderIndex_(sourceShadowNode.orderIndex_), family_(sourceShadowNode.family_), traits_(sourceShadowNode.traits_) { react_native_assert(props_); react_native_assert(children_); // State could have been progressed above by checking // `sourceShadowNode.getMostRecentState()`. traits_.set(ShadowNodeTraits::Trait::ChildrenAreShared); if (fragment.children) { for (const auto& child : *children_) { child->family_->setParent(family_); } updateTraitsIfNeccessary(); } } std::shared_ptr<ShadowNode> ShadowNode::clone( const ShadowNodeFragment& fragment) const { const auto& family = *family_; const auto& componentDescriptor = family.componentDescriptor_; if (family.nativeProps_DEPRECATED != nullptr) { auto propsParserContext = PropsParserContext{family_->getSurfaceId(), {}}; if (fragment.props == ShadowNodeFragment::propsPlaceholder()) { // Clone existing `props_` with `family.nativeProps_DEPRECATED` to apply // previously set props via `setNativeProps` API. auto props = componentDescriptor.cloneProps( propsParserContext, props_, RawProps(*family.nativeProps_DEPRECATED)); auto clonedNode = componentDescriptor.cloneShadowNode( *this, {.props = props, .children = fragment.children, .state = fragment.state}); return clonedNode; } else { // TODO: We might need to merge fragment.priops with // `family.nativeProps_DEPRECATED`. return componentDescriptor.cloneShadowNode(*this, fragment); } } else { return componentDescriptor.cloneShadowNode(*this, fragment); } } std::shared_ptr<const ContextContainer> ShadowNode::getContextContainer() const { return family_->componentDescriptor_.getContextContainer(); } #pragma mark - Getters ComponentName ShadowNode::getComponentName() const { return family_->getComponentName(); } ComponentHandle ShadowNode::getComponentHandle() const { return family_->getComponentHandle(); } const std::vector<std::shared_ptr<const ShadowNode>>& ShadowNode::getChildren() const { return *children_; } ShadowNodeTraits ShadowNode::getTraits() const { return traits_; } const Props::Shared& ShadowNode::getProps() const { return props_; } const SharedEventEmitter& ShadowNode::getEventEmitter() const { return family_->eventEmitter_; } jsi::Value ShadowNode::getInstanceHandle(jsi::Runtime& runtime) const { return family_->getInstanceHandle(runtime); } Tag ShadowNode::getTag() const { return family_->tag_; } SurfaceId ShadowNode::getSurfaceId() const { return family_->surfaceId_; } const ComponentDescriptor& ShadowNode::getComponentDescriptor() const { return family_->componentDescriptor_; } const State::Shared& ShadowNode::getState() const { return state_; } State::Shared ShadowNode::getMostRecentState() const { return family_->getMostRecentState(); } int ShadowNode::getOrderIndex() const { return orderIndex_; } void ShadowNode::sealRecursive() const { if (getSealed()) { return; } seal(); props_->seal(); for (const auto& child : *children_) { child->sealRecursive(); } } #pragma mark - Mutating Methods void ShadowNode::appendChild(const std::shared_ptr<const ShadowNode>& child) { ensureUnsealed(); cloneChildrenIfShared(); auto& children = const_cast<std::vector<std::shared_ptr<const ShadowNode>>&>(*children_); children.push_back(child); child->family_->setParent(family_); updateTraitsIfNeccessary(); } void ShadowNode::replaceChild( const ShadowNode& oldChild, const std::shared_ptr<const ShadowNode>& newChild, size_t suggestedIndex) { ensureUnsealed(); cloneChildrenIfShared(); newChild->family_->setParent(family_); auto& children = const_cast<std::vector<std::shared_ptr<const ShadowNode>>&>(*children_); auto size = children.size(); if (suggestedIndex != std::numeric_limits<size_t>::max() && suggestedIndex < size) { // If provided `suggestedIndex` is accurate, // replacing in place using the index. if (children.at(suggestedIndex).get() == &oldChild) { children[suggestedIndex] = newChild; return; } } for (size_t index = 0; index < size; index++) { if (children.at(index).get() == &oldChild) { children[index] = newChild; return; } } react_native_assert(false && "Child to replace was not found."); updateTraitsIfNeccessary(); } void ShadowNode::cloneChildrenIfShared() { if (!traits_.check(ShadowNodeTraits::Trait::ChildrenAreShared)) { return; } traits_.unset(ShadowNodeTraits::Trait::ChildrenAreShared); children_ = std::make_shared<std::vector<std::shared_ptr<const ShadowNode>>>( *children_); } void ShadowNode::updateTraitsIfNeccessary() { if (ReactNativeFeatureFlags::enableViewCulling()) { if (traits_.check(ShadowNodeTraits::Trait::Unstable_uncullableView)) { return; } for (const auto& child : *children_) { if (child->getTraits().check( ShadowNodeTraits::Trait::Unstable_uncullableView) || child->getTraits().check( ShadowNodeTraits::Trait::Unstable_uncullableTrace)) { traits_.set(ShadowNodeTraits::Trait::Unstable_uncullableTrace); return; } } traits_.unset(ShadowNodeTraits::Trait::Unstable_uncullableTrace); } } void ShadowNode::setMounted(bool mounted) const { if (mounted) { family_->setMostRecentState(getState()); family_->setMounted(); hasBeenMounted_ = mounted; } family_->eventEmitter_->setEnabled(mounted); } bool ShadowNode::getHasBeenPromoted() const { return hasBeenMounted_.load(); } void ShadowNode::setRuntimeShadowNodeReference( const std::shared_ptr<ShadowNodeWrapper>& runtimeShadowNodeReference) const { runtimeShadowNodeReference_ = runtimeShadowNodeReference; } void ShadowNode::updateRuntimeShadowNodeReference( const std::shared_ptr<const ShadowNode>& destinationShadowNode) const { if (auto reference = runtimeShadowNodeReference_.lock()) { reference->shadowNode = destinationShadowNode; } } void ShadowNode::transferRuntimeShadowNodeReference( const std::shared_ptr<const ShadowNode>& destinationShadowNode) const { destinationShadowNode->runtimeShadowNodeReference_ = runtimeShadowNodeReference_; if (!ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit()) { updateRuntimeShadowNodeReference(destinationShadowNode); } } void ShadowNode::transferRuntimeShadowNodeReference( const std::shared_ptr<const ShadowNode>& destinationShadowNode, const ShadowNodeFragment& fragment) const { if ((ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit() || useRuntimeShadowNodeReferenceUpdateOnThread) && fragment.runtimeShadowNodeReference) { transferRuntimeShadowNodeReference(destinationShadowNode); } } const ShadowNodeFamily& ShadowNode::getFamily() const { return *family_; } ShadowNodeFamily::Shared ShadowNode::getFamilyShared() const { return family_; } std::shared_ptr<ShadowNode> ShadowNode::cloneTree( const ShadowNodeFamily& shadowNodeFamily, const std::function<std::shared_ptr<ShadowNode>( const ShadowNode& oldShadowNode)>& callback) const { auto ancestors = shadowNodeFamily.getAncestors(*this); if (ancestors.empty()) { return std::shared_ptr<ShadowNode>{nullptr}; } auto& parent = ancestors.back(); auto& oldShadowNode = parent.first.get().getChildren().at(parent.second); auto newShadowNode = callback(*oldShadowNode); react_native_assert( newShadowNode && "`callback` returned `nullptr` which is not allowed value."); auto childNode = newShadowNode; for (auto it = ancestors.rbegin(); it != ancestors.rend(); ++it) { auto& parentNode = it->first.get(); auto childIndex = it->second; auto children = parentNode.getChildren(); react_native_assert( ShadowNode::sameFamily(*children.at(childIndex), *childNode)); children[childIndex] = childNode; childNode = parentNode.clone( {.children = std::make_shared<std::vector<std::shared_ptr<const ShadowNode>>>( children)}); } return std::const_pointer_cast<ShadowNode>(childNode); } namespace { std::shared_ptr<ShadowNode> cloneMultipleRecursive( const ShadowNode& shadowNode, const std::unordered_set<const ShadowNodeFamily*>& familiesToUpdate, const std::unordered_map<const ShadowNodeFamily*, int>& childrenCount, const std::function<std::shared_ptr< ShadowNode>(const ShadowNode&, const ShadowNodeFragment&)>& callback) { const auto* family = &shadowNode.getFamily(); auto& children = shadowNode.getChildren(); std::shared_ptr<std::vector<std::shared_ptr<const ShadowNode>>> newChildren; auto count = childrenCount.at(family); for (int i = 0; count > 0 && i < children.size(); i++) { const auto childFamily = &children[i]->getFamily(); if (childrenCount.contains(childFamily)) { count--; if (!newChildren) { newChildren = std::make_shared<std::vector<std::shared_ptr<const ShadowNode>>>( children); } (*newChildren)[i] = cloneMultipleRecursive( *children[i], familiesToUpdate, childrenCount, callback); } } ShadowNodeFragment fragment{.children = newChildren}; if (familiesToUpdate.contains(family)) { return callback(shadowNode, fragment); } return shadowNode.clone(fragment); } } // namespace std::shared_ptr<ShadowNode> ShadowNode::cloneMultiple( const std::unordered_set<const ShadowNodeFamily*>& familiesToUpdate, const std::function<std::shared_ptr<ShadowNode>( const ShadowNode& oldShadowNode, const ShadowNodeFragment& fragment)>& callback) const { std::unordered_map<const ShadowNodeFamily*, int> childrenCount; for (const auto& family : familiesToUpdate) { if (childrenCount.contains(family)) { continue; } childrenCount[family] = 0; auto ancestor = family->parent_.lock(); while ((ancestor != nullptr) && ancestor != family_) { auto ancestorIt = childrenCount.find(ancestor.get()); if (ancestorIt != childrenCount.end()) { ancestorIt->second++; break; } childrenCount[ancestor.get()] = 1; ancestor = ancestor->parent_.lock(); } if (ancestor == family_) { childrenCount[ancestor.get()]++; } } if (childrenCount.empty()) { return nullptr; } return cloneMultipleRecursive( *this, familiesToUpdate, childrenCount, callback); } #pragma mark - DebugStringConvertible #if RN_DEBUG_STRING_CONVERTIBLE std::string ShadowNode::getDebugName() const { return getComponentName(); } std::string ShadowNode::getDebugValue() const { return "r" + std::to_string(revision_) + "/sr" + std::to_string(state_ ? state_->getRevision() : 0) + (getSealed() ? "/sealed" : "") + (getProps()->nativeId.empty() ? "" : "/id=" + getProps()->nativeId); } SharedDebugStringConvertibleList ShadowNode::getDebugChildren() const { auto debugChildren = SharedDebugStringConvertibleList{}; for (const auto& child : *children_) { auto debugChild = std::dynamic_pointer_cast<const DebugStringConvertible>(child); if (debugChild) { debugChildren.push_back(debugChild); } } return debugChildren; } SharedDebugStringConvertibleList ShadowNode::getDebugProps() const { return props_->getDebugProps() + SharedDebugStringConvertibleList{ debugStringConvertibleItem("tag", std::to_string(getTag()))}; } #endif // Explicitly define destructors here, as they have to exist in order to act as // a "key function" for the ShadowNodeWrapper class -- this allows for RTTI to // work properly across dynamic library boundaries (i.e. dynamic_cast that is // used by getNativeState method) ShadowNodeWrapper::~ShadowNodeWrapper() = default; } // namespace facebook::react