From 32615b91b2e49710d312489d9ccf7e4088de8581 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 17 Nov 2025 06:41:11 -0800 Subject: [PATCH] Restructure core dependencies to avoid cxxreact and fabric (#54446) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/54446 Changelog: [Internal] Differential Revision: D86515326 --- packages/react-native/Package.swift | 44 +++--- .../react/uimanager/TransformHelper.kt | 4 + .../src/main/jni/react/jni/OnLoad-common.cpp | 2 - .../src/main/jni/react/jni/OnLoad.cpp | 2 + .../ReactCommon/React-Bridging.podspec | 51 +++++++ ...mmon.podspec => React-TurboModule.podspec} | 32 ++--- .../ReactCommon/cxxreact/ErrorUtils.h | 34 +---- .../ReactCommon/cxxreact/Instance.cpp | 6 +- .../ReactCommon/cxxreact/MoveWrapper.h | 123 +--------------- .../ReactCommon/cxxreact/NativeToJsBridge.cpp | 4 +- .../cxxreact/React-cxxreact.podspec | 1 + .../ReactCommon/jserrorhandler/ErrorUtils.cpp | 42 ++++++ .../ReactCommon/jserrorhandler/ErrorUtils.h | 16 +++ .../jserrorhandler/JsErrorHandler.cpp | 4 +- .../React-jserrorhandler.podspec | 3 +- .../jsiexecutor/jsireact/JSIExecutor.cpp | 2 +- .../android/ReactCommon/JavaTurboModule.cpp | 2 +- .../ios/React-NativeModulesApple.podspec | 2 +- .../React-runtimescheduler.podspec | 1 + .../runtimescheduler/RuntimeScheduler.cpp | 2 +- .../react/runtime/ReactInstance.cpp | 3 +- .../ReactCommon/react/utils/MoveWrapper.h | 131 ++++++++++++++++++ .../cocoapods/__tests__/codegen_utils-test.rb | 2 +- .../__tests__/new_architecture-test.rb | 4 +- .../scripts/cocoapods/new_architecture.rb | 2 +- .../generate-artifacts-executor-test.js.snap | 30 ++-- .../templates/ReactCodegen.podspec.template | 2 +- .../react-native/scripts/react_native_pods.rb | 2 +- 28 files changed, 319 insertions(+), 234 deletions(-) create mode 100644 packages/react-native/ReactCommon/React-Bridging.podspec rename packages/react-native/ReactCommon/{ReactCommon.podspec => React-TurboModule.podspec} (80%) create mode 100644 packages/react-native/ReactCommon/jserrorhandler/ErrorUtils.cpp create mode 100644 packages/react-native/ReactCommon/jserrorhandler/ErrorUtils.h create mode 100644 packages/react-native/ReactCommon/react/utils/MoveWrapper.h diff --git a/packages/react-native/Package.swift b/packages/react-native/Package.swift index d95d1c6a1d3c..ad9d82f910a4 100644 --- a/packages/react-native/Package.swift +++ b/packages/react-native/Package.swift @@ -96,6 +96,7 @@ let reactDebug = RNTarget( path: "ReactCommon/react/debug", dependencies: [.reactNativeDependencies] ) + /// React-jsi.podspec let jsi = RNTarget( name: .jsi, @@ -192,13 +193,29 @@ let reactJsInspector = RNTarget( ] ) +/// React-Bridging.podspec +let reactTurboModuleBridging = RNTarget( + name: .reactTurboModuleBridging, + path: "ReactCommon/react/bridging", + excludedPaths: ["tests"], + dependencies: [.reactNativeDependencies, .reactPerfLogger, .jsi] +) + +/// React-jserrorhandler.podspec +let reactJsErrorHandler = RNTarget( + name: .reactJsErrorHandler, + path: "ReactCommon/jserrorhandler", + excludedPaths: ["tests"], + dependencies: [.reactNativeDependencies, .jsi, .reactFeatureFlags, .reactDebug, .reactTurboModuleBridging] +) + /// React-cxxreact.podspec let reactCxxReact = RNTarget( name: .reactCxxReact, path: "ReactCommon/cxxreact", searchPaths: [CallInvokerPath], excludedPaths: ["tests"], - dependencies: [.reactNativeDependencies, .jsi, .reactPerfLogger, .logger, .reactDebug, .reactJsInspector] + dependencies: [.reactNativeDependencies, .jsi, .reactJsErrorHandler, .reactPerfLogger, .logger, .reactDebug, .reactJsInspector] ) /// React-jsitooling.podspec @@ -243,7 +260,7 @@ let reactPerformanceCdpMetrics = RNTarget( name: .reactPerformanceCdpMetrics, path: "ReactCommon/react/performance/cdpmetrics", excludedPaths: ["tests"], - dependencies: [.reactNativeDependencies, .reactCxxReact, .jsi, .reactPerformanceTimeline, .reactRuntimeExecutor] + dependencies: [.reactNativeDependencies, .jsi, .reactPerformanceTimeline, .reactRuntimeExecutor] ) /// React-performancetimeline.podspec @@ -251,7 +268,7 @@ let reactPerformanceTimeline = RNTarget( name: .reactPerformanceTimeline, path: "ReactCommon/react/performance/timeline", excludedPaths: ["tests"], - dependencies: [.reactNativeDependencies, .reactFeatureFlags, .reactJsInspectorTracing, .reactCxxReact, .reactPerfLogger] + dependencies: [.reactNativeDependencies, .reactFeatureFlags, .reactJsInspectorTracing, .reactPerfLogger] ) /// React-runtimescheduler.podspec @@ -259,24 +276,7 @@ let reactRuntimeScheduler = RNTarget( name: .reactRuntimeScheduler, path: "ReactCommon/react/renderer/runtimescheduler", excludedPaths: ["tests"], - dependencies: [.reactNativeDependencies, .reactFeatureFlags, .reactCxxReact, .reactPerfLogger, .reactPerformanceTimeline, .reactRendererConsistency, .reactUtils, .reactRuntimeExecutor] -) - -/// ReactCommon.podspec -/// This target represent the ReactCommon/turbomodule/bridging subspec -let reactTurboModuleBridging = RNTarget( - name: .reactTurboModuleBridging, - path: "ReactCommon/react/bridging", - excludedPaths: ["tests"], - dependencies: [.reactNativeDependencies, .reactPerfLogger, .reactCxxReact, .jsi, .logger] -) - -/// React-jserrorhandler.podspec -let reactJsErrorHandler = RNTarget( - name: .reactJsErrorHandler, - path: "ReactCommon/jserrorhandler", - excludedPaths: ["tests"], - dependencies: [.reactNativeDependencies, .jsi, .reactCxxReact, .reactFeatureFlags, .reactDebug, .reactTurboModuleBridging] + dependencies: [.reactNativeDependencies, .reactJsErrorHandler, .reactFeatureFlags, .reactCxxReact, .reactPerfLogger, .reactPerformanceTimeline, .reactRendererConsistency, .reactUtils, .reactRuntimeExecutor] ) /// React-graphicsApple @@ -871,7 +871,7 @@ extension String { static let reactRCTActionSheet = "React-RCTActionSheet" // Empty target static let reactRCTLinking = "React-RCTLinking" static let reactCoreModules = "React-CoreModules" - static let reactTurboModuleBridging = "ReactCommon/turbomodule/bridging" + static let reactTurboModuleBridging = "React-Bridging" static let reactTurboModuleCore = "ReactCommon/turbomodule/core" static let reactTurboModuleCoreDefaults = "ReactCommon/turbomodule/core/defaults" static let reactTurboModuleCoreMicrotasks = "ReactCommon/turbomodule/core/microtasks" diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.kt index 554f40516802..980110cd7b18 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.kt @@ -9,6 +9,7 @@ package com.facebook.react.uimanager import com.facebook.common.logging.FLog import com.facebook.react.bridge.NativeArray +import com.facebook.react.bridge.ReactNativeJNISoLoader import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.ReadableType @@ -16,6 +17,9 @@ import com.facebook.react.common.ReactConstants import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags public object TransformHelper { + init { + ReactNativeJNISoLoader.staticInit() + } private val helperMatrix: ThreadLocal = object : ThreadLocal() { diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/OnLoad-common.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/jni/OnLoad-common.cpp index ef069ed7e41d..293d2ffc8abf 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/OnLoad-common.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/OnLoad-common.cpp @@ -11,7 +11,6 @@ #include "JReactMarker.h" #include "NativeArray.h" #include "NativeMap.h" -#include "TransformHelper.h" #include "WritableNativeArray.h" #include "WritableNativeMap.h" @@ -28,7 +27,6 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { ReadableNativeMap::registerNatives(); WritableNativeArray::registerNatives(); WritableNativeMap::registerNatives(); - TransformHelper::registerNatives(); }); } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp index b1a66674505e..e0972505205b 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp @@ -14,6 +14,7 @@ #include "InspectorNetworkRequestListener.h" #include "JavaScriptExecutorHolder.h" #include "ReactInstanceManagerInspectorTarget.h" +#include "TransformHelper.h" #ifndef WITH_GLOGINIT #define WITH_GLOGINIT 1 @@ -43,6 +44,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { #endif ReactInstanceManagerInspectorTarget::registerNatives(); InspectorNetworkRequestListener::registerNatives(); + TransformHelper::registerNatives(); }); } diff --git a/packages/react-native/ReactCommon/React-Bridging.podspec b/packages/react-native/ReactCommon/React-Bridging.podspec new file mode 100644 index 000000000000..b4d5223b021b --- /dev/null +++ b/packages/react-native/ReactCommon/React-Bridging.podspec @@ -0,0 +1,51 @@ +# 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. + +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "..", "package.json"))) +version = package['version'] + +source = { :git => 'https://github.com/facebook/react-native.git' } +if version == '1000.0.0' + # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") +else + source[:tag] = "v#{version}" +end + +Pod::Spec.new do |s| + s.name = "React-Bridging" + s.module_name = "React-Bridging" + s.version = version + s.summary = "-" # TODO + s.homepage = "https://reactnative.dev/" + s.license = package["license"] + s.author = "Meta Platforms, Inc. and its affiliates" + s.platforms = min_supported_versions + s.source = source + s.header_dir = "ReactCommon" # Use global header_dir for all subspecs for use_frameworks! compatibility + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Headers/Private/React-Core\"", + "USE_HEADERMAP" => "YES", + "DEFINES_MODULE" => "YES", + "CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(), + "GCC_WARN_PEDANTIC" => "YES" } + + resolve_use_frameworks(s, header_mappings_dir: './') + + add_rn_third_party_dependencies(s) + + s.source_files = podspec_sources("react/bridging/**/*.{cpp,h}", "react/bridging/**/*.h") + s.exclude_files = "react/bridging/tests" + s.header_dir = "react/bridging" + s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\"" } + + s.dependency "React-jsi", version + s.dependency "React-callinvoker", version + s.dependency "React-perflogger", version + if use_hermes() + s.dependency "hermes-engine" + end +end diff --git a/packages/react-native/ReactCommon/ReactCommon.podspec b/packages/react-native/ReactCommon/React-TurboModule.podspec similarity index 80% rename from packages/react-native/ReactCommon/ReactCommon.podspec rename to packages/react-native/ReactCommon/React-TurboModule.podspec index 83e81864640d..271c4ac0a803 100644 --- a/packages/react-native/ReactCommon/ReactCommon.podspec +++ b/packages/react-native/ReactCommon/React-TurboModule.podspec @@ -41,32 +41,24 @@ Pod::Spec.new do |s| # TODO (T48588859): Restructure this target to align with dir structure: "react/nativemodule/..." # Note: Update this only when ready to minimize breaking changes. s.subspec "turbomodule" do |ss| - ss.dependency "React-callinvoker", version - ss.dependency "React-perflogger", version - ss.dependency "React-cxxreact", version - ss.dependency "React-jsi", version - ss.dependency "React-logger", version - if use_hermes() - ss.dependency "hermes-engine" - end - - ss.subspec "bridging" do |sss| - sss.dependency "React-jsi", version - sss.source_files = podspec_sources("react/bridging/**/*.{cpp,h}", "react/bridging/**/*.h") - sss.exclude_files = "react/bridging/tests" - sss.header_dir = "react/bridging" - sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\"" } - if use_hermes() - sss.dependency "hermes-engine" - end - end - ss.subspec "core" do |sss| sss.source_files = podspec_sources("react/nativemodule/core/ReactCommon/**/*.{cpp,h}", "react/nativemodule/core/ReactCommon/**/*.h") sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_CONFIGURATION_BUILD_DIR)/React-debug/React_debug.framework/Headers\" \"$(PODS_CONFIGURATION_BUILD_DIR)/React-debug/React_featureflags.framework/Headers\" \"$(PODS_CONFIGURATION_BUILD_DIR)/React-utils/React_utils.framework/Headers\"" } + + sss.dependency "React-cxxreact", version sss.dependency "React-debug", version sss.dependency "React-featureflags", version + sss.dependency "React-logger", version + sss.dependency "React-jsi", version sss.dependency "React-utils", version + if use_hermes() + sss.dependency "hermes-engine" + end + end + + # Provided for backwards compat, use React-Bridging instead + ss.subspec "bridging" do |sss| + sss.dependency "React-Bridging", version end end end diff --git a/packages/react-native/ReactCommon/cxxreact/ErrorUtils.h b/packages/react-native/ReactCommon/cxxreact/ErrorUtils.h index 5d9d5db36827..35f445115074 100644 --- a/packages/react-native/ReactCommon/cxxreact/ErrorUtils.h +++ b/packages/react-native/ReactCommon/cxxreact/ErrorUtils.h @@ -7,36 +7,6 @@ #pragma once -#include +#warning Deprecated: use instead. -namespace facebook::react { - -inline static void handleJSError(jsi::Runtime &runtime, const jsi::JSError &error, bool isFatal) -{ - auto errorUtils = runtime.global().getProperty(runtime, "ErrorUtils"); - if (errorUtils.isUndefined() || !errorUtils.isObject() || - !errorUtils.getObject(runtime).hasProperty(runtime, "reportFatalError") || - !errorUtils.getObject(runtime).hasProperty(runtime, "reportError")) { - // ErrorUtils was not set up. This probably means the bundle didn't - // load properly. - throw jsi::JSError( - runtime, - "ErrorUtils is not set up properly. Something probably went wrong trying to load the JS bundle. Trying to report error " + - error.getMessage(), - error.getStack()); - } - - // TODO(janzer): Rewrite this function to return the processed error - // instead of just reporting it through the native module - if (isFatal) { - auto func = errorUtils.asObject(runtime).getPropertyAsFunction(runtime, "reportFatalError"); - - func.call(runtime, error.value()); - } else { - auto func = errorUtils.asObject(runtime).getPropertyAsFunction(runtime, "reportError"); - - func.call(runtime, error.value()); - } -} - -} // namespace facebook::react +#include diff --git a/packages/react-native/ReactCommon/cxxreact/Instance.cpp b/packages/react-native/ReactCommon/cxxreact/Instance.cpp index d61f049076eb..166ba6e2fd7c 100644 --- a/packages/react-native/ReactCommon/cxxreact/Instance.cpp +++ b/packages/react-native/ReactCommon/cxxreact/Instance.cpp @@ -9,15 +9,12 @@ #ifndef RCT_REMOVE_LEGACY_ARCH -#include "ErrorUtils.h" +#include #include "JSBigString.h" -#include "JSBundleType.h" #include "JSExecutor.h" #include "MessageQueueThread.h" -#include "MethodCall.h" #include "NativeToJsBridge.h" #include "RAMBundleRegistry.h" -#include "RecoverableError.h" #include "TraceSection.h" #include @@ -27,7 +24,6 @@ #include #include -#include #include #include #include diff --git a/packages/react-native/ReactCommon/cxxreact/MoveWrapper.h b/packages/react-native/ReactCommon/cxxreact/MoveWrapper.h index 22a9a0f238f4..217f5e4d89d8 100644 --- a/packages/react-native/ReactCommon/cxxreact/MoveWrapper.h +++ b/packages/react-native/ReactCommon/cxxreact/MoveWrapper.h @@ -7,125 +7,6 @@ #pragma once -#include +#warning Deprecated. Use instead. -namespace facebook::react { - -/* -NOTE: we keep this internal copy of folly/MoveWrapper.h to unblock -the the workstream of dropping the dependency on folly in RN! - -For a technical explanation on why we still need this we defer -to the doc in folly/Function.h: - -"There are some limitations in std::function that folly::Function tries to -avoid. std::function is copy-constructible and requires that the callable that -it wraps is copy-constructible as well, which is a constraint that is often -inconvenient. In most cases when using a std::function you don't make use of -its copy-constructibility, so you might sometimes feel like you get back very -little in return for a noticeable restriction. This restriction becomes -apparent when trying to use a lambda capturing a unique_ptr (or any -non-copyable type) as a callback for a folly::Future. - -std::unique_ptr foo_ptr = new Foo; - -some_future.then( - [foo_ptr = std::move(foo_ptr)] mutable - (int x) - { foo_ptr->setX(x); } -); - -This piece of code did not compile before folly::Future started using -folly::Function instead of std::function to store the callback. Because the -lambda captures something non-copyable (the unique_ptr), it is not copyable -itself. And std::function can only store copyable callables. - -The implementation of folly::Future did not make use of the -copy-constructibility of std::function at any point. There was no benefit from -the fact that the std::function is copy-constructible, but the fact that it can -only wrap copy-constructible callables posed a restriction. - -A workaround was available: folly::MoveWrapper, which wraps an object that may -be non-copyable and implements copy operations by moving the embedded object. -Using a folly::MoveWrapper, you can capture non-copyable objects in a lambda, -and the lambda itself is still copyable and may be wrapped in a std::function. -It is a pragmatic solution for the above problem, but you have to be a little -careful. The problem is that you can’t use a MoveWrapper anywhere where copy -operations are assumed to behave like actual copy operations. Also, a -folly::MoveWrapper> essentially behaves like auto_ptr. Ask -yourself whether you’d want to use lots of auto_ptrs in your codebase. And the -original question still persists: we very often don’t benefit from -copy-constructibility of std::function, so why do we have to live with this -restriction? I.e. why do we have to use MoveWrapper?" -*/ - -/** C++11 closures don't support move-in capture. Nor does std::bind. - facepalm. - - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3610.html - - "[...] a work-around that should make people's stomach crawl: - write a wrapper that performs move-on-copy, much like the deprecated - auto_ptr" - - Unlike auto_ptr, this doesn't require a heap allocation. - */ -template -class MoveWrapper { - public: - /** If value can be default-constructed, why not? - Then we don't have to move it in */ - MoveWrapper() = default; - - /// Move a value in. - explicit MoveWrapper(T &&t) : value(std::move(t)) {} - - /// copy is move - MoveWrapper(const MoveWrapper &other) : value(std::move(other.value)) {} - - /// move is also move - MoveWrapper(MoveWrapper &&other) noexcept : value(std::move(other.value)) {} - - const T &operator*() const - { - return value; - } - T &operator*() - { - return value; - } - - const T *operator->() const - { - return &value; - } - T *operator->() - { - return &value; - } - - /// move the value out (sugar for std::move(*moveWrapper)) - T &&move() - { - return std::move(value); - } - - // If you want these you're probably doing it wrong, though they'd be - // easy enough to implement - MoveWrapper &operator=(const MoveWrapper &) = delete; - MoveWrapper &operator=(MoveWrapper &&) = delete; - - private: - mutable T value; -}; - -/// Make a MoveWrapper from the argument. Because the name "makeMoveWrapper" -/// is already quite transparent in its intent, this will work for lvalues as -/// if you had wrapped them in std::move. -template ::type> -MoveWrapper makeMoveWrapper(T &&t) -{ - return MoveWrapper(std::forward(t)); -} - -} // namespace facebook::react +#include diff --git a/packages/react-native/ReactCommon/cxxreact/NativeToJsBridge.cpp b/packages/react-native/ReactCommon/cxxreact/NativeToJsBridge.cpp index 4f39102bb4ed..2ff380df4f4e 100644 --- a/packages/react-native/ReactCommon/cxxreact/NativeToJsBridge.cpp +++ b/packages/react-native/ReactCommon/cxxreact/NativeToJsBridge.cpp @@ -12,16 +12,16 @@ #include #include #include +#include #include +#include #include -#include "ErrorUtils.h" #include "Instance.h" #include "JSBigString.h" #include "MessageQueueThread.h" #include "MethodCall.h" #include "ModuleRegistry.h" -#include "MoveWrapper.h" #include "RAMBundleRegistry.h" #include "TraceSection.h" diff --git a/packages/react-native/ReactCommon/cxxreact/React-cxxreact.podspec b/packages/react-native/ReactCommon/cxxreact/React-cxxreact.podspec index 59fbcf3c593e..ee899bc320cc 100644 --- a/packages/react-native/ReactCommon/cxxreact/React-cxxreact.podspec +++ b/packages/react-native/ReactCommon/cxxreact/React-cxxreact.podspec @@ -40,6 +40,7 @@ Pod::Spec.new do |s| s.dependency "React-callinvoker", version add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) s.dependency "React-perflogger", version + s.dependency "React-jserrorhandler", version s.dependency "React-jsi", version s.dependency "React-logger", version s.dependency "React-debug", version diff --git a/packages/react-native/ReactCommon/jserrorhandler/ErrorUtils.cpp b/packages/react-native/ReactCommon/jserrorhandler/ErrorUtils.cpp new file mode 100644 index 000000000000..91f951e21210 --- /dev/null +++ b/packages/react-native/ReactCommon/jserrorhandler/ErrorUtils.cpp @@ -0,0 +1,42 @@ +/* + * 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 "ErrorUtils.h" + +namespace facebook::react { + +void handleJSError( + jsi::Runtime& runtime, + const jsi::JSError& error, + bool isFatal) { + auto errorUtils = runtime.global().getProperty(runtime, "ErrorUtils"); + if (errorUtils.isUndefined() || !errorUtils.isObject() || + !errorUtils.getObject(runtime).hasProperty(runtime, "reportFatalError") || + !errorUtils.getObject(runtime).hasProperty(runtime, "reportError")) { + // ErrorUtils was not set up. This probably means the bundle didn't + // load properly. + throw jsi::JSError( + runtime, + "ErrorUtils is not set up properly. Something probably went wrong trying to load the JS bundle. Trying to report error " + + error.getMessage(), + error.getStack()); + } + + // TODO(janzer): Rewrite this function to return the processed error + // instead of just reporting it through the native module + if (isFatal) { + auto func = errorUtils.asObject(runtime).getPropertyAsFunction( + runtime, "reportFatalError"); + func.call(runtime, error.value()); + } else { + auto func = errorUtils.asObject(runtime).getPropertyAsFunction( + runtime, "reportError"); + func.call(runtime, error.value()); + } +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/jserrorhandler/ErrorUtils.h b/packages/react-native/ReactCommon/jserrorhandler/ErrorUtils.h new file mode 100644 index 000000000000..f71377e3a9a6 --- /dev/null +++ b/packages/react-native/ReactCommon/jserrorhandler/ErrorUtils.h @@ -0,0 +1,16 @@ +/* + * 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 + +namespace facebook::react { + +void handleJSError(jsi::Runtime &runtime, const jsi::JSError &error, bool isFatal); + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp b/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp index 3ed51ff06c46..cf022c2d3516 100644 --- a/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp +++ b/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp @@ -6,11 +6,13 @@ */ #include "JsErrorHandler.h" -#include + #include #include #include #include + +#include "ErrorUtils.h" #include "StackTraceParser.h" using namespace facebook; diff --git a/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec b/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec index a5cc33ecc99a..99152a31d419 100644 --- a/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec +++ b/packages/react-native/ReactCommon/jserrorhandler/React-jserrorhandler.podspec @@ -37,8 +37,7 @@ Pod::Spec.new do |s| resolve_use_frameworks(s, header_mappings_dir: '../', module_name: "React_jserrorhandler") s.dependency "React-jsi" - s.dependency "React-cxxreact" - s.dependency "ReactCommon/turbomodule/bridging" + s.dependency "React-Bridging" add_dependency(s, "React-featureflags") add_dependency(s, "React-debug") diff --git a/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp b/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp index 5c16909b002c..5975b468a6d5 100644 --- a/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp +++ b/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp @@ -7,13 +7,13 @@ #include "jsireact/JSIExecutor.h" -#include #include #include #include #include #include #include +#include #include #include #include diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp b/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp index 5c9b464a8c3a..6b5c024a0923 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp @@ -8,11 +8,11 @@ #include #include -#include #include #include #include #include +#include #include #include diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/React-NativeModulesApple.podspec b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/React-NativeModulesApple.podspec index 7fd2869614b6..d90054aa2210 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/React-NativeModulesApple.podspec +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/React-NativeModulesApple.podspec @@ -37,7 +37,7 @@ Pod::Spec.new do |s| s.source_files = podspec_sources("ReactCommon/**/*.{mm,cpp,h}", "ReactCommon/**/*.{h}") s.dependency "ReactCommon/turbomodule/core" - s.dependency "ReactCommon/turbomodule/bridging" + s.dependency "React-Bridging" s.dependency "React-callinvoker" s.dependency "React-Core" s.dependency "React-cxxreact" diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec index 56823e61d623..864bd1919ee2 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec @@ -47,6 +47,7 @@ Pod::Spec.new do |s| s.dependency "React-utils" s.dependency "React-featureflags" s.dependency "React-timing" + s.dependency "React-jserrorhandler" s.dependency "React-jsi" s.dependency "React-performancetimeline" s.dependency "React-rendererconsistency" diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp index f7393dbdd520..0ae806bae8d4 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp @@ -9,8 +9,8 @@ #include "RuntimeScheduler_Legacy.h" #include "RuntimeScheduler_Modern.h" -#include #include +#include #include #include diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp index 8e1dfd2fa0ee..96ade0c64e3e 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp @@ -8,12 +8,11 @@ #include "ReactInstance.h" #include -#include #include -#include #include #include #include +#include #include #include #include diff --git a/packages/react-native/ReactCommon/react/utils/MoveWrapper.h b/packages/react-native/ReactCommon/react/utils/MoveWrapper.h new file mode 100644 index 000000000000..22a9a0f238f4 --- /dev/null +++ b/packages/react-native/ReactCommon/react/utils/MoveWrapper.h @@ -0,0 +1,131 @@ +/* + * 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 + +namespace facebook::react { + +/* +NOTE: we keep this internal copy of folly/MoveWrapper.h to unblock +the the workstream of dropping the dependency on folly in RN! + +For a technical explanation on why we still need this we defer +to the doc in folly/Function.h: + +"There are some limitations in std::function that folly::Function tries to +avoid. std::function is copy-constructible and requires that the callable that +it wraps is copy-constructible as well, which is a constraint that is often +inconvenient. In most cases when using a std::function you don't make use of +its copy-constructibility, so you might sometimes feel like you get back very +little in return for a noticeable restriction. This restriction becomes +apparent when trying to use a lambda capturing a unique_ptr (or any +non-copyable type) as a callback for a folly::Future. + +std::unique_ptr foo_ptr = new Foo; + +some_future.then( + [foo_ptr = std::move(foo_ptr)] mutable + (int x) + { foo_ptr->setX(x); } +); + +This piece of code did not compile before folly::Future started using +folly::Function instead of std::function to store the callback. Because the +lambda captures something non-copyable (the unique_ptr), it is not copyable +itself. And std::function can only store copyable callables. + +The implementation of folly::Future did not make use of the +copy-constructibility of std::function at any point. There was no benefit from +the fact that the std::function is copy-constructible, but the fact that it can +only wrap copy-constructible callables posed a restriction. + +A workaround was available: folly::MoveWrapper, which wraps an object that may +be non-copyable and implements copy operations by moving the embedded object. +Using a folly::MoveWrapper, you can capture non-copyable objects in a lambda, +and the lambda itself is still copyable and may be wrapped in a std::function. +It is a pragmatic solution for the above problem, but you have to be a little +careful. The problem is that you can’t use a MoveWrapper anywhere where copy +operations are assumed to behave like actual copy operations. Also, a +folly::MoveWrapper> essentially behaves like auto_ptr. Ask +yourself whether you’d want to use lots of auto_ptrs in your codebase. And the +original question still persists: we very often don’t benefit from +copy-constructibility of std::function, so why do we have to live with this +restriction? I.e. why do we have to use MoveWrapper?" +*/ + +/** C++11 closures don't support move-in capture. Nor does std::bind. + facepalm. + + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3610.html + + "[...] a work-around that should make people's stomach crawl: + write a wrapper that performs move-on-copy, much like the deprecated + auto_ptr" + + Unlike auto_ptr, this doesn't require a heap allocation. + */ +template +class MoveWrapper { + public: + /** If value can be default-constructed, why not? + Then we don't have to move it in */ + MoveWrapper() = default; + + /// Move a value in. + explicit MoveWrapper(T &&t) : value(std::move(t)) {} + + /// copy is move + MoveWrapper(const MoveWrapper &other) : value(std::move(other.value)) {} + + /// move is also move + MoveWrapper(MoveWrapper &&other) noexcept : value(std::move(other.value)) {} + + const T &operator*() const + { + return value; + } + T &operator*() + { + return value; + } + + const T *operator->() const + { + return &value; + } + T *operator->() + { + return &value; + } + + /// move the value out (sugar for std::move(*moveWrapper)) + T &&move() + { + return std::move(value); + } + + // If you want these you're probably doing it wrong, though they'd be + // easy enough to implement + MoveWrapper &operator=(const MoveWrapper &) = delete; + MoveWrapper &operator=(MoveWrapper &&) = delete; + + private: + mutable T value; +}; + +/// Make a MoveWrapper from the argument. Because the name "makeMoveWrapper" +/// is already quite transparent in its intent, this will work for lvalues as +/// if you had wrapped them in std::move. +template ::type> +MoveWrapper makeMoveWrapper(T &&t) +{ + return MoveWrapper(std::forward(t)); +} + +} // namespace facebook::react diff --git a/packages/react-native/scripts/cocoapods/__tests__/codegen_utils-test.rb b/packages/react-native/scripts/cocoapods/__tests__/codegen_utils-test.rb index 4571bacd7a3d..026125104f7d 100644 --- a/packages/react-native/scripts/cocoapods/__tests__/codegen_utils-test.rb +++ b/packages/react-native/scripts/cocoapods/__tests__/codegen_utils-test.rb @@ -263,7 +263,7 @@ def get_podspec_no_fabric_no_script "React-Core": [], "React-jsi": [], "React-jsiexecutor": [], - "ReactCommon/turbomodule/bridging": [], + "React-Bridging": [], "ReactCommon/turbomodule/core": [], "hermes-engine": [], "React-NativeModulesApple": [], diff --git a/packages/react-native/scripts/cocoapods/__tests__/new_architecture-test.rb b/packages/react-native/scripts/cocoapods/__tests__/new_architecture-test.rb index c0345758e1c0..2af31fa3fd81 100644 --- a/packages/react-native/scripts/cocoapods/__tests__/new_architecture-test.rb +++ b/packages/react-native/scripts/cocoapods/__tests__/new_architecture-test.rb @@ -145,7 +145,7 @@ def test_installModulesDependencies_whenNewArchEnabledAndNewArchAndNoSearchPaths { :dependency_name => "ReactCodegen" }, { :dependency_name => "RCTRequired" }, { :dependency_name => "RCTTypeSafety" }, - { :dependency_name => "ReactCommon/turbomodule/bridging" }, + { :dependency_name => "React-Bridging" }, { :dependency_name => "ReactCommon/turbomodule/core" }, { :dependency_name => "React-NativeModulesApple" }, { :dependency_name => "Yoga" }, @@ -187,7 +187,7 @@ def test_installModulesDependencies_whenNewArchDisabledAndSearchPathsAndCompiler { :dependency_name => "ReactCodegen" }, { :dependency_name => "RCTRequired" }, { :dependency_name => "RCTTypeSafety" }, - { :dependency_name => "ReactCommon/turbomodule/bridging" }, + { :dependency_name => "React-Bridging" }, { :dependency_name => "ReactCommon/turbomodule/core" }, { :dependency_name => "React-NativeModulesApple" }, { :dependency_name => "Yoga" }, diff --git a/packages/react-native/scripts/cocoapods/new_architecture.rb b/packages/react-native/scripts/cocoapods/new_architecture.rb index 8c7508b5400f..a41a31d32645 100644 --- a/packages/react-native/scripts/cocoapods/new_architecture.rb +++ b/packages/react-native/scripts/cocoapods/new_architecture.rb @@ -117,7 +117,7 @@ def self.install_modules_dependencies(spec, new_arch_enabled, folly_version = He spec.dependency "RCTRequired" spec.dependency "RCTTypeSafety" - spec.dependency "ReactCommon/turbomodule/bridging" + spec.dependency "React-Bridging" spec.dependency "ReactCommon/turbomodule/core" spec.dependency "React-NativeModulesApple" spec.dependency "Yoga" diff --git a/packages/react-native/scripts/codegen/__tests__/__snapshots__/generate-artifacts-executor-test.js.snap b/packages/react-native/scripts/codegen/__tests__/__snapshots__/generate-artifacts-executor-test.js.snap index 8c76f937ae04..fa150ecf6620 100644 --- a/packages/react-native/scripts/codegen/__tests__/__snapshots__/generate-artifacts-executor-test.js.snap +++ b/packages/react-native/scripts/codegen/__tests__/__snapshots__/generate-artifacts-executor-test.js.snap @@ -235,7 +235,7 @@ exports[`execute test-app "ReactCodegen/RCTModulesConformingToProtocolsProvider. { static NSArray *classNames = nil; static dispatch_once_t onceToken; - + dispatch_once(&onceToken, ^{ classNames = @[ @\\"RCTTestAppDeprecatedImageURLLoader\\", @@ -244,7 +244,7 @@ exports[`execute test-app "ReactCodegen/RCTModulesConformingToProtocolsProvider. @\\"TestLibraryImageURLLoader\\" ]; }); - + return classNames; } @@ -252,7 +252,7 @@ exports[`execute test-app "ReactCodegen/RCTModulesConformingToProtocolsProvider. { static NSArray *classNames = nil; static dispatch_once_t onceToken; - + dispatch_once(&onceToken, ^{ classNames = @[ @\\"RCTTestAppDeprecatedImageDataDecoder\\", @@ -261,7 +261,7 @@ exports[`execute test-app "ReactCodegen/RCTModulesConformingToProtocolsProvider. @\\"TestLibraryImageDataDecoder\\" ]; }); - + return classNames; } @@ -269,7 +269,7 @@ exports[`execute test-app "ReactCodegen/RCTModulesConformingToProtocolsProvider. { static NSArray *classNames = nil; static dispatch_once_t onceToken; - + dispatch_once(&onceToken, ^{ classNames = @[ @\\"RCTTestAppDeprecatedURLRequestHandler\\", @@ -278,7 +278,7 @@ exports[`execute test-app "ReactCodegen/RCTModulesConformingToProtocolsProvider. @\\"TestLibraryURLRequestHandler\\" ]; }); - + return classNames; } @@ -465,7 +465,7 @@ Pod::Spec.new do |s| s.dependency \\"RCTTypeSafety\\" s.dependency \\"React-Core\\" s.dependency \\"React-jsi\\" - s.dependency \\"ReactCommon/turbomodule/bridging\\" + s.dependency \\"React-Bridging\\" s.dependency \\"ReactCommon/turbomodule/core\\" s.dependency \\"React-NativeModulesApple\\" s.dependency 'React-graphics' @@ -733,13 +733,13 @@ exports[`execute test-app-legacy "ReactCodegen/RCTModulesConformingToProtocolsPr { static NSArray *classNames = nil; static dispatch_once_t onceToken; - + dispatch_once(&onceToken, ^{ classNames = @[ @\\"RCTTestAppDeprecatedImageURLLoader\\" ]; }); - + return classNames; } @@ -747,13 +747,13 @@ exports[`execute test-app-legacy "ReactCodegen/RCTModulesConformingToProtocolsPr { static NSArray *classNames = nil; static dispatch_once_t onceToken; - + dispatch_once(&onceToken, ^{ classNames = @[ - + ]; }); - + return classNames; } @@ -761,13 +761,13 @@ exports[`execute test-app-legacy "ReactCodegen/RCTModulesConformingToProtocolsPr { static NSArray *classNames = nil; static dispatch_once_t onceToken; - + dispatch_once(&onceToken, ^{ classNames = @[ @\\"RCTTestAppDeprecatedURLRequestHandler\\" ]; }); - + return classNames; } @@ -944,7 +944,7 @@ Pod::Spec.new do |s| s.dependency \\"RCTTypeSafety\\" s.dependency \\"React-Core\\" s.dependency \\"React-jsi\\" - s.dependency \\"ReactCommon/turbomodule/bridging\\" + s.dependency \\"React-Bridging\\" s.dependency \\"ReactCommon/turbomodule/core\\" s.dependency \\"React-NativeModulesApple\\" s.dependency 'React-graphics' diff --git a/packages/react-native/scripts/codegen/templates/ReactCodegen.podspec.template b/packages/react-native/scripts/codegen/templates/ReactCodegen.podspec.template index de434d4b91d8..ff00b262a37d 100644 --- a/packages/react-native/scripts/codegen/templates/ReactCodegen.podspec.template +++ b/packages/react-native/scripts/codegen/templates/ReactCodegen.podspec.template @@ -69,7 +69,7 @@ Pod::Spec.new do |s| s.dependency "RCTTypeSafety" s.dependency "React-Core" s.dependency "React-jsi" - s.dependency "ReactCommon/turbomodule/bridging" + s.dependency "React-Bridging" s.dependency "ReactCommon/turbomodule/core" s.dependency "React-NativeModulesApple" s.dependency 'React-graphics' diff --git a/packages/react-native/scripts/react_native_pods.rb b/packages/react-native/scripts/react_native_pods.rb index 75b21b486136..b0a32617d573 100644 --- a/packages/react-native/scripts/react_native_pods.rb +++ b/packages/react-native/scripts/react_native_pods.rb @@ -179,7 +179,7 @@ def use_react_native! ( pod 'React-perflogger', :path => "#{prefix}/ReactCommon/reactperflogger" pod 'React-oscompat', :path => "#{prefix}/ReactCommon/oscompat" pod 'React-logger', :path => "#{prefix}/ReactCommon/logger" - pod 'ReactCommon/turbomodule/core', :path => "#{prefix}/ReactCommon", :modular_headers => true + pod 'ReactCommon/turbomodule/core', :path => "#{prefix}/React-Turbomodule.podspec", :modular_headers => true pod 'React-NativeModulesApple', :path => "#{prefix}/ReactCommon/react/nativemodule/core/platform/ios", :modular_headers => true pod 'Yoga', :path => "#{prefix}/ReactCommon/yoga", :modular_headers => true setup_fabric!(:react_native_path => prefix)