From a2abbc2e3f65c751ff2429d2e5cf140324b3b600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20=C5=BBelawski?= Date: Mon, 13 Jan 2025 19:26:13 +0100 Subject: [PATCH] refactor: TurboModule invalidation handling with DummyReanimatedModuleProxy --- .../FabricExample.xcodeproj/project.pbxproj | 12 +- apps/fabric-example/ios/Podfile.lock | 2 +- apps/macos-example/macos/Podfile.lock | 2 +- apps/paper-example/ios/Podfile.lock | 2 +- .../project.pbxproj | 12 +- apps/tvos-example/ios/Podfile.lock | 2 +- .../AnimatedSensor/AnimatedSensorModule.cpp | 2 +- .../DummyReanimatedModuleProxy.cpp | 110 +++++++++++++++++ .../DummyReanimatedModuleProxy.h | 115 ++++++++++++++++++ .../NativeModules/ReanimatedModuleProxy.cpp | 16 +-- .../RuntimeDecorators/RNRuntimeDecorator.cpp | 41 ------- .../RuntimeDecorators/RNRuntimeDecorator.h | 42 ++++++- .../NativeModules/WorkletsModuleProxy.cpp | 2 +- .../Registries/EventHandlerRegistry.cpp | 2 +- .../cpp/worklets/Tools/ReanimatedVersion.cpp | 4 + .../worklets/Tools/SingleInstanceChecker.h | 8 +- .../WorkletRuntime/WorkletRuntime.cpp | 4 +- .../worklets/WorkletRuntime/WorkletRuntime.h | 4 +- .../WorkletRuntime/WorkletRuntimeCollector.h | 9 +- .../com/swmansion/reanimated/NativeProxy.java | 52 +++++--- .../cpp/reanimated/android/NativeProxy.cpp | 42 ++++++- .../main/cpp/reanimated/android/NativeProxy.h | 16 +++ .../nativeProxy/NativeProxyCommon.java | 2 + .../swmansion/worklets/WorkletsModule.java | 8 ++ .../apple/reanimated/apple/REAModule.mm | 34 ++++-- .../reanimated/apple/native/NativeProxy.mm | 2 +- .../apple/worklets/apple/WorkletsModule.h | 2 + .../apple/worklets/apple/WorkletsModule.mm | 9 ++ 28 files changed, 447 insertions(+), 111 deletions(-) create mode 100644 packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/DummyReanimatedModuleProxy.cpp create mode 100644 packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/DummyReanimatedModuleProxy.h delete mode 100644 packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.cpp diff --git a/apps/fabric-example/ios/FabricExample.xcodeproj/project.pbxproj b/apps/fabric-example/ios/FabricExample.xcodeproj/project.pbxproj index 92b635050692..f4f88fbb0d0e 100644 --- a/apps/fabric-example/ios/FabricExample.xcodeproj/project.pbxproj +++ b/apps/fabric-example/ios/FabricExample.xcodeproj/project.pbxproj @@ -321,7 +321,7 @@ }; 1232BDDFC9DE5BCE2776187D /* [CP-User] Generate metadata for clangd */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; + alwaysOutOfDate = true; buildActionMask = 2147483647; files = ( ); @@ -573,7 +573,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; @@ -646,7 +649,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/apps/fabric-example/ios/Podfile.lock b/apps/fabric-example/ios/Podfile.lock index 4c09f0df1bd0..05164163610c 100644 --- a/apps/fabric-example/ios/Podfile.lock +++ b/apps/fabric-example/ios/Podfile.lock @@ -2229,7 +2229,7 @@ SPEC CHECKSUMS: RNScreens: d0854539b51a53e38b61bcc9fb402439a9c73b26 RNSVG: 7e38044415125a1d108294377de261d2fe2c54c9 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: dd05f79b1943c96893294ee5040576aa6e8a8d82 + Yoga: 0c8754b0ea9edb13b6ce6b60f0f69eb5f164f16a PODFILE CHECKSUM: 4a9e0af2552a3fcd2303b56ad75e373f8bae65b9 diff --git a/apps/macos-example/macos/Podfile.lock b/apps/macos-example/macos/Podfile.lock index 8544ada63b3a..6a84a8a4fad8 100644 --- a/apps/macos-example/macos/Podfile.lock +++ b/apps/macos-example/macos/Podfile.lock @@ -1868,7 +1868,7 @@ SPEC CHECKSUMS: RNReanimated: ed490424d3b8b9f2acd104577c73b374fc79310b RNSVG: 669ed128ab9005090c612a0d627dbecb6ab5c76f SocketRocket: 9ee265c4b5ae2382d18e4ee1d2dd2d7af0ff1ab5 - Yoga: 209f62622a01344dbb9fa8d348610eaeb7df2cca + Yoga: 446e6f351a519539ff00a1159fe41e589aab1b94 PODFILE CHECKSUM: 8d50cc2acc9f6a6b1a12bd9106b86385ad72266f diff --git a/apps/paper-example/ios/Podfile.lock b/apps/paper-example/ios/Podfile.lock index 0ac3060060d0..46c9898f204e 100644 --- a/apps/paper-example/ios/Podfile.lock +++ b/apps/paper-example/ios/Podfile.lock @@ -2060,7 +2060,7 @@ SPEC CHECKSUMS: RNScreens: 81237378a6d7921fb2dbb6a6c6c45c3d8cc696bb RNSVG: 515a902fc18a375907eb4c3abec0b803fbfa37ef SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: dd05f79b1943c96893294ee5040576aa6e8a8d82 + Yoga: 0c8754b0ea9edb13b6ce6b60f0f69eb5f164f16a PODFILE CHECKSUM: f6c84e0ec8eddea6d3ee15329987727bd1e6ff08 diff --git a/apps/paper-example/ios/ReanimatedExample.xcodeproj/project.pbxproj b/apps/paper-example/ios/ReanimatedExample.xcodeproj/project.pbxproj index b1d0c2b3b80d..63b536470231 100644 --- a/apps/paper-example/ios/ReanimatedExample.xcodeproj/project.pbxproj +++ b/apps/paper-example/ios/ReanimatedExample.xcodeproj/project.pbxproj @@ -365,7 +365,7 @@ }; EC2A315BF2310B4CCE81E020 /* [CP-User] Generate metadata for clangd */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; + alwaysOutOfDate = true; buildActionMask = 2147483647; files = ( ); @@ -579,7 +579,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; @@ -652,7 +655,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/apps/tvos-example/ios/Podfile.lock b/apps/tvos-example/ios/Podfile.lock index 04272ce1515a..f6536db5edda 100644 --- a/apps/tvos-example/ios/Podfile.lock +++ b/apps/tvos-example/ios/Podfile.lock @@ -1888,7 +1888,7 @@ SPEC CHECKSUMS: ReactCommon: b927fd46115bd2acb146e24cf1a08f22abda8b3f RNReanimated: 3a5e1e235c940894097b0734aad9ebce45431ddd SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: 2b0e5affb9ab46e4ebad33530df829c153c323d8 + Yoga: 651e5fd560c7e408ab9d9ca44b8de1b622d7f0cc PODFILE CHECKSUM: 79e1477a8eb76b717bdd7c1610f7f8e6772536a9 diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/AnimatedSensor/AnimatedSensorModule.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/AnimatedSensor/AnimatedSensorModule.cpp index 3ca1bb023532..4ec3280ea3b6 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/AnimatedSensor/AnimatedSensorModule.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/AnimatedSensor/AnimatedSensorModule.cpp @@ -41,7 +41,7 @@ jsi::Value AnimatedSensorModule::registerSensor( return; } - jsi::Runtime &uiRuntime = uiWorkletRuntime->getJSIRuntime(); + jsi::Runtime &uiRuntime = *uiWorkletRuntime->getJSIRuntime(); jsi::Object value(uiRuntime); if (sensorType == SensorType::ROTATION_VECTOR) { // TODO: timestamp should be provided by the platform implementation diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/DummyReanimatedModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/DummyReanimatedModuleProxy.cpp new file mode 100644 index 000000000000..1a622f654bd8 --- /dev/null +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/DummyReanimatedModuleProxy.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include + +#ifdef RCT_NEW_ARCH_ENABLED +#include +#include +#endif // RCT_NEW_ARCH_ENABLED + +#include +#include +#include +#include + +#ifdef __ANDROID__ +#include +#endif // __ANDROID__ + +#ifdef RCT_NEW_ARCH_ENABLED +#include +#include +#include +#endif // RCT_NEW_ARCH_ENABLED + +using namespace facebook; + +namespace reanimated { + +DummyReanimatedModuleProxy::DummyReanimatedModuleProxy( + jsi::Runtime &rnRuntime, + const std::shared_ptr &jsCallInvoker, + const bool isBridgeless, + const bool isReducedMotion) + : ReanimatedModuleProxySpec(jsCallInvoker), + isBridgeless_(isBridgeless), + isReducedMotion_(isReducedMotion) {} + +jsi::Value DummyReanimatedModuleProxy::registerEventHandler( + jsi::Runtime &rt, + const jsi::Value &worklet, + const jsi::Value &eventName, + const jsi::Value &emitterReactTag) { + return jsi::Value(static_cast(0)); +} + +void DummyReanimatedModuleProxy::unregisterEventHandler( + jsi::Runtime &rt, + const jsi::Value ®istrationId) {} + +jsi::Value DummyReanimatedModuleProxy::getViewProp( + jsi::Runtime &rnRuntime, + const jsi::Value &viewTag, + const jsi::Value &propName, + const jsi::Value &callback) { + return jsi::Value::undefined(); +} + +jsi::Value DummyReanimatedModuleProxy::enableLayoutAnimations( + jsi::Runtime &rt, + const jsi::Value &config) { + return jsi::Value::undefined(); +} + +jsi::Value DummyReanimatedModuleProxy::configureProps( + jsi::Runtime &rt, + const jsi::Value &uiProps, + const jsi::Value &nativeProps) { + return jsi::Value::undefined(); +} + +jsi::Value DummyReanimatedModuleProxy::configureLayoutAnimationBatch( + jsi::Runtime &rt, + const jsi::Value &layoutAnimationsBatch) { + return jsi::Value::undefined(); +} + +void DummyReanimatedModuleProxy::setShouldAnimateExiting( + jsi::Runtime &rt, + const jsi::Value &viewTag, + const jsi::Value &shouldAnimate) {} + +jsi::Value DummyReanimatedModuleProxy::registerSensor( + jsi::Runtime &rt, + const jsi::Value &sensorType, + const jsi::Value &interval, + const jsi::Value &iosReferenceFrame, + const jsi::Value &sensorDataContainer) { + return jsi::Value(static_cast(0)); +} + +void DummyReanimatedModuleProxy::unregisterSensor( + jsi::Runtime &rt, + const jsi::Value &sensorId) {} + +jsi::Value DummyReanimatedModuleProxy::subscribeForKeyboardEvents( + jsi::Runtime &rt, + const jsi::Value &handlerWorklet, + const jsi::Value &isStatusBarTranslucent, + const jsi::Value &isNavigationBarTranslucent) { + return jsi::Value(static_cast(0)); +} + +void DummyReanimatedModuleProxy::unsubscribeFromKeyboardEvents( + jsi::Runtime &rt, + const jsi::Value &listenerId) {} + +} // namespace reanimated diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/DummyReanimatedModuleProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/DummyReanimatedModuleProxy.h new file mode 100644 index 000000000000..579cef5762fa --- /dev/null +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/DummyReanimatedModuleProxy.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef RCT_NEW_ARCH_ENABLED +#include +#include +#include +#include +#endif // RCT_NEW_ARCH_ENABLED + +#include +#include +#include +#include +#include + +#ifdef RCT_NEW_ARCH_ENABLED +#include +#endif // RCT_NEW_ARCH_ENABLED + +#include +#include +#include +#include +#include + +namespace reanimated { + +class DummyReanimatedModuleProxy : public ReanimatedModuleProxySpec { + public: + DummyReanimatedModuleProxy( + jsi::Runtime &rnRuntime, + const std::shared_ptr &jsCallInvoker, + const bool isBridgeless, + const bool isReducedMotion); + + jsi::Value registerEventHandler( + jsi::Runtime &rt, + const jsi::Value &worklet, + const jsi::Value &eventName, + const jsi::Value &emitterReactTag) override; + void unregisterEventHandler( + jsi::Runtime &rt, + const jsi::Value ®istrationId) override; + + jsi::Value getViewProp( + jsi::Runtime &rt, +#ifdef RCT_NEW_ARCH_ENABLED + const jsi::Value &shadowNodeWrapper, +#else + const jsi::Value &viewTag, +#endif + const jsi::Value &propName, + const jsi::Value &callback) override; + + jsi::Value enableLayoutAnimations(jsi::Runtime &rt, const jsi::Value &config) + override; + jsi::Value configureProps( + jsi::Runtime &rt, + const jsi::Value &uiProps, + const jsi::Value &nativeProps) override; + jsi::Value configureLayoutAnimationBatch( + jsi::Runtime &rt, + const jsi::Value &layoutAnimationsBatch) override; + void setShouldAnimateExiting( + jsi::Runtime &rt, + const jsi::Value &viewTag, + const jsi::Value &shouldAnimate) override; + + jsi::Value registerSensor( + jsi::Runtime &rt, + const jsi::Value &sensorType, + const jsi::Value &interval, + const jsi::Value &iosReferenceFrame, + const jsi::Value &sensorDataContainer) override; + void unregisterSensor(jsi::Runtime &rt, const jsi::Value &sensorId) override; + + void cleanupSensors(); + + jsi::Value subscribeForKeyboardEvents( + jsi::Runtime &rt, + const jsi::Value &keyboardEventContainer, + const jsi::Value &isStatusBarTranslucent, + const jsi::Value &isNavigationBarTranslucent) override; + void unsubscribeFromKeyboardEvents( + jsi::Runtime &rt, + const jsi::Value &listenerId) override; + + [[nodiscard]] inline bool isBridgeless() const { + return isBridgeless_; + } + + [[nodiscard]] inline bool isReducedMotion() const { + return isReducedMotion_; + } + + inline std::shared_ptr getJSLogger() const { + return nullptr; + } + + private: + const bool isBridgeless_; + const bool isReducedMotion_; + +#ifndef NDEBUG + worklets::SingleInstanceChecker + singleInstanceChecker_; +#endif // NDEBUG +}; + +} // namespace reanimated diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp index ffe538bd9dcc..b8e58537fe00 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp @@ -154,7 +154,7 @@ void ReanimatedModuleProxy::commonInit( #endif jsi::Runtime &uiRuntime = - workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); + *workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); UIRuntimeDecorator::decorate( uiRuntime, #ifdef RCT_NEW_ARCH_ENABLED @@ -293,7 +293,7 @@ jsi::Value ReanimatedModuleProxy::getViewProp( const auto shadowNode = shadowNodeFromValue(rnRuntime, shadowNodeWrapper); workletsModuleProxy_->getUIScheduler()->scheduleOnUI(COPY_CAPTURE_WITH_THIS { jsi::Runtime &uiRuntime = - workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); + *workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); const auto resultStr = obtainPropFromShadowNode(uiRuntime, propNameStr, shadowNode); @@ -435,7 +435,7 @@ void ReanimatedModuleProxy::maybeRequestRender() { if (!renderRequested_) { renderRequested_ = true; jsi::Runtime &uiRuntime = - workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); + *workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); requestRender_(onRenderCallback_, uiRuntime); } } @@ -444,7 +444,7 @@ void ReanimatedModuleProxy::onRender(double timestampMs) { auto callbacks = std::move(frameCallbacks_); frameCallbacks_.clear(); jsi::Runtime &uiRuntime = - workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); + *workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); jsi::Value timestamp{timestampMs}; for (const auto &callback : callbacks) { runOnRuntimeGuarded(uiRuntime, *callback, timestamp); @@ -554,7 +554,7 @@ bool ReanimatedModuleProxy::handleRawEvent( eventType = "on" + eventType.substr(3); } jsi::Runtime &rt = - workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); + *workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); const auto &eventPayload = rawEvent.eventPayload; jsi::Value payload = eventPayload->asJSIValue(rt); @@ -594,7 +594,7 @@ void ReanimatedModuleProxy::performOperations() { operationsInBatch_.clear(); jsi::Runtime &rt = - workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); + *workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); { auto lock = propsRegistry_->createLock(); @@ -732,7 +732,7 @@ jsi::String ReanimatedModuleProxy::obtainProp( const jsi::Value &shadowNodeWrapper, const jsi::Value &propName) { jsi::Runtime &uiRuntime = - workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); + *workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); const auto propNameStr = propName.asString(rt).utf8(rt); const auto shadowNode = shadowNodeFromValue(rt, shadowNodeWrapper); const auto resultStr = @@ -810,7 +810,7 @@ void ReanimatedModuleProxy::initializeLayoutAnimationsProxy() { layoutAnimationsManager_, componentDescriptorRegistry, scheduler->getContextContainer(), - workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(), + *workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(), workletsModuleProxy_->getUIScheduler()); } } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.cpp deleted file mode 100644 index 59ff85f65058..000000000000 --- a/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include - -namespace reanimated { - -void RNRuntimeDecorator::decorate( - jsi::Runtime &rnRuntime, - jsi::Runtime &uiRuntime, - const std::shared_ptr &reanimatedModuleProxy) { - auto workletRuntimeValue = - rnRuntime.global() - .getPropertyAsObject(rnRuntime, "ArrayBuffer") - .asFunction(rnRuntime) - .callAsConstructor(rnRuntime, {static_cast(sizeof(void *))}); - uintptr_t *workletRuntimeData = reinterpret_cast( - workletRuntimeValue.getObject(rnRuntime).getArrayBuffer(rnRuntime).data( - rnRuntime)); - workletRuntimeData[0] = reinterpret_cast(&uiRuntime); - rnRuntime.global().setProperty( - rnRuntime, "_WORKLET_RUNTIME", workletRuntimeValue); - - rnRuntime.global().setProperty( - rnRuntime, "_IS_BRIDGELESS", reanimatedModuleProxy->isBridgeless()); - -#ifndef NDEBUG - checkJSVersion(rnRuntime, reanimatedModuleProxy->getJSLogger()); -#endif // NDEBUG - injectReanimatedCppVersion(rnRuntime); - - rnRuntime.global().setProperty( - rnRuntime, - "_REANIMATED_IS_REDUCED_MOTION", - reanimatedModuleProxy->isReducedMotion()); - - rnRuntime.global().setProperty( - rnRuntime, - "__reanimatedModuleProxy", - jsi::Object::createFromHostObject(rnRuntime, reanimatedModuleProxy)); -} - -} // namespace reanimated diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.h b/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.h index e36d42178224..7ab68aa8d5c3 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.h @@ -2,6 +2,8 @@ #include +#include + #include #include @@ -12,10 +14,44 @@ namespace reanimated { class RNRuntimeDecorator { public: + template static void decorate( - jsi::Runtime &rnRuntime, - jsi::Runtime &uiRuntime, - const std::shared_ptr &reanimatedModuleProxy); + jsi::Runtime *pRnRuntime, + jsi::Runtime *uiRuntime, + const std::shared_ptr &reanimatedModuleProxy) { + auto &rnRuntime = *pRnRuntime; + + auto workletRuntimeValue = + rnRuntime.global() + .getPropertyAsObject(rnRuntime, "ArrayBuffer") + .asFunction(rnRuntime) + .callAsConstructor( + rnRuntime, {static_cast(sizeof(void *))}); + uintptr_t *workletRuntimeData = reinterpret_cast( + workletRuntimeValue.getObject(rnRuntime).getArrayBuffer(rnRuntime).data( + rnRuntime)); + workletRuntimeData[0] = reinterpret_cast(&uiRuntime); + rnRuntime.global().setProperty( + rnRuntime, "_WORKLET_RUNTIME", workletRuntimeValue); + + rnRuntime.global().setProperty( + rnRuntime, "_IS_BRIDGELESS", reanimatedModuleProxy->isBridgeless()); + +#ifndef NDEBUG + checkJSVersion(rnRuntime, reanimatedModuleProxy->getJSLogger()); +#endif // NDEBUG + injectReanimatedCppVersion(rnRuntime); + + rnRuntime.global().setProperty( + rnRuntime, + "_REANIMATED_IS_REDUCED_MOTION", + reanimatedModuleProxy->isReducedMotion()); + + rnRuntime.global().setProperty( + rnRuntime, + "__reanimatedModuleProxy", + jsi::Object::createFromHostObject(rnRuntime, reanimatedModuleProxy)); + } }; } // namespace reanimated diff --git a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.cpp index 12f9385e7e3f..a610dd7facf8 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.cpp @@ -93,7 +93,7 @@ void WorkletsModuleProxy::scheduleOnUI( // temporary JSI objects and hence it allows for such objects to be // garbage collected much sooner. Apparently the scope API is only // supported on Hermes at the moment. - const auto scope = jsi::Scope(uiWorkletRuntime_->getJSIRuntime()); + const auto scope = jsi::Scope(*uiWorkletRuntime_->getJSIRuntime()); #endif uiWorkletRuntime_->runGuarded(shareableWorklet); diff --git a/packages/react-native-reanimated/Common/cpp/worklets/Registries/EventHandlerRegistry.cpp b/packages/react-native-reanimated/Common/cpp/worklets/Registries/EventHandlerRegistry.cpp index 7e14d1d00f04..1e6b9a825319 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/Registries/EventHandlerRegistry.cpp +++ b/packages/react-native-reanimated/Common/cpp/worklets/Registries/EventHandlerRegistry.cpp @@ -74,7 +74,7 @@ void EventHandlerRegistry::processEvent( } } - jsi::Runtime &rt = uiWorkletRuntime->getJSIRuntime(); + jsi::Runtime &rt = *uiWorkletRuntime->getJSIRuntime(); eventPayload.asObject(rt).setProperty( rt, "eventName", jsi::String::createFromUtf8(rt, eventName)); for (auto handler : handlersForEvent) { diff --git a/packages/react-native-reanimated/Common/cpp/worklets/Tools/ReanimatedVersion.cpp b/packages/react-native-reanimated/Common/cpp/worklets/Tools/ReanimatedVersion.cpp index 0f982afebca6..367859133db9 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/Tools/ReanimatedVersion.cpp +++ b/packages/react-native-reanimated/Common/cpp/worklets/Tools/ReanimatedVersion.cpp @@ -55,6 +55,10 @@ bool matchVersion(const std::string &version1, const std::string &version2) { void checkJSVersion( jsi::Runtime &rnRuntime, const std::shared_ptr &jsLogger) { + if (!jsLogger) { + return; + } + auto cppVersion = getReanimatedCppVersion(); auto maybeJSVersion = diff --git a/packages/react-native-reanimated/Common/cpp/worklets/Tools/SingleInstanceChecker.h b/packages/react-native-reanimated/Common/cpp/worklets/Tools/SingleInstanceChecker.h index 9c8c0eb23cb8..8544251b0754 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/Tools/SingleInstanceChecker.h +++ b/packages/react-native-reanimated/Common/cpp/worklets/Tools/SingleInstanceChecker.h @@ -52,12 +52,12 @@ SingleInstanceChecker::SingleInstanceChecker() { std::string className = __cxxabiv1::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status); + instanceCount_++; + assertWithMessage( - instanceCount_ == 0, - "[Reanimated] More than one instance of " + className + + instanceCount_ <= 2, + "[Reanimated] More than two instances of " + className + " present. This may indicate a memory leak due to a retain cycle."); - - instanceCount_++; } template diff --git a/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp b/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp index dc137e99395e..fb0287230f14 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp +++ b/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp @@ -70,7 +70,7 @@ WorkletRuntime::WorkletRuntime( #endif name_(name) { jsi::Runtime &rt = *runtime_; - WorkletRuntimeCollector::install(rt); + WorkletRuntimeCollector::install(&rt); WorkletRuntimeDecorator::decorate(rt, name, jsScheduler); auto codeBuffer = std::make_shared( @@ -93,7 +93,7 @@ jsi::Value WorkletRuntime::executeSync( worklet, "[Reanimated] Only worklets can be executed synchronously on UI runtime."); auto lock = std::unique_lock(*runtimeMutex_); - jsi::Runtime &uiRuntime = getJSIRuntime(); + jsi::Runtime &uiRuntime = *getJSIRuntime(); auto result = runGuarded(shareableWorklet); auto shareableResult = extractShareableOrThrow(uiRuntime, result); lock.unlock(); diff --git a/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h b/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h index f904d051772d..b460afa6e73c 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h +++ b/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h @@ -28,8 +28,8 @@ class WorkletRuntime : public jsi::HostObject, const bool supportsLocking, const std::string &valueUnpackerCode); - jsi::Runtime &getJSIRuntime() const { - return *runtime_; + jsi::Runtime *getJSIRuntime() const { + return runtime_.get(); } template diff --git a/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeCollector.h b/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeCollector.h index 09c709132d79..acfb32ad882b 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeCollector.h +++ b/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeCollector.h @@ -23,10 +23,11 @@ class WorkletRuntimeCollector : public jsi::HostObject { WorkletRuntimeRegistry::unregisterRuntime(runtime_); } - static void install(jsi::Runtime &rt) { - auto collector = std::make_shared(rt); - auto object = jsi::Object::createFromHostObject(rt, collector); - rt.global().setProperty(rt, "__workletRuntimeCollector", object); + static void install(jsi::Runtime *rt) { + auto &runtime = *rt; + auto collector = std::make_shared(runtime); + auto object = jsi::Object::createFromHostObject(runtime, collector); + runtime.global().setProperty(runtime, "__workletRuntimeCollector", object); } private: diff --git a/packages/react-native-reanimated/android/src/fabric/java/com/swmansion/reanimated/NativeProxy.java b/packages/react-native-reanimated/android/src/fabric/java/com/swmansion/reanimated/NativeProxy.java index 7921dde2b006..8f08abdedc98 100644 --- a/packages/react-native-reanimated/android/src/fabric/java/com/swmansion/reanimated/NativeProxy.java +++ b/packages/react-native-reanimated/android/src/fabric/java/com/swmansion/reanimated/NativeProxy.java @@ -28,27 +28,39 @@ public class NativeProxy extends NativeProxyCommon { public @OptIn(markerClass = FrameworkAPI.class) NativeProxy( ReactApplicationContext context, WorkletsModule workletsModule) { super(context); - ReactFeatureFlagsWrapper.enableMountHooks(); - FabricUIManager fabricUIManager = - (FabricUIManager) UIManagerHelper.getUIManager(context, UIManagerType.FABRIC); + CallInvokerHolderImpl callInvokerHolder = JSCallInvokerResolver.getJSCallInvokerHolder(context); - LayoutAnimations LayoutAnimations = new LayoutAnimations(context); + if (workletsModule.isValid()) { + ReactFeatureFlagsWrapper.enableMountHooks(); - CallInvokerHolderImpl callInvokerHolder = JSCallInvokerResolver.getJSCallInvokerHolder(context); - mHybridData = - initHybrid( - workletsModule, - Objects.requireNonNull(context.getJavaScriptContextHolder()).get(), - callInvokerHolder, - LayoutAnimations, - context.isBridgeless(), - fabricUIManager); - - prepareLayoutAnimations(LayoutAnimations); - installJSIBindings(); - if (BuildConfig.DEBUG) { - checkCppVersion(); + FabricUIManager fabricUIManager = + (FabricUIManager) UIManagerHelper.getUIManager(context, UIManagerType.FABRIC); + + LayoutAnimations LayoutAnimations = new LayoutAnimations(context); + + mHybridData = + initHybrid( + workletsModule, + Objects.requireNonNull(context.getJavaScriptContextHolder()).get(), + callInvokerHolder, + LayoutAnimations, + context.isBridgeless(), + fabricUIManager); + + prepareLayoutAnimations(LayoutAnimations); + + installJSIBindings(); + if (BuildConfig.DEBUG) { + checkCppVersion(); + } + } else { + mHybridData = + initDummyHybrid( + Objects.requireNonNull(context.getJavaScriptContextHolder()).get(), + callInvokerHolder, + context.isBridgeless()); + installDummyJSIBindings(); } } @@ -61,6 +73,10 @@ private native HybridData initHybrid( boolean isBridgeless, FabricUIManager fabricUIManager); + @OptIn(markerClass = FrameworkAPI.class) + private native HybridData initDummyHybrid( + long jsContext, CallInvokerHolderImpl jsCallInvokerHolder, boolean isBridgeless); + public native boolean isAnyHandlerWaitingForEvent(String eventName, int emitterReactTag); public native void performOperations(); diff --git a/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/NativeProxy.cpp b/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/NativeProxy.cpp index 729fab7dd163..944cc144e94f 100644 --- a/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/NativeProxy.cpp +++ b/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/NativeProxy.cpp @@ -55,6 +55,19 @@ NativeProxy::NativeProxy( #endif // RCT_NEW_ARCH_ENABLED } +NativeProxy::NativeProxy( + jni::alias_ref jThis, + jsi::Runtime *rnRuntime, + const std::shared_ptr &jsCallInvoker, + const bool isBridgeless) + : javaPart_(jni::make_global(jThis)), + rnRuntime_(rnRuntime), + dummyReanimatedModuleProxy_(std::make_shared( + *rnRuntime, + jsCallInvoker, + isBridgeless, + getIsReducedMotion())) {} + #ifdef RCT_NEW_ARCH_ENABLED void NativeProxy::commonInit( jni::alias_ref @@ -114,6 +127,17 @@ jni::local_ref NativeProxy::initHybrid( ); } +jni::local_ref NativeProxy::initDummyHybrid( + jni::alias_ref jThis, + jlong jsContext, + jni::alias_ref + jsCallInvokerHolder, + bool isBridgeless) { + auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker(); + return makeCxxInstance( + jThis, (jsi::Runtime *)jsContext, jsCallInvoker, isBridgeless); +} + #ifndef NDEBUG void NativeProxy::checkJavaVersion(jsi::Runtime &rnRuntime) { std::string javaVersion; @@ -155,9 +179,9 @@ void NativeProxy::injectCppVersion() { void NativeProxy::installJSIBindings() { jsi::Runtime &rnRuntime = *rnRuntime_; - WorkletRuntimeCollector::install(rnRuntime); + WorkletRuntimeCollector::install(&rnRuntime); RNRuntimeDecorator::decorate( - rnRuntime, + &rnRuntime, workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(), reanimatedModuleProxy_); #ifndef NDEBUG @@ -169,6 +193,11 @@ void NativeProxy::installJSIBindings() { setupLayoutAnimations(); } +void NativeProxy::installDummyJSIBindings() { + RNRuntimeDecorator::decorate( + rnRuntime_, nullptr, dummyReanimatedModuleProxy_); +} + bool NativeProxy::isAnyHandlerWaitingForEvent( const std::string &eventName, const int emitterReactTag) { @@ -190,7 +219,10 @@ bool NativeProxy::getIsReducedMotion() { void NativeProxy::registerNatives() { registerHybrid( {makeNativeMethod("initHybrid", NativeProxy::initHybrid), + makeNativeMethod("initDummyHybrid", NativeProxy::initDummyHybrid), makeNativeMethod("installJSIBindings", NativeProxy::installJSIBindings), + makeNativeMethod( + "installDummyJSIBindings", NativeProxy::installDummyJSIBindings), makeNativeMethod( "isAnyHandlerWaitingForEvent", NativeProxy::isAnyHandlerWaitingForEvent), @@ -413,7 +445,7 @@ void NativeProxy::handleEvent( } jsi::Runtime &rt = - workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); + *workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); jsi::Value payload; try { payload = jsi::Value::createFromJsonUtf8( @@ -520,7 +552,7 @@ void NativeProxy::setupLayoutAnimations() { if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { if (auto workletsModuleProxy = weakWorkletsModuleProxy.lock()) { jsi::Runtime &rt = - workletsModuleProxy->getUIWorkletRuntime()->getJSIRuntime(); + *workletsModuleProxy->getUIWorkletRuntime()->getJSIRuntime(); jsi::Object yogaValues(rt); for (const auto &entry : *values) { try { @@ -592,7 +624,7 @@ void NativeProxy::setupLayoutAnimations() { if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { if (auto workletsModuleProxy = weakWorkletsModuleProxy.lock()) { jsi::Runtime &rt = - workletsModuleProxy->getUIWorkletRuntime()->getJSIRuntime(); + *workletsModuleProxy->getUIWorkletRuntime()->getJSIRuntime(); reanimatedModuleProxy->layoutAnimationsManager() .cancelLayoutAnimation(rt, tag); } diff --git a/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/NativeProxy.h b/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/NativeProxy.h index af8bdbccfbd8..3481c83d0900 100644 --- a/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/NativeProxy.h +++ b/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/NativeProxy.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -160,6 +161,13 @@ class NativeProxy : public jni::HybridClass { #endif ); + static jni::local_ref initDummyHybrid( + jni::alias_ref jThis, + jlong jsContext, + jni::alias_ref + jsCallInvokerHolder, + const bool isBridgeless); + static void registerNatives(); ~NativeProxy(); @@ -170,6 +178,7 @@ class NativeProxy : public jni::HybridClass { jsi::Runtime *rnRuntime_; std::shared_ptr workletsModuleProxy_; std::shared_ptr reanimatedModuleProxy_; + std::shared_ptr dummyReanimatedModuleProxy_; jni::global_ref layoutAnimations_; #ifndef NDEBUG void checkJavaVersion(jsi::Runtime &); @@ -181,6 +190,7 @@ class NativeProxy : public jni::HybridClass { // std::shared_ptr eventListener_; #endif // RCT_NEW_ARCH_ENABLED void installJSIBindings(); + void installDummyJSIBindings(); #ifdef RCT_NEW_ARCH_ENABLED void synchronouslyUpdateUIProps( jsi::Runtime &rt, @@ -275,6 +285,12 @@ class NativeProxy : public jni::HybridClass { #endif ); + explicit NativeProxy( + jni::alias_ref jThis, + jsi::Runtime *rnRuntime, + const std::shared_ptr &jsCallInvoker, + const bool isBridgeless); + #ifdef RCT_NEW_ARCH_ENABLED void commonInit(jni::alias_ref &fabricUIManager); diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/nativeProxy/NativeProxyCommon.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/nativeProxy/NativeProxyCommon.java index 3b291a92d0ca..5f0c97c6c5f3 100644 --- a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/nativeProxy/NativeProxyCommon.java +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/nativeProxy/NativeProxyCommon.java @@ -75,6 +75,8 @@ protected NativeProxyCommon(ReactApplicationContext context) { protected native void installJSIBindings(); + protected native void installDummyJSIBindings(); + private void toggleSlowAnimations() { slowAnimationsEnabled = !slowAnimationsEnabled; if (slowAnimationsEnabled) { diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/WorkletsModule.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/WorkletsModule.java index d5249f8050be..69d32ff967f4 100644 --- a/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/WorkletsModule.java +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/WorkletsModule.java @@ -34,6 +34,12 @@ protected HybridData getHybridData() { private final WorkletsMessageQueueThread mMessageQueueThread = new WorkletsMessageQueueThread(); private final AndroidUIScheduler mAndroidUIScheduler; + private boolean mIsValid = true; + + public boolean isValid() { + return mIsValid; + } + public AndroidUIScheduler getAndroidUIScheduler() { return mAndroidUIScheduler; } @@ -69,6 +75,8 @@ public boolean installTurboModule(String valueUnpackerCode) { } public void invalidate() { + mIsValid = false; + // We have to destroy extra runtimes when invalidate is called. If we clean // it up later instead there's a chance the runtime will retain references // to invalidated memory and will crash on its destruction. diff --git a/packages/react-native-reanimated/apple/reanimated/apple/REAModule.mm b/packages/react-native-reanimated/apple/reanimated/apple/REAModule.mm index 0db7007fc224..36b0d2534ee9 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/REAModule.mm +++ b/packages/react-native-reanimated/apple/reanimated/apple/REAModule.mm @@ -16,6 +16,7 @@ #import #endif // RCT_NEW_ARCH_ENABLED +#import #import #import #import @@ -296,13 +297,26 @@ - (void)sendEventWithName:(NSString *)eventName body:(id)body assert(jsiRuntime != nullptr); - auto reanimatedModuleProxy = - reanimated::createReanimatedModule(self, self.bridge, jsCallInvoker, workletsModule, isBridgeless); - - auto &uiRuntime = [workletsModule getWorkletsModuleProxy]->getUIWorkletRuntime() -> getJSIRuntime(); - - jsi::Runtime &rnRuntime = *jsiRuntime; - [self commonInit:reanimatedModuleProxy withRnRuntime:rnRuntime withUIRuntime:uiRuntime]; + if ([workletsModule isValid]) { + auto reanimatedModuleProxy = + reanimated::createReanimatedModule(self, self.bridge, jsCallInvoker, workletsModule, isBridgeless); + + auto workletsModuleProxy = [workletsModule getWorkletsModuleProxy]; + + auto *uiRuntime = workletsModuleProxy->getUIWorkletRuntime()->getJSIRuntime(); + + [self commonInit:reanimatedModuleProxy withRnRuntime:jsiRuntime withUIRuntime:uiRuntime]; + } else { + // This path takes place when JavaScript reload is called + // after WorkletsModule is created and before ReaModule is. + // Therefore WorkletsModule would already be cleaned up + // and we only install a DummyReanimatedModuleProxy. It will be + // cleaned up when reload finishes anyway. + auto &rnRuntime = *jsiRuntime; + auto dummyReanimatedModuleProxy = + std::make_shared(rnRuntime, jsCallInvoker, isBridgeless, false); + RNRuntimeDecorator::decorate(jsiRuntime, nullptr, dummyReanimatedModuleProxy); + } return @YES; } @@ -316,8 +330,8 @@ - (void)sendEventWithName:(NSString *)eventName body:(id)body #endif // RCT_NEW_ARCH_ENABLED - (void)commonInit:(std::shared_ptr)reanimatedModuleProxy - withRnRuntime:(jsi::Runtime &)rnRuntime - withUIRuntime:(jsi::Runtime &)uiRuntime + withRnRuntime:(jsi::Runtime *)rnRuntime + withUIRuntime:(jsi::Runtime *)uiRuntime { WorkletRuntimeCollector::install(rnRuntime); RNRuntimeDecorator::decorate(rnRuntime, uiRuntime, reanimatedModuleProxy); @@ -326,7 +340,7 @@ - (void)commonInit:(std::shared_ptr)reanimatedModuleProxy weakReanimatedModuleProxy_ = reanimatedModuleProxy; if (self->_surfacePresenter != nil) { // reload, uiManager is null right now, we need to wait for `installReanimatedAfterReload` - [self injectDependencies:rnRuntime]; + [self injectDependencies:*rnRuntime]; } #endif // RCT_NEW_ARCH_ENABLED } diff --git a/packages/react-native-reanimated/apple/reanimated/apple/native/NativeProxy.mm b/packages/react-native-reanimated/apple/reanimated/apple/native/NativeProxy.mm index 1766a892b8dd..1c33cb9e924b 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/native/NativeProxy.mm +++ b/packages/react-native-reanimated/apple/reanimated/apple/native/NativeProxy.mm @@ -73,7 +73,7 @@ static inline bool getIsReducedMotion() auto reanimatedModuleProxy = std::make_shared( workletsModuleProxy, rnRuntime, jsInvoker, platformDepMethodsHolder, isBridgeless, getIsReducedMotion()); - commonInit(reaModule, workletsModuleProxy->getUIWorkletRuntime()->getJSIRuntime(), reanimatedModuleProxy); + commonInit(reaModule, *workletsModuleProxy->getUIWorkletRuntime()->getJSIRuntime(), reanimatedModuleProxy); // Layout Animation callbacks setup #ifdef RCT_NEW_ARCH_ENABLED // nothing diff --git a/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.h b/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.h index 5a3b01a10dd9..f026df1ce94a 100644 --- a/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.h +++ b/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.h @@ -6,4 +6,6 @@ - (std::shared_ptr)getWorkletsModuleProxy; +- (BOOL)isValid; + @end diff --git a/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.mm b/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.mm index 9dacb4b6f00f..68975d9d6ba7 100644 --- a/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.mm +++ b/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.mm @@ -22,6 +22,7 @@ @implementation WorkletsModule { #ifndef NDEBUG worklets::SingleInstanceChecker singleInstanceChecker_; #endif // NDEBUG + bool isValid_; } - (std::shared_ptr)getWorkletsModuleProxy @@ -29,6 +30,11 @@ @implementation WorkletsModule { return workletsModuleProxy_; } +- (BOOL)isValid +{ + return isValid_; +} + @synthesize moduleRegistry = _moduleRegistry; RCT_EXPORT_MODULE(WorkletsModule); @@ -48,12 +54,15 @@ @implementation WorkletsModule { workletsModuleProxy_ = std::make_shared( rnRuntime, valueUnpackerCodeStr, jsQueue, jsCallInvoker, jsScheduler, uiScheduler); RNRuntimeWorkletDecorator::decorate(rnRuntime, workletsModuleProxy_); + isValid_ = true; return @YES; } - (void)invalidate { + isValid_ = false; + // We have to destroy extra runtimes when invalidate is called. If we clean // it up later instead there's a chance the runtime will retain references // to invalidated memory and will crash on destruction.