From 89fa5976e6f8e017cae03c0fde809c3669acf0d9 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 7 Aug 2026 02:47:41 -0700 Subject: [PATCH] Avoid copying surface props when starting or updating a surface (#57813) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: `SurfaceHandler` already takes a throwaway snapshot of its `Parameters` under `parametersMutex_` before handing them to the `UIManager`, but `UIManager::startSurface` and `UIManager::setSurfaceProps` took `moduleName` and `props` by const reference and then copy-captured them into the lambda posted to the `RuntimeExecutor`. That forced a second deep copy of the props tree — which for a real surface holds the initial route params and deep link data — on every surface start, prop update, and display mode change. Take both by value and move them into the lambda, and move at the `SurfaceHandler` call sites, so the snapshot is handed off instead of duplicated. The snapshot is a local that is dead after the call, so there is nothing left to observe the moved-from state. Changelog: [General][Changed] - `UIManager::startSurface` and `UIManager::setSurfaceProps` now take `moduleName` and `props` by value Reviewed By: zeyap, christophpurrer Differential Revision: D114730310 --- .../renderer/scheduler/SurfaceHandler.cpp | 12 ++++++------ .../react/renderer/uimanager/UIManager.cpp | 18 ++++++++++++------ .../react/renderer/uimanager/UIManager.h | 11 ++++------- .../api-snapshots/ReactAndroidDebugCxx.api | 4 ++-- .../api-snapshots/ReactAndroidNewarchCxx.api | 4 ++-- .../api-snapshots/ReactAndroidReleaseCxx.api | 4 ++-- .../api-snapshots/ReactAppleDebugCxx.api | 4 ++-- .../api-snapshots/ReactAppleNewarchCxx.api | 4 ++-- .../api-snapshots/ReactAppleReleaseCxx.api | 4 ++-- .../api-snapshots/ReactCommonDebugCxx.api | 4 ++-- .../api-snapshots/ReactCommonNewarchCxx.api | 4 ++-- .../api-snapshots/ReactCommonReleaseCxx.api | 4 ++-- 12 files changed, 40 insertions(+), 37 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp index 930472f096e9..c69fc641ae0b 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp @@ -64,8 +64,8 @@ void SurfaceHandler::start() const noexcept { if (!parameters.moduleName.empty()) { link_.uiManager->startSurface( std::move(shadowTree), - parameters.moduleName, - parameters.props, + std::move(parameters.moduleName), + std::move(parameters.props), parameters_.displayMode); } else { link_.uiManager->startEmptySurface(std::move(shadowTree)); @@ -118,8 +118,8 @@ void SurfaceHandler::setDisplayMode(DisplayMode displayMode) const noexcept { link_.uiManager->setSurfaceProps( parameters.surfaceId, - parameters.moduleName, - parameters.props, + std::move(parameters.moduleName), + std::move(parameters.props), parameters.displayMode); applyDisplayMode(displayMode); @@ -164,8 +164,8 @@ void SurfaceHandler::setProps(const folly::dynamic& props) const noexcept { if (link_.status == Status::Running) { link_.uiManager->setSurfaceProps( parameters.surfaceId, - parameters.moduleName, - parameters.props, + std::move(parameters.moduleName), + std::move(parameters.props), parameters.displayMode); } } diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp index 0c35031aa5db..6b7c66679419 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -234,8 +234,8 @@ void UIManager::setIsJSResponder( void UIManager::startSurface( ShadowTree::Unique&& shadowTree, - const std::string& moduleName, - const folly::dynamic& props, + std::string moduleName, + folly::dynamic props, DisplayMode displayMode) const noexcept { TraceSection s("UIManager::startSurface"); @@ -249,7 +249,10 @@ void UIManager::startSurface( } }); - runtimeExecutor_([=](jsi::Runtime& runtime) { + runtimeExecutor_([surfaceId, + moduleName = std::move(moduleName), + props = std::move(props), + displayMode](jsi::Runtime& runtime) { TraceSection s("UIManager::startSurface::onRuntime"); AppRegistryBinding::startSurface( runtime, surfaceId, moduleName, props, displayMode); @@ -264,12 +267,15 @@ void UIManager::startEmptySurface( void UIManager::setSurfaceProps( SurfaceId surfaceId, - const std::string& moduleName, - const folly::dynamic& props, + std::string moduleName, + folly::dynamic props, DisplayMode displayMode) const noexcept { TraceSection s("UIManager::setSurfaceProps"); - runtimeExecutor_([=](jsi::Runtime& runtime) { + runtimeExecutor_([surfaceId, + moduleName = std::move(moduleName), + props = std::move(props), + displayMode](jsi::Runtime& runtime) { AppRegistryBinding::setSurfaceProps( runtime, surfaceId, moduleName, props, displayMode); }); diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h index 95468f1370e9..f7452c8defe6 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h @@ -116,17 +116,14 @@ class UIManager final : public ShadowTreeDelegate { void startSurface( ShadowTree::Unique &&shadowTree, - const std::string &moduleName, - const folly::dynamic &props, + std::string moduleName, + folly::dynamic props, DisplayMode displayMode) const noexcept; void startEmptySurface(ShadowTree::Unique &&shadowTree) const noexcept; - void setSurfaceProps( - SurfaceId surfaceId, - const std::string &moduleName, - const folly::dynamic &props, - DisplayMode displayMode) const noexcept; + void setSurfaceProps(SurfaceId surfaceId, std::string moduleName, folly::dynamic props, DisplayMode displayMode) + const noexcept; ShadowTree::Unique stopSurface(SurfaceId surfaceId) const; diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api index 3551f220d2d3..7c3645371e9d 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api @@ -5295,10 +5295,10 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void setDelegate(facebook::react::UIManagerDelegate* delegate); public void setIsJSResponder(const std::shared_ptr& shadowNode, bool isJSResponder, bool blockNativeResponder) const; public void setNativeAnimatedDelegate(std::weak_ptr delegate); - public void setSurfaceProps(facebook::react::SurfaceId surfaceId, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void setSurfaceProps(facebook::react::SurfaceId surfaceId, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void setViewTransitionDelegate(facebook::react::UIManagerViewTransitionDelegate* delegate); public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; - public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api index 00497f9fbdd8..e6a21f9eea65 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api @@ -5106,10 +5106,10 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void setDelegate(facebook::react::UIManagerDelegate* delegate); public void setIsJSResponder(const std::shared_ptr& shadowNode, bool isJSResponder, bool blockNativeResponder) const; public void setNativeAnimatedDelegate(std::weak_ptr delegate); - public void setSurfaceProps(facebook::react::SurfaceId surfaceId, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void setSurfaceProps(facebook::react::SurfaceId surfaceId, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void setViewTransitionDelegate(facebook::react::UIManagerViewTransitionDelegate* delegate); public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; - public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api index 96738999812a..29ee35199c2e 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -5286,10 +5286,10 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void setDelegate(facebook::react::UIManagerDelegate* delegate); public void setIsJSResponder(const std::shared_ptr& shadowNode, bool isJSResponder, bool blockNativeResponder) const; public void setNativeAnimatedDelegate(std::weak_ptr delegate); - public void setSurfaceProps(facebook::react::SurfaceId surfaceId, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void setSurfaceProps(facebook::react::SurfaceId surfaceId, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void setViewTransitionDelegate(facebook::react::UIManagerViewTransitionDelegate* delegate); public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; - public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api index 2f0b741dc71c..6f5cc9024254 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api @@ -7469,10 +7469,10 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void setDelegate(facebook::react::UIManagerDelegate* delegate); public void setIsJSResponder(const std::shared_ptr& shadowNode, bool isJSResponder, bool blockNativeResponder) const; public void setNativeAnimatedDelegate(std::weak_ptr delegate); - public void setSurfaceProps(facebook::react::SurfaceId surfaceId, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void setSurfaceProps(facebook::react::SurfaceId surfaceId, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void setViewTransitionDelegate(facebook::react::UIManagerViewTransitionDelegate* delegate); public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; - public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api index 45383f4e39d4..fb3e95955510 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api @@ -7308,10 +7308,10 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void setDelegate(facebook::react::UIManagerDelegate* delegate); public void setIsJSResponder(const std::shared_ptr& shadowNode, bool isJSResponder, bool blockNativeResponder) const; public void setNativeAnimatedDelegate(std::weak_ptr delegate); - public void setSurfaceProps(facebook::react::SurfaceId surfaceId, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void setSurfaceProps(facebook::react::SurfaceId surfaceId, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void setViewTransitionDelegate(facebook::react::UIManagerViewTransitionDelegate* delegate); public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; - public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api index 6d98f870e97c..fdf57989cc46 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -7460,10 +7460,10 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void setDelegate(facebook::react::UIManagerDelegate* delegate); public void setIsJSResponder(const std::shared_ptr& shadowNode, bool isJSResponder, bool blockNativeResponder) const; public void setNativeAnimatedDelegate(std::weak_ptr delegate); - public void setSurfaceProps(facebook::react::SurfaceId surfaceId, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void setSurfaceProps(facebook::react::SurfaceId surfaceId, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void setViewTransitionDelegate(facebook::react::UIManagerViewTransitionDelegate* delegate); public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; - public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api index 0c7d0a5b8a1c..6fc08ebfbe03 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api @@ -3746,10 +3746,10 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void setDelegate(facebook::react::UIManagerDelegate* delegate); public void setIsJSResponder(const std::shared_ptr& shadowNode, bool isJSResponder, bool blockNativeResponder) const; public void setNativeAnimatedDelegate(std::weak_ptr delegate); - public void setSurfaceProps(facebook::react::SurfaceId surfaceId, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void setSurfaceProps(facebook::react::SurfaceId surfaceId, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void setViewTransitionDelegate(facebook::react::UIManagerViewTransitionDelegate* delegate); public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; - public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api index 4b6b80e8c2b8..7a924c02b7a1 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api @@ -3597,10 +3597,10 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void setDelegate(facebook::react::UIManagerDelegate* delegate); public void setIsJSResponder(const std::shared_ptr& shadowNode, bool isJSResponder, bool blockNativeResponder) const; public void setNativeAnimatedDelegate(std::weak_ptr delegate); - public void setSurfaceProps(facebook::react::SurfaceId surfaceId, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void setSurfaceProps(facebook::react::SurfaceId surfaceId, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void setViewTransitionDelegate(facebook::react::UIManagerViewTransitionDelegate* delegate); public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; - public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api index a0e1c10862b6..7b4a17dc670c 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api @@ -3737,10 +3737,10 @@ class facebook::react::UIManager : public facebook::react::ShadowTreeDelegate { public void setDelegate(facebook::react::UIManagerDelegate* delegate); public void setIsJSResponder(const std::shared_ptr& shadowNode, bool isJSResponder, bool blockNativeResponder) const; public void setNativeAnimatedDelegate(std::weak_ptr delegate); - public void setSurfaceProps(facebook::react::SurfaceId surfaceId, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void setSurfaceProps(facebook::react::SurfaceId surfaceId, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void setViewTransitionDelegate(facebook::react::UIManagerViewTransitionDelegate* delegate); public void startEmptySurface(facebook::react::ShadowTree::Unique&& shadowTree) const noexcept; - public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, const std::string& moduleName, const folly::dynamic& props, facebook::react::DisplayMode displayMode) const noexcept; + public void startSurface(facebook::react::ShadowTree::Unique&& shadowTree, std::string moduleName, folly::dynamic props, facebook::react::DisplayMode displayMode) const noexcept; public void stopSurfaceForAnimationDelegate(facebook::react::SurfaceId surfaceId) const; public void synchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic& props); public void unregisterCommitHook(facebook::react::UIManagerCommitHook& commitHook);