diff --git a/README.md b/README.md index ab1de80b334..d7d5b17a312 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ > Reanimated 3 is here! Check out our [documentation page](https://docs.swmansion.com/react-native-reanimated/) for more information React Native Reanimated provides a more comprehensive, -low level abstraction for the Animated library API to be built -on top of and hence allow for much greater flexibility especially when it -comes to gesture based interactions. +low-level abstraction for the Animated library API on which to build, +allowing for much greater flexibility, especially when it comes to +gesture-based interactions. ### Nightly CI state diff --git a/package.json b/package.json index b207048a35d..9df784781ff 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "packages/react-native-reanimated", "packages/eslint-plugin-reanimated", "packages/react-native-reanimated/plugin", + "packages/react-native-worklets", "apps/common-app", "apps/paper-example", "apps/fabric-example", diff --git a/packages/docs-reanimated/docs/advanced/useEvent.mdx b/packages/docs-reanimated/docs/advanced/useEvent.mdx index 83ffc0d98b8..2525a6d5b8b 100644 --- a/packages/docs-reanimated/docs/advanced/useEvent.mdx +++ b/packages/docs-reanimated/docs/advanced/useEvent.mdx @@ -4,7 +4,7 @@ sidebar_position: 4 # useEvent -`useEvent` is a low-level hook. It returns event handler that will be called when native event occurs. You can use it to create custom event handler hooks, like [`useScrollViewOffset`](/docs/scroll/useScrollViewOffset/) or [`useAnimatedScrollHandler`](/docs/scroll/useAnimatedScrollHandler/). +`useEvent` is a low-level hook. It returns an event handler that will be called when a native event occurs. You can use it to create custom event handler hooks, like [`useScrollViewOffset`](/docs/scroll/useScrollViewOffset/) or [`useAnimatedScrollHandler`](/docs/scroll/useAnimatedScrollHandler/). ## Reference @@ -63,22 +63,22 @@ type EventHandlerProcessed< #### `handler` -Function that receives event object with native payload, that can be passed to custom handler hook's worklets. +Function that receives an event object with a native payload, which can be passed to the custom handler hook's worklets. - `event` - event object. The payload can differ depending on the type of the event. #### `eventNames` -Array of event names that will be handled by handler. +Array of event names that will be handled by the handler. #### `rebuild` -Value indicating whether handler should be rebuilt. +Value indicating whether the handler should be rebuilt. ### Returns -The hook returns event handler that will be invoked when native event is dispatched. That handler may be connected to multiple components and will be invoked for each one's specific events. +The hook returns an event handler that will be invoked when a native event is dispatched. That handler may be connected to multiple components and will be invoked for each one's specific events. ## Example diff --git a/packages/docs-reanimated/versioned_docs/version-2.x/api/hooks/useEvent.md b/packages/docs-reanimated/versioned_docs/version-2.x/api/hooks/useEvent.md index 15325f7fd43..8abbbd924ac 100644 --- a/packages/docs-reanimated/versioned_docs/version-2.x/api/hooks/useEvent.md +++ b/packages/docs-reanimated/versioned_docs/version-2.x/api/hooks/useEvent.md @@ -4,13 +4,13 @@ title: useEvent sidebar_label: useEvent --- -This is low-level hook returning event handler that will be invoked with native events, which should be used in order to create custom event handler hook like `useAnimatedGestureHandler` or `useAnimatedScrollHandler`. +This is a low-level hook returning an event handler that will be invoked with native events, which should be used in order to create custom event handler hooks, like `useAnimatedGestureHandler` or `useAnimatedScrollHandler`. ### Arguments #### `handler` [function] -Handler will receive event object with native payload, that can be passed to custom handler hook's worklets. +Handler that will receive an event object with a native payload, which can be passed to the custom handler hook's worklets. - `event` [object] - event object. The payload can differ depending on the type of the event. @@ -21,11 +21,11 @@ Array of event names that will be handled by handler. #### `rebuilt` [boolean] -Value indicating whether handler should be rebuilt. +Value indicating whether the handler should be rebuilt. ### Returns -The hook returns event handler that will be invoked when native event is dispatched. +The hook returns an event handler that will be invoked when a native event is dispatched. ## Example diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 0aeda945bb4..f55b383bdc8 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -1,7 +1,7 @@ #ifdef RCT_NEW_ARCH_ENABLED #include -#include +#include #include #include diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 8fd9a89d7fa..a37f43a414a 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -19,7 +19,7 @@ namespace reanimated { -class NativeReanimatedModule; +class ReanimatedModuleProxy; using namespace facebook; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp similarity index 89% rename from packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp rename to packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp index b1f430a89cd..cc689308bd7 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp @@ -1,5 +1,4 @@ -#include -#include +#include #include #include #include @@ -51,28 +50,22 @@ using namespace facebook; namespace reanimated { -NativeReanimatedModule::NativeReanimatedModule( - const std::shared_ptr &nativeWorkletsModule, +ReanimatedModuleProxy::ReanimatedModuleProxy( + const std::shared_ptr &workletsModuleProxy, jsi::Runtime &rnRuntime, - const std::shared_ptr &jsScheduler, - const std::shared_ptr &jsQueue, - const std::shared_ptr &uiScheduler, + const std::shared_ptr &jsCallInvoker, const PlatformDepMethodsHolder &platformDepMethodsHolder, const bool isBridgeless, const bool isReducedMotion) - : NativeReanimatedModuleSpec( - isBridgeless ? nullptr : jsScheduler->getJSCallInvoker()), + : ReanimatedModuleProxySpec(jsCallInvoker), isBridgeless_(isBridgeless), isReducedMotion_(isReducedMotion), - jsQueue_(jsQueue), - nativeWorkletsModule_(nativeWorkletsModule), - jsScheduler_(jsScheduler), - uiScheduler_(uiScheduler), - valueUnpackerCode_(nativeWorkletsModule->getValueUnpackerCode()), + workletsModuleProxy_(workletsModuleProxy), + valueUnpackerCode_(workletsModuleProxy->getValueUnpackerCode()), uiWorkletRuntime_(std::make_shared( rnRuntime, - jsQueue, - jsScheduler_, + workletsModuleProxy->getJSQueue(), + workletsModuleProxy->getJSScheduler(), "Reanimated UI runtime", true /* supportsLocking */, valueUnpackerCode_)), @@ -83,7 +76,8 @@ NativeReanimatedModule::NativeReanimatedModule( onRender(timestampMs); }), animatedSensorModule_(platformDepMethodsHolder), - jsLogger_(std::make_shared(jsScheduler_)), + jsLogger_( + std::make_shared(workletsModuleProxy->getJSScheduler())), layoutAnimationsManager_( std::make_shared(jsLogger_)), #ifdef RCT_NEW_ARCH_ENABLED @@ -103,7 +97,7 @@ NativeReanimatedModule::NativeReanimatedModule( commonInit(platformDepMethodsHolder); } -void NativeReanimatedModule::commonInit( +void ReanimatedModuleProxy::commonInit( const PlatformDepMethodsHolder &platformDepMethodsHolder) { auto requestAnimationFrame = [this](jsi::Runtime &rt, const jsi::Value &callback) { @@ -195,20 +189,23 @@ void NativeReanimatedModule::commonInit( platformDepMethodsHolder.maybeFlushUIUpdatesQueueFunction); } -NativeReanimatedModule::~NativeReanimatedModule() { +ReanimatedModuleProxy::~ReanimatedModuleProxy() { // event handler registry and frame callbacks store some JSI values from UI // runtime, so they have to go away before we tear down the runtime eventHandlerRegistry_.reset(); frameCallbacks_.clear(); +#ifdef RCT_NEW_ARCH_ENABLED + operationsInBatch_.clear(); +#endif // RCT_NEW_ARCH_ENABLED uiWorkletRuntime_.reset(); } -void NativeReanimatedModule::scheduleOnUI( +void ReanimatedModuleProxy::scheduleOnUI( jsi::Runtime &rt, const jsi::Value &worklet) { auto shareableWorklet = extractShareableOrThrow( rt, worklet, "[Reanimated] Only worklets can be scheduled to run on UI."); - uiScheduler_->scheduleOnUI(COPY_CAPTURE_WITH_THIS { + workletsModuleProxy_->getUIScheduler()->scheduleOnUI(COPY_CAPTURE_WITH_THIS { #if JS_RUNTIME_HERMES // JSI's scope defined here allows for JSI-objects to be cleared up // after each runtime loop. Within these loops we typically create some @@ -221,31 +218,30 @@ void NativeReanimatedModule::scheduleOnUI( }); } -jsi::Value NativeReanimatedModule::executeOnUIRuntimeSync( +jsi::Value ReanimatedModuleProxy::executeOnUIRuntimeSync( jsi::Runtime &rt, const jsi::Value &worklet) { return uiWorkletRuntime_->executeSync(rt, worklet); } -jsi::Value NativeReanimatedModule::createWorkletRuntime( +jsi::Value ReanimatedModuleProxy::createWorkletRuntime( jsi::Runtime &rt, const jsi::Value &name, const jsi::Value &initializer) { auto workletRuntime = std::make_shared( rt, - jsQueue_, - jsScheduler_, + workletsModuleProxy_->getJSQueue(), + workletsModuleProxy_->getJSScheduler(), name.asString(rt).utf8(rt), false /* supportsLocking */, valueUnpackerCode_); auto initializerShareable = extractShareableOrThrow( rt, initializer, "[Reanimated] Initializer must be a worklet."); workletRuntime->runGuarded(initializerShareable); - ReanimatedWorkletRuntimeDecorator::decorate(workletRuntime->getJSIRuntime()); return jsi::Object::createFromHostObject(rt, workletRuntime); } -jsi::Value NativeReanimatedModule::scheduleOnRuntime( +jsi::Value ReanimatedModuleProxy::scheduleOnRuntime( jsi::Runtime &rt, const jsi::Value &workletRuntimeValue, const jsi::Value &shareableWorkletValue) { @@ -253,7 +249,7 @@ jsi::Value NativeReanimatedModule::scheduleOnRuntime( return jsi::Value::undefined(); } -jsi::Value NativeReanimatedModule::registerEventHandler( +jsi::Value ReanimatedModuleProxy::registerEventHandler( jsi::Runtime &rt, const jsi::Value &worklet, const jsi::Value &eventName, @@ -266,7 +262,7 @@ jsi::Value NativeReanimatedModule::registerEventHandler( rt, worklet, "[Reanimated] Event handler must be a worklet."); int emitterReactTagInt = emitterReactTag.asNumber(); - uiScheduler_->scheduleOnUI(COPY_CAPTURE_WITH_THIS { + workletsModuleProxy_->getUIScheduler()->scheduleOnUI(COPY_CAPTURE_WITH_THIS { auto handler = std::make_shared( newRegistrationId, eventNameStr, emitterReactTagInt, handlerShareable); eventHandlerRegistry_->registerEventHandler(std::move(handler)); @@ -275,11 +271,11 @@ jsi::Value NativeReanimatedModule::registerEventHandler( return jsi::Value(static_cast(newRegistrationId)); } -void NativeReanimatedModule::unregisterEventHandler( +void ReanimatedModuleProxy::unregisterEventHandler( jsi::Runtime &, const jsi::Value ®istrationId) { uint64_t id = registrationId.asNumber(); - uiScheduler_->scheduleOnUI( + workletsModuleProxy_->getUIScheduler()->scheduleOnUI( COPY_CAPTURE_WITH_THIS { eventHandlerRegistry_->unregisterEventHandler(id); }); @@ -299,7 +295,7 @@ static inline std::string intColorToHex(const int val) { return hexColor; } -std::string NativeReanimatedModule::obtainPropFromShadowNode( +std::string ReanimatedModuleProxy::obtainPropFromShadowNode( jsi::Runtime &rt, const std::string &propName, const ShadowNode::Shared &shadowNode) { @@ -342,7 +338,7 @@ std::string NativeReanimatedModule::obtainPropFromShadowNode( "` with function `getViewProp` is not supported")); } -jsi::Value NativeReanimatedModule::getViewProp( +jsi::Value ReanimatedModuleProxy::getViewProp( jsi::Runtime &rnRuntime, const jsi::Value &shadowNodeWrapper, const jsi::Value &propName, @@ -351,23 +347,24 @@ jsi::Value NativeReanimatedModule::getViewProp( const auto funPtr = std::make_shared( callback.getObject(rnRuntime).asFunction(rnRuntime)); const auto shadowNode = shadowNodeFromValue(rnRuntime, shadowNodeWrapper); - uiScheduler_->scheduleOnUI([=]() { + workletsModuleProxy_->getUIScheduler()->scheduleOnUI(COPY_CAPTURE_WITH_THIS { jsi::Runtime &uiRuntime = uiWorkletRuntime_->getJSIRuntime(); const auto resultStr = obtainPropFromShadowNode(uiRuntime, propNameStr, shadowNode); - jsScheduler_->scheduleOnJS([=](jsi::Runtime &rnRuntime) { - const auto resultValue = - jsi::String::createFromUtf8(rnRuntime, resultStr); - funPtr->call(rnRuntime, resultValue); - }); + workletsModuleProxy_->getJSScheduler()->scheduleOnJS( + [=](jsi::Runtime &rnRuntime) { + const auto resultValue = + jsi::String::createFromUtf8(rnRuntime, resultStr); + funPtr->call(rnRuntime, resultValue); + }); }); return jsi::Value::undefined(); } #else -jsi::Value NativeReanimatedModule::getViewProp( +jsi::Value ReanimatedModuleProxy::getViewProp( jsi::Runtime &rnRuntime, const jsi::Value &viewTag, const jsi::Value &propName, @@ -378,7 +375,7 @@ jsi::Value NativeReanimatedModule::getViewProp( const int viewTagInt = viewTag.asNumber(); - uiScheduler_->scheduleOnUI( + workletsModuleProxy_->getUIScheduler()->scheduleOnUI( COPY_CAPTURE_WITH_THIS () { @@ -388,8 +385,8 @@ jsi::Value NativeReanimatedModule::getViewProp( const auto resultValue = obtainPropFunction_(uiRuntime, viewTagInt, propNameValue); const auto resultStr = resultValue.asString(uiRuntime).utf8(uiRuntime); - - jsScheduler_->scheduleOnJS([=](jsi::Runtime &rnRuntime) { + const auto jsScheduler = workletsModuleProxy_->getJSScheduler(); + jsScheduler->scheduleOnJS([=](jsi::Runtime &rnRuntime) { const auto resultValue = jsi::String::createFromUtf8(rnRuntime, resultStr); funPtr->call(rnRuntime, resultValue); @@ -400,14 +397,14 @@ jsi::Value NativeReanimatedModule::getViewProp( #endif -jsi::Value NativeReanimatedModule::enableLayoutAnimations( +jsi::Value ReanimatedModuleProxy::enableLayoutAnimations( jsi::Runtime &, const jsi::Value &config) { FeaturesConfig::setLayoutAnimationEnabled(config.getBool()); return jsi::Value::undefined(); } -jsi::Value NativeReanimatedModule::configureProps( +jsi::Value ReanimatedModuleProxy::configureProps( jsi::Runtime &rt, const jsi::Value &uiProps, const jsi::Value &nativeProps) { @@ -430,7 +427,7 @@ jsi::Value NativeReanimatedModule::configureProps( return jsi::Value::undefined(); } -jsi::Value NativeReanimatedModule::configureLayoutAnimationBatch( +jsi::Value ReanimatedModuleProxy::configureLayoutAnimationBatch( jsi::Runtime &rt, const jsi::Value &layoutAnimationsBatch) { auto array = layoutAnimationsBatch.asObject(rt).asArray(rt); @@ -466,7 +463,7 @@ jsi::Value NativeReanimatedModule::configureLayoutAnimationBatch( return jsi::Value::undefined(); } -void NativeReanimatedModule::setShouldAnimateExiting( +void ReanimatedModuleProxy::setShouldAnimateExiting( jsi::Runtime &rt, const jsi::Value &viewTag, const jsi::Value &shouldAnimate) { @@ -474,21 +471,21 @@ void NativeReanimatedModule::setShouldAnimateExiting( viewTag.asNumber(), shouldAnimate.getBool()); } -bool NativeReanimatedModule::isAnyHandlerWaitingForEvent( +bool ReanimatedModuleProxy::isAnyHandlerWaitingForEvent( const std::string &eventName, const int emitterReactTag) { return eventHandlerRegistry_->isAnyHandlerWaitingForEvent( eventName, emitterReactTag); } -void NativeReanimatedModule::requestAnimationFrame( +void ReanimatedModuleProxy::requestAnimationFrame( jsi::Runtime &rt, const jsi::Value &callback) { frameCallbacks_.push_back(std::make_shared(rt, callback)); maybeRequestRender(); } -void NativeReanimatedModule::maybeRequestRender() { +void ReanimatedModuleProxy::maybeRequestRender() { if (!renderRequested_) { renderRequested_ = true; jsi::Runtime &uiRuntime = uiWorkletRuntime_->getJSIRuntime(); @@ -496,7 +493,7 @@ void NativeReanimatedModule::maybeRequestRender() { } } -void NativeReanimatedModule::onRender(double timestampMs) { +void ReanimatedModuleProxy::onRender(double timestampMs) { auto callbacks = std::move(frameCallbacks_); frameCallbacks_.clear(); jsi::Runtime &uiRuntime = uiWorkletRuntime_->getJSIRuntime(); @@ -506,7 +503,7 @@ void NativeReanimatedModule::onRender(double timestampMs) { } } -jsi::Value NativeReanimatedModule::registerSensor( +jsi::Value ReanimatedModuleProxy::registerSensor( jsi::Runtime &rt, const jsi::Value &sensorType, const jsi::Value &interval, @@ -521,18 +518,18 @@ jsi::Value NativeReanimatedModule::registerSensor( sensorDataHandler); } -void NativeReanimatedModule::unregisterSensor( +void ReanimatedModuleProxy::unregisterSensor( jsi::Runtime &, const jsi::Value &sensorId) { animatedSensorModule_.unregisterSensor(sensorId); } -void NativeReanimatedModule::cleanupSensors() { +void ReanimatedModuleProxy::cleanupSensors() { animatedSensorModule_.unregisterAllSensors(); } #ifdef RCT_NEW_ARCH_ENABLED -bool NativeReanimatedModule::isThereAnyLayoutProp( +bool ReanimatedModuleProxy::isThereAnyLayoutProp( jsi::Runtime &rt, const jsi::Object &props) { const jsi::Array propNames = props.getPropertyNames(rt); @@ -548,7 +545,7 @@ bool NativeReanimatedModule::isThereAnyLayoutProp( return false; } -jsi::Value NativeReanimatedModule::filterNonAnimatableProps( +jsi::Value ReanimatedModuleProxy::filterNonAnimatableProps( jsi::Runtime &rt, const jsi::Value &props) { jsi::Object nonAnimatableProps(rt); @@ -572,7 +569,7 @@ jsi::Value NativeReanimatedModule::filterNonAnimatableProps( } #endif // RCT_NEW_ARCH_ENABLED -bool NativeReanimatedModule::handleEvent( +bool ReanimatedModuleProxy::handleEvent( const std::string &eventName, const int emitterReactTag, const jsi::Value &payload, @@ -586,7 +583,7 @@ bool NativeReanimatedModule::handleEvent( } #ifdef RCT_NEW_ARCH_ENABLED -bool NativeReanimatedModule::handleRawEvent( +bool ReanimatedModuleProxy::handleRawEvent( const RawEvent &rawEvent, double currentTime) { const EventTarget *eventTarget = rawEvent.eventTarget.get(); @@ -617,7 +614,7 @@ bool NativeReanimatedModule::handleRawEvent( return res; } -void NativeReanimatedModule::updateProps( +void ReanimatedModuleProxy::updateProps( jsi::Runtime &rt, const jsi::Value &operations) { auto array = operations.asObject(rt).asArray(rt); @@ -632,7 +629,7 @@ void NativeReanimatedModule::updateProps( } } -void NativeReanimatedModule::performOperations() { +void ReanimatedModuleProxy::performOperations() { if (operationsInBatch_.empty() && tagsToRemove_.empty()) { // nothing to do return; @@ -757,7 +754,7 @@ void NativeReanimatedModule::performOperations() { } } -void NativeReanimatedModule::removeFromPropsRegistry( +void ReanimatedModuleProxy::removeFromPropsRegistry( jsi::Runtime &rt, const jsi::Value &viewTags) { auto array = viewTags.asObject(rt).asArray(rt); @@ -766,7 +763,7 @@ void NativeReanimatedModule::removeFromPropsRegistry( } } -void NativeReanimatedModule::dispatchCommand( +void ReanimatedModuleProxy::dispatchCommand( jsi::Runtime &rt, const jsi::Value &shadowNodeValue, const jsi::Value &commandNameValue, @@ -777,7 +774,7 @@ void NativeReanimatedModule::dispatchCommand( uiManager_->dispatchCommand(shadowNode, commandName, args); } -jsi::String NativeReanimatedModule::obtainProp( +jsi::String ReanimatedModuleProxy::obtainProp( jsi::Runtime &rt, const jsi::Value &shadowNodeWrapper, const jsi::Value &propName) { @@ -789,7 +786,7 @@ jsi::String NativeReanimatedModule::obtainProp( return jsi::String::createFromUtf8(rt, resultStr); } -jsi::Value NativeReanimatedModule::measure( +jsi::Value ReanimatedModuleProxy::measure( jsi::Runtime &rt, const jsi::Value &shadowNodeValue) { // based on implementation from UIManagerBinding.cpp @@ -833,7 +830,7 @@ jsi::Value NativeReanimatedModule::measure( return result; } -void NativeReanimatedModule::initializeFabric( +void ReanimatedModuleProxy::initializeFabric( const std::shared_ptr &uiManager) { uiManager_ = uiManager; @@ -845,7 +842,7 @@ void NativeReanimatedModule::initializeFabric( propsRegistry_, uiManager_, layoutAnimationsProxy_); } -void NativeReanimatedModule::initializeLayoutAnimationsProxy() { +void ReanimatedModuleProxy::initializeLayoutAnimationsProxy() { uiManager_->setAnimationDelegate(nullptr); auto scheduler = reinterpret_cast(uiManager_->getDelegate()); auto componentDescriptorRegistry = @@ -860,13 +857,13 @@ void NativeReanimatedModule::initializeLayoutAnimationsProxy() { componentDescriptorRegistry, scheduler->getContextContainer(), uiWorkletRuntime_->getJSIRuntime(), - uiScheduler_); + workletsModuleProxy_->getUIScheduler()); } } #endif // RCT_NEW_ARCH_ENABLED -jsi::Value NativeReanimatedModule::subscribeForKeyboardEvents( +jsi::Value ReanimatedModuleProxy::subscribeForKeyboardEvents( jsi::Runtime &rt, const jsi::Value &handlerWorklet, const jsi::Value &isStatusBarTranslucent, @@ -886,7 +883,7 @@ jsi::Value NativeReanimatedModule::subscribeForKeyboardEvents( isNavigationBarTranslucent.getBool()); } -void NativeReanimatedModule::unsubscribeFromKeyboardEvents( +void ReanimatedModuleProxy::unsubscribeFromKeyboardEvents( jsi::Runtime &, const jsi::Value &listenerId) { unsubscribeFromKeyboardEventsFunction_(listenerId.asNumber()); diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.h b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h similarity index 88% rename from packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.h rename to packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h index 29bfecc7a63..6e71a2e5654 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h @@ -2,7 +2,7 @@ #include #include -#include +#include #include #ifdef RCT_NEW_ARCH_ENABLED @@ -12,7 +12,7 @@ #include #endif // RCT_NEW_ARCH_ENABLED -#include +#include #include #include #include @@ -30,19 +30,17 @@ namespace reanimated { -class NativeReanimatedModule : public NativeReanimatedModuleSpec { +class ReanimatedModuleProxy : public ReanimatedModuleProxySpec { public: - NativeReanimatedModule( - const std::shared_ptr &nativeWorkletsModule, + ReanimatedModuleProxy( + const std::shared_ptr &workletsModuleProxy, jsi::Runtime &rnRuntime, - const std::shared_ptr &jsScheduler, - const std::shared_ptr &jsQueue, - const std::shared_ptr &uiScheduler, + const std::shared_ptr &jsCallInvoker, const PlatformDepMethodsHolder &platformDepMethodsHolder, const bool isBridgeless, const bool isReducedMotion); - ~NativeReanimatedModule(); + ~ReanimatedModuleProxy(); void scheduleOnUI(jsi::Runtime &rt, const jsi::Value &worklet) override; jsi::Value executeOnUIRuntimeSync(jsi::Runtime &rt, const jsi::Value &worklet) @@ -175,9 +173,9 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec { return isReducedMotion_; } - [[nodiscard]] inline std::shared_ptr - getNativeWorkletsModule() const { - return nativeWorkletsModule_; + [[nodiscard]] inline std::shared_ptr + getWorkletsModuleProxy() const { + return workletsModuleProxy_; } private: @@ -194,10 +192,7 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec { const bool isBridgeless_; const bool isReducedMotion_; - const std::shared_ptr jsQueue_; - const std::shared_ptr nativeWorkletsModule_; - const std::shared_ptr jsScheduler_; - const std::shared_ptr uiScheduler_; + const std::shared_ptr workletsModuleProxy_; const std::string valueUnpackerCode_; std::shared_ptr uiWorkletRuntime_; @@ -237,8 +232,7 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec { const KeyboardEventUnsubscribeFunction unsubscribeFromKeyboardEventsFunction_; #ifndef NDEBUG - worklets::SingleInstanceChecker - singleInstanceChecker_; + worklets::SingleInstanceChecker singleInstanceChecker_; #endif // NDEBUG }; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.cpp similarity index 83% rename from packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.cpp rename to packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.cpp index 036f201a88d..d797483bcf1 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.cpp @@ -1,9 +1,9 @@ -#include +#include #include #define REANIMATED_SPEC_PREFIX(FN_NAME) \ - __hostFunction_NativeReanimatedModuleSpec_##FN_NAME + __hostFunction_ReanimatedModuleProxySpec_##FN_NAME namespace reanimated { @@ -14,7 +14,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(scheduleOnUI)( TurboModule &turboModule, const jsi::Value *args, size_t) { - static_cast(&turboModule) + static_cast(&turboModule) ->scheduleOnUI(rt, std::move(args[0])); return jsi::Value::undefined(); } @@ -24,7 +24,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(executeOnUIRuntimeSync)( TurboModule &turboModule, const jsi::Value *args, size_t) { - return static_cast(&turboModule) + return static_cast(&turboModule) ->executeOnUIRuntimeSync(rt, std::move(args[0])); } @@ -33,7 +33,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(createWorkletRuntime)( TurboModule &turboModule, const jsi::Value *args, size_t) { - return static_cast(&turboModule) + return static_cast(&turboModule) ->createWorkletRuntime(rt, std::move(args[0]), std::move(args[1])); } @@ -42,7 +42,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(scheduleOnRuntime)( TurboModule &turboModule, const jsi::Value *args, size_t) { - return static_cast(&turboModule) + return static_cast(&turboModule) ->scheduleOnRuntime(rt, std::move(args[0]), std::move(args[1])); } @@ -51,7 +51,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(registerEventHandler)( TurboModule &turboModule, const jsi::Value *args, size_t) { - return static_cast(&turboModule) + return static_cast(&turboModule) ->registerEventHandler( rt, std::move(args[0]), std::move(args[1]), std::move(args[2])); } @@ -61,7 +61,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(unregisterEventHandler)( TurboModule &turboModule, const jsi::Value *args, size_t) { - static_cast(&turboModule) + static_cast(&turboModule) ->unregisterEventHandler(rt, std::move(args[0])); return jsi::Value::undefined(); } @@ -71,7 +71,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(getViewProp)( TurboModule &turboModule, const jsi::Value *args, size_t) { - static_cast(&turboModule) + static_cast(&turboModule) ->getViewProp( rt, std::move(args[0]), std::move(args[1]), std::move(args[2])); return jsi::Value::undefined(); @@ -82,7 +82,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(enableLayoutAnimations)( TurboModule &turboModule, const jsi::Value *args, size_t) { - static_cast(&turboModule) + static_cast(&turboModule) ->enableLayoutAnimations(rt, std::move(args[0])); return jsi::Value::undefined(); } @@ -92,7 +92,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(registerSensor)( TurboModule &turboModule, const jsi::Value *args, size_t) { - return static_cast(&turboModule) + return static_cast(&turboModule) ->registerSensor( rt, std::move(args[0]), @@ -106,7 +106,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(unregisterSensor)( TurboModule &turboModule, const jsi::Value *args, size_t) { - static_cast(&turboModule) + static_cast(&turboModule) ->unregisterSensor(rt, std::move(args[0])); return jsi::Value::undefined(); } @@ -116,7 +116,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(configureProps)( TurboModule &turboModule, const jsi::Value *args, size_t) { - static_cast(&turboModule) + static_cast(&turboModule) ->configureProps(rt, std::move(args[0]), std::move(args[1])); return jsi::Value::undefined(); } @@ -126,7 +126,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(subscribeForKeyboardEvents)( TurboModule &turboModule, const jsi::Value *args, size_t) { - return static_cast(&turboModule) + return static_cast(&turboModule) ->subscribeForKeyboardEvents( rt, std::move(args[0]), std::move(args[1]), std::move(args[2])); } @@ -136,7 +136,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(unsubscribeFromKeyboardEvents)( TurboModule &turboModule, const jsi::Value *args, size_t) { - static_cast(&turboModule) + static_cast(&turboModule) ->unsubscribeFromKeyboardEvents(rt, std::move(args[0])); return jsi::Value::undefined(); } @@ -146,7 +146,7 @@ static jsi::Value REANIMATED_SPEC_PREFIX(configureLayoutAnimationBatch)( TurboModule &turboModule, const jsi::Value *args, size_t) { - return static_cast(&turboModule) + return static_cast(&turboModule) ->configureLayoutAnimationBatch(rt, std::move(args[0])); } @@ -155,12 +155,12 @@ static jsi::Value REANIMATED_SPEC_PREFIX(setShouldAnimateExiting)( TurboModule &turboModule, const jsi::Value *args, size_t) { - static_cast(&turboModule) + static_cast(&turboModule) ->setShouldAnimateExiting(rt, std::move(args[0]), std::move(args[1])); return jsi::Value::undefined(); } -NativeReanimatedModuleSpec::NativeReanimatedModuleSpec( +ReanimatedModuleProxySpec::ReanimatedModuleProxySpec( const std::shared_ptr &jsInvoker) : TurboModule("NativeReanimated", jsInvoker) { methodMap_["scheduleOnUI"] = diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.h b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.h similarity index 96% rename from packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.h rename to packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.h index a9a85ad1a6c..3f82813abff 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModuleSpec.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.h @@ -12,9 +12,9 @@ using namespace react; namespace reanimated { -class JSI_EXPORT NativeReanimatedModuleSpec : public TurboModule { +class JSI_EXPORT ReanimatedModuleProxySpec : public TurboModule { protected: - explicit NativeReanimatedModuleSpec( + explicit ReanimatedModuleProxySpec( const std::shared_ptr &jsInvoker); public: diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.cpp index 660145a40f7..5bf60c154a8 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.cpp @@ -5,10 +5,10 @@ namespace reanimated { void RNRuntimeDecorator::decorate( jsi::Runtime &rnRuntime, - const std::shared_ptr &nativeReanimatedModule) { + const std::shared_ptr &reanimatedModuleProxy) { rnRuntime.global().setProperty(rnRuntime, "_WORKLET", false); - jsi::Runtime &uiRuntime = nativeReanimatedModule->getUIRuntime(); + jsi::Runtime &uiRuntime = reanimatedModuleProxy->getUIRuntime(); auto workletRuntimeValue = rnRuntime.global() .getPropertyAsObject(rnRuntime, "ArrayBuffer") @@ -29,22 +29,22 @@ void RNRuntimeDecorator::decorate( rnRuntime.global().setProperty(rnRuntime, "_IS_FABRIC", isFabric); rnRuntime.global().setProperty( - rnRuntime, "_IS_BRIDGELESS", nativeReanimatedModule->isBridgeless()); + rnRuntime, "_IS_BRIDGELESS", reanimatedModuleProxy->isBridgeless()); #ifndef NDEBUG - checkJSVersion(rnRuntime, nativeReanimatedModule->getJSLogger()); + checkJSVersion(rnRuntime, reanimatedModuleProxy->getJSLogger()); #endif // NDEBUG injectReanimatedCppVersion(rnRuntime); rnRuntime.global().setProperty( rnRuntime, "_REANIMATED_IS_REDUCED_MOTION", - nativeReanimatedModule->isReducedMotion()); + reanimatedModuleProxy->isReducedMotion()); rnRuntime.global().setProperty( rnRuntime, "__reanimatedModuleProxy", - jsi::Object::createFromHostObject(rnRuntime, nativeReanimatedModule)); + 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 ff18f2c2dc6..ac568a678b2 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/RNRuntimeDecorator.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include @@ -14,7 +14,7 @@ class RNRuntimeDecorator { public: static void decorate( jsi::Runtime &rnRuntime, - const std::shared_ptr &nativeReanimatedModule); + const std::shared_ptr &reanimatedModuleProxy); }; } // namespace reanimated diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/ReanimatedWorkletRuntimeDecorator.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/ReanimatedWorkletRuntimeDecorator.cpp deleted file mode 100644 index acd03e73677..00000000000 --- a/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/ReanimatedWorkletRuntimeDecorator.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include - -#include -#include -#include - -namespace reanimated { - -using namespace worklets; - -void ReanimatedWorkletRuntimeDecorator::decorate(jsi::Runtime &rt) { - jsi_utils::installJsiFunction( - rt, "_log", [](jsi::Runtime &rt, const jsi::Value &value) { - PlatformLogger::log(stringifyJSIValue(rt, value)); - }); -} - -} // namespace reanimated diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/ReanimatedWorkletRuntimeDecorator.h b/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/ReanimatedWorkletRuntimeDecorator.h deleted file mode 100644 index da7f2a923cd..00000000000 --- a/packages/react-native-reanimated/Common/cpp/reanimated/RuntimeDecorators/ReanimatedWorkletRuntimeDecorator.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -using namespace facebook; - -namespace reanimated { - -class ReanimatedWorkletRuntimeDecorator { - public: - static void decorate(jsi::Runtime &rt); -}; - -} // namespace reanimated diff --git a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/NativeWorkletsModule.h b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/NativeWorkletsModule.h deleted file mode 100644 index 5912056fb16..00000000000 --- a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/NativeWorkletsModule.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace worklets { - -class NativeWorkletsModule : public NativeWorkletsModuleSpec { - public: - explicit NativeWorkletsModule(const std::string &valueUnpackerCode); - - ~NativeWorkletsModule(); - - jsi::Value makeShareableClone( - jsi::Runtime &rt, - const jsi::Value &value, - const jsi::Value &shouldRetainRemote, - const jsi::Value &nativeStateSource) override; - - [[nodiscard]] inline std::string getValueUnpackerCode() const { - return valueUnpackerCode_; - } - - private: - const std::string valueUnpackerCode_; -#ifndef NDEBUG - SingleInstanceChecker singleInstanceChecker_; -#endif // NDEBUG -}; - -} // namespace worklets diff --git a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/NativeWorkletsModule.cpp b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.cpp similarity index 52% rename from packages/react-native-reanimated/Common/cpp/worklets/NativeModules/NativeWorkletsModule.cpp rename to packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.cpp index 80270752948..e71837b1199 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/NativeWorkletsModule.cpp +++ b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.cpp @@ -5,7 +5,7 @@ #include #endif // RCT_NEW_ARCH_ENABLED -#include +#include #include #ifdef __ANDROID__ @@ -18,13 +18,21 @@ using namespace facebook; namespace worklets { -NativeWorkletsModule::NativeWorkletsModule(const std::string &valueUnpackerCode) - : NativeWorkletsModuleSpec(nullptr), - valueUnpackerCode_(valueUnpackerCode) {} - -NativeWorkletsModule::~NativeWorkletsModule() {} - -jsi::Value NativeWorkletsModule::makeShareableClone( +WorkletsModuleProxy::WorkletsModuleProxy( + const std::string &valueUnpackerCode, + const std::shared_ptr &jsQueue, + const std::shared_ptr &jsCallInvoker, + const std::shared_ptr &jsScheduler, + const std::shared_ptr &uiScheduler) + : WorkletsModuleProxySpec(jsCallInvoker), + valueUnpackerCode_(valueUnpackerCode), + jsQueue_(jsQueue), + jsScheduler_(jsScheduler), + uiScheduler_(uiScheduler) {} + +WorkletsModuleProxy::~WorkletsModuleProxy() {} + +jsi::Value WorkletsModuleProxy::makeShareableClone( jsi::Runtime &rt, const jsi::Value &value, const jsi::Value &shouldRetainRemote, diff --git a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.h b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.h new file mode 100644 index 00000000000..dc7cee4cf27 --- /dev/null +++ b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace worklets { + +class WorkletsModuleProxy : public WorkletsModuleProxySpec { + public: + explicit WorkletsModuleProxy( + const std::string &valueUnpackerCode, + const std::shared_ptr &jsQueue, + const std::shared_ptr &jsCallInvoker, + const std::shared_ptr &jsScheduler, + const std::shared_ptr &uiScheduler); + + ~WorkletsModuleProxy(); + + jsi::Value makeShareableClone( + jsi::Runtime &rt, + const jsi::Value &value, + const jsi::Value &shouldRetainRemote, + const jsi::Value &nativeStateSource) override; + + [[nodiscard]] inline std::string getValueUnpackerCode() const { + return valueUnpackerCode_; + } + + [[nodiscard]] inline std::shared_ptr getJSQueue() const { + return jsQueue_; + } + + [[nodiscard]] inline std::shared_ptr getJSScheduler() const { + return jsScheduler_; + } + + [[nodiscard]] inline std::shared_ptr getUIScheduler() const { + return uiScheduler_; + } + + private: + const std::string valueUnpackerCode_; + const std::shared_ptr jsQueue_; + const std::shared_ptr jsScheduler_; + const std::shared_ptr uiScheduler_; +#ifndef NDEBUG + SingleInstanceChecker singleInstanceChecker_; +#endif // NDEBUG +}; + +} // namespace worklets diff --git a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/NativeWorkletsModuleSpec.cpp b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.cpp similarity index 71% rename from packages/react-native-reanimated/Common/cpp/worklets/NativeModules/NativeWorkletsModuleSpec.cpp rename to packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.cpp index 95f05ac8036..624c42ee224 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/NativeWorkletsModuleSpec.cpp +++ b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.cpp @@ -1,9 +1,9 @@ -#include +#include #include #define WORKLETS_SPEC_PREFIX(FN_NAME) \ - __hostFunction_NativeWorkletsModuleSpec_##FN_NAME + __hostFunction_WorkletsModuleProxySpec_##FN_NAME namespace worklets { @@ -12,12 +12,12 @@ static jsi::Value WORKLETS_SPEC_PREFIX(makeShareableClone)( TurboModule &turboModule, const jsi::Value *args, size_t) { - return static_cast(&turboModule) + return static_cast(&turboModule) ->makeShareableClone( rt, std::move(args[0]), std::move(args[1]), std::move(args[2])); } -NativeWorkletsModuleSpec::NativeWorkletsModuleSpec( +WorkletsModuleProxySpec::WorkletsModuleProxySpec( const std::shared_ptr jsInvoker) : TurboModule("NativeWorklets", jsInvoker) { methodMap_["makeShareableClone"] = diff --git a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/NativeWorkletsModuleSpec.h b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.h similarity index 82% rename from packages/react-native-reanimated/Common/cpp/worklets/NativeModules/NativeWorkletsModuleSpec.h rename to packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.h index 1c1d622e17c..c79a86b908f 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/NativeWorkletsModuleSpec.h +++ b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.h @@ -9,9 +9,9 @@ using namespace react; namespace worklets { -class JSI_EXPORT NativeWorkletsModuleSpec : public TurboModule { +class JSI_EXPORT WorkletsModuleProxySpec : public TurboModule { protected: - explicit NativeWorkletsModuleSpec( + explicit WorkletsModuleProxySpec( const std::shared_ptr jsInvoker); public: diff --git a/packages/react-native-reanimated/Common/cpp/worklets/SharedItems/Shareables.cpp b/packages/react-native-reanimated/Common/cpp/worklets/SharedItems/Shareables.cpp index 525cd114ebb..f8c5bfc1366 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/SharedItems/Shareables.cpp +++ b/packages/react-native-reanimated/Common/cpp/worklets/SharedItems/Shareables.cpp @@ -47,7 +47,12 @@ jsi::Value makeShareableClone( if (value.isObject()) { auto object = value.asObject(rt); if (!object.getProperty(rt, "__workletHash").isUndefined()) { - shareable = std::make_shared(rt, object); + if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) { + shareable = + std::make_shared>(rt, object); + } else { + shareable = std::make_shared(rt, object); + } } else if (!object.getProperty(rt, "__init").isUndefined()) { shareable = std::make_shared(rt, object); } else if (object.isFunction(rt)) { @@ -284,7 +289,12 @@ jsi::Value ShareableHandle::toJSValue(jsi::Runtime &rt) { remoteRuntime_ = &rt; } } - return jsi::Value(rt, *remoteValue_); + if (&rt == remoteRuntime_) { + return jsi::Value(rt, *remoteValue_); + } + auto initObj = initializer_->toJSValue(rt); + return getValueUnpacker(rt).call( + rt, initObj, jsi::String::createFromAscii(rt, "Handle")); } jsi::Value ShareableString::toJSValue(jsi::Runtime &rt) { diff --git a/packages/react-native-reanimated/Common/cpp/worklets/Tools/JSScheduler.cpp b/packages/react-native-reanimated/Common/cpp/worklets/Tools/JSScheduler.cpp index 0699e0cebaa..b3f227ed409 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/Tools/JSScheduler.cpp +++ b/packages/react-native-reanimated/Common/cpp/worklets/Tools/JSScheduler.cpp @@ -17,24 +17,4 @@ JSScheduler::JSScheduler( rnRuntime_(rnRuntime), jsCallInvoker_(jsCallInvoker) {} -#ifdef RCT_NEW_ARCH_ENABLED -// With `runtimeExecutor`. -JSScheduler::JSScheduler( - jsi::Runtime &rnRuntime, - RuntimeExecutor runtimeExecutor) - : scheduleOnJS([&](Job job) { - runtimeExecutor_( - [job = std::move(job)](jsi::Runtime &runtime) { job(runtime); }); - }), - rnRuntime_(rnRuntime), - runtimeExecutor_(runtimeExecutor) {} -#endif // RCT_NEW_ARCH_ENABLED - -const std::shared_ptr JSScheduler::getJSCallInvoker() const { - assert( - jsCallInvoker_ != nullptr && - "[Reanimated] Expected jsCallInvoker, got nullptr instead."); - return jsCallInvoker_; -} - } // namespace worklets diff --git a/packages/react-native-reanimated/Common/cpp/worklets/Tools/JSScheduler.h b/packages/react-native-reanimated/Common/cpp/worklets/Tools/JSScheduler.h index 84eaa512d81..c85ab8a9673 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/Tools/JSScheduler.h +++ b/packages/react-native-reanimated/Common/cpp/worklets/Tools/JSScheduler.h @@ -15,26 +15,14 @@ class JSScheduler { using Job = std::function; public: - // With `jsCallInvoker`. explicit JSScheduler( jsi::Runtime &rnRuntime, const std::shared_ptr &jsCallInvoker); -#ifdef RCT_NEW_ARCH_ENABLED - // With `runtimeExecutor`. - explicit JSScheduler( - jsi::Runtime &rnRuntime, - RuntimeExecutor runtimeExecutor); -#endif // RCT_NEW_ARCH_ENABLED - const std::function scheduleOnJS = nullptr; - const std::shared_ptr getJSCallInvoker() const; protected: jsi::Runtime &rnRuntime_; -#ifdef RCT_NEW_ARCH_ENABLED - RuntimeExecutor runtimeExecutor_ = nullptr; -#endif // RCT_NEW_ARCH_ENABLED const std::shared_ptr jsCallInvoker_ = nullptr; }; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/Tools/PlatformLogger.h b/packages/react-native-reanimated/Common/cpp/worklets/Tools/PlatformLogger.h similarity index 83% rename from packages/react-native-reanimated/Common/cpp/reanimated/Tools/PlatformLogger.h rename to packages/react-native-reanimated/Common/cpp/worklets/Tools/PlatformLogger.h index 37db18e3326..de58cb93098 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/Tools/PlatformLogger.h +++ b/packages/react-native-reanimated/Common/cpp/worklets/Tools/PlatformLogger.h @@ -2,7 +2,7 @@ #include -namespace reanimated { +namespace worklets { class PlatformLogger { public: @@ -13,4 +13,4 @@ class PlatformLogger { static void log(const bool b); }; -} // namespace reanimated +} // namespace worklets diff --git a/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/RNRuntimeWorkletDecorator.cpp b/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/RNRuntimeWorkletDecorator.cpp index 37565314534..8575570f72b 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/RNRuntimeWorkletDecorator.cpp +++ b/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/RNRuntimeWorkletDecorator.cpp @@ -4,11 +4,11 @@ namespace worklets { void RNRuntimeWorkletDecorator::decorate( jsi::Runtime &rnRuntime, - const std::shared_ptr &nativeWorkletsModule) { + const std::shared_ptr &workletsModuleProxy) { rnRuntime.global().setProperty( rnRuntime, "__workletsModuleProxy", - jsi::Object::createFromHostObject(rnRuntime, nativeWorkletsModule)); + jsi::Object::createFromHostObject(rnRuntime, workletsModuleProxy)); } } // namespace worklets diff --git a/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/RNRuntimeWorkletDecorator.h b/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/RNRuntimeWorkletDecorator.h index 9884e71091a..41cf12b5598 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/RNRuntimeWorkletDecorator.h +++ b/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/RNRuntimeWorkletDecorator.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include using namespace facebook; @@ -13,7 +13,7 @@ class RNRuntimeWorkletDecorator { public: static void decorate( jsi::Runtime &rnRuntime, - const std::shared_ptr &nativeWorkletsModule); + const std::shared_ptr &workletsModuleProxy); }; } // namespace worklets diff --git a/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.cpp b/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.cpp index 07caee39767..4d677927155 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.cpp +++ b/packages/react-native-reanimated/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -76,6 +77,11 @@ void WorkletRuntimeDecorator::decorate( evalWithSourceUrl)); #endif // NDEBUG + jsi_utils::installJsiFunction( + rt, "_log", [](jsi::Runtime &rt, const jsi::Value &value) { + PlatformLogger::log(stringifyJSIValue(rt, value)); + }); + jsi_utils::installJsiFunction( rt, "_toString", [](jsi::Runtime &rt, const jsi::Value &value) { return jsi::String::createFromUtf8(rt, stringifyJSIValue(rt, value)); diff --git a/packages/react-native-reanimated/android/build.gradle b/packages/react-native-reanimated/android/build.gradle index a35ae1fcdf7..c98d195f462 100644 --- a/packages/react-native-reanimated/android/build.gradle +++ b/packages/react-native-reanimated/android/build.gradle @@ -362,22 +362,6 @@ android { } } - if (IS_NEW_ARCHITECTURE_ENABLED) { - // RuntimeExecutor and CallInvokerHolder - if (REACT_NATIVE_MINOR_VERSION <= 74) { - srcDirs += "src/reactNativeVersionPatch/NativeProxyFabric/74" - } else { - srcDirs += "src/reactNativeVersionPatch/NativeProxyFabric/latest" - } - } else { - // CallInvokerHolder - if (REACT_NATIVE_MINOR_VERSION <= 74) { - srcDirs += "src/reactNativeVersionPatch/NativeProxyPaper/74" - } else { - srcDirs += "src/reactNativeVersionPatch/NativeProxyPaper/latest" - } - } - // BorderRadiiDrawableUtils if (REACT_NATIVE_MINOR_VERSION <= 74) { srcDirs += "src/reactNativeVersionPatch/BorderRadiiDrawableUtils/74" diff --git a/packages/react-native-reanimated/android/src/reactNativeVersionPatch/NativeProxyFabric/74/com/swmansion/reanimated/NativeProxy.java b/packages/react-native-reanimated/android/src/fabric/java/com/swmansion/reanimated/NativeProxy.java similarity index 65% rename from packages/react-native-reanimated/android/src/reactNativeVersionPatch/NativeProxyFabric/74/com/swmansion/reanimated/NativeProxy.java rename to packages/react-native-reanimated/android/src/fabric/java/com/swmansion/reanimated/NativeProxy.java index 1f732ab91f1..1701b938ca4 100644 --- a/packages/react-native-reanimated/android/src/reactNativeVersionPatch/NativeProxyFabric/74/com/swmansion/reanimated/NativeProxy.java +++ b/packages/react-native-reanimated/android/src/fabric/java/com/swmansion/reanimated/NativeProxy.java @@ -4,8 +4,6 @@ import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.RuntimeExecutor; -import com.facebook.react.bridge.queue.MessageQueueThread; import com.facebook.react.common.annotations.FrameworkAPI; import com.facebook.react.fabric.FabricUIManager; import com.facebook.react.turbomodule.core.CallInvokerHolderImpl; @@ -14,10 +12,14 @@ import com.swmansion.reanimated.layoutReanimation.LayoutAnimations; import com.swmansion.reanimated.layoutReanimation.NativeMethodsHolder; import com.swmansion.reanimated.nativeProxy.NativeProxyCommon; +import com.swmansion.worklets.JSCallInvokerResolver; import com.swmansion.worklets.WorkletsModule; import java.util.HashMap; import java.util.Objects; +/** + * @noinspection JavaJniMissingFunction + */ public class NativeProxy extends NativeProxyCommon { @DoNotStrip @SuppressWarnings("unused") @@ -33,32 +35,16 @@ public class NativeProxy extends NativeProxyCommon { LayoutAnimations LayoutAnimations = new LayoutAnimations(context); - ReanimatedMessageQueueThread messageQueueThread = new ReanimatedMessageQueueThread(); - - if (context.isBridgeless()) { - RuntimeExecutor runtimeExecutor = context.getRuntimeExecutor(); - mHybridData = - initHybridBridgeless( - workletsModule, - Objects.requireNonNull(context.getJavaScriptContextHolder()).get(), - runtimeExecutor, - mAndroidUIScheduler, - LayoutAnimations, - messageQueueThread, - fabricUIManager); - } else { - CallInvokerHolderImpl callInvokerHolder = - (CallInvokerHolderImpl) context.getCatalystInstance().getJSCallInvokerHolder(); - mHybridData = - initHybrid( - workletsModule, - Objects.requireNonNull(context.getJavaScriptContextHolder()).get(), - callInvokerHolder, - mAndroidUIScheduler, - LayoutAnimations, - messageQueueThread, - fabricUIManager); - } + CallInvokerHolderImpl callInvokerHolder = JSCallInvokerResolver.getJSCallInvokerHolder(context); + mHybridData = + initHybrid( + workletsModule, + Objects.requireNonNull(context.getJavaScriptContextHolder()).get(), + callInvokerHolder, + LayoutAnimations, + context.isBridgeless(), + fabricUIManager); + prepareLayoutAnimations(LayoutAnimations); installJSIBindings(); if (BuildConfig.DEBUG) { @@ -66,22 +52,13 @@ public class NativeProxy extends NativeProxyCommon { } } + @OptIn(markerClass = FrameworkAPI.class) private native HybridData initHybrid( WorkletsModule workletsModule, long jsContext, CallInvokerHolderImpl jsCallInvokerHolder, - AndroidUIScheduler androidUIScheduler, - LayoutAnimations LayoutAnimations, - MessageQueueThread messageQueueThread, - FabricUIManager fabricUIManager); - - private native HybridData initHybridBridgeless( - WorkletsModule workletsModule, - long jsContext, - RuntimeExecutor runtimeExecutor, - AndroidUIScheduler androidUIScheduler, LayoutAnimations LayoutAnimations, - MessageQueueThread messageQueueThread, + boolean isBridgeless, FabricUIManager fabricUIManager); public native boolean isAnyHandlerWaitingForEvent(String eventName, int emitterReactTag); @@ -93,7 +70,8 @@ protected HybridData getHybridData() { return mHybridData; } - public static NativeMethodsHolder createNativeMethodsHolder(LayoutAnimations layoutAnimations) { + public static NativeMethodsHolder createNativeMethodsHolder( + LayoutAnimations ignoredLayoutAnimations) { return new NativeMethodsHolder() { @Override public void startAnimation(int tag, int type, HashMap values) { 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 42b1f21ce14..8b9c4753320 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 @@ -1,7 +1,6 @@ #include #include #include -#include #include #include @@ -9,12 +8,12 @@ #include #include #include +#include #include #include #include #include -#include #include #include @@ -29,12 +28,11 @@ using namespace react; NativeProxy::NativeProxy( jni::alias_ref jThis, - const std::shared_ptr &nativeWorkletsModule, + const std::shared_ptr &workletsModuleProxy, jsi::Runtime *rnRuntime, const std::shared_ptr &jsCallInvoker, - const std::shared_ptr &uiScheduler, jni::global_ref layoutAnimations, - jni::alias_ref messageQueueThread + const bool isBridgeless #ifdef RCT_NEW_ARCH_ENABLED , jni::alias_ref @@ -43,14 +41,12 @@ NativeProxy::NativeProxy( ) : javaPart_(jni::make_global(jThis)), rnRuntime_(rnRuntime), - nativeReanimatedModule_(std::make_shared( - nativeWorkletsModule, + reanimatedModuleProxy_(std::make_shared( + workletsModuleProxy, *rnRuntime, - std::make_shared(*rnRuntime, jsCallInvoker), - std::make_shared(messageQueueThread), - uiScheduler, + jsCallInvoker, getPlatformDependentMethods(), - /* isBridgeless */ false, + isBridgeless, getIsReducedMotion())), layoutAnimations_(std::move(layoutAnimations)) { #ifdef RCT_NEW_ARCH_ENABLED @@ -59,42 +55,17 @@ NativeProxy::NativeProxy( } #ifdef RCT_NEW_ARCH_ENABLED -NativeProxy::NativeProxy( - jni::alias_ref jThis, - const std::shared_ptr &nativeWorkletsModule, - jsi::Runtime *rnRuntime, - RuntimeExecutor runtimeExecutor, - const std::shared_ptr &uiScheduler, - jni::global_ref layoutAnimations, - jni::alias_ref messageQueueThread, - jni::alias_ref - fabricUIManager) - : javaPart_(jni::make_global(jThis)), - rnRuntime_(rnRuntime), - nativeReanimatedModule_(std::make_shared( - nativeWorkletsModule, - *rnRuntime, - std::make_shared(*rnRuntime, runtimeExecutor), - std::make_shared(messageQueueThread), - uiScheduler, - getPlatformDependentMethods(), - /* isBridgeless */ true, - getIsReducedMotion())), - layoutAnimations_(std::move(layoutAnimations)) { - commonInit(fabricUIManager); -} - void NativeProxy::commonInit( jni::alias_ref &fabricUIManager) { const auto &uiManager = fabricUIManager->getBinding()->getScheduler()->getUIManager(); - nativeReanimatedModule_->initializeFabric(uiManager); + reanimatedModuleProxy_->initializeFabric(uiManager); // removed temporarily, event listener mechanism needs to be fixed on RN side // eventListener_ = std::make_shared( - // [nativeReanimatedModule, + // [reanimatedModuleProxy, // getAnimationTimestamp](const RawEvent &rawEvent) { - // return nativeReanimatedModule->handleRawEvent( + // return reanimatedModuleProxy->handleRawEvent( // rawEvent, getAnimationTimestamp()); // }); // reactScheduler_ = binding->getScheduler(); @@ -109,7 +80,7 @@ NativeProxy::~NativeProxy() { // cleanup all animated sensors here, since NativeProxy // has already been destroyed when AnimatedSensorModule's // destructor is ran - nativeReanimatedModule_->cleanupSensors(); + reanimatedModuleProxy_->cleanupSensors(); } jni::local_ref NativeProxy::initHybrid( @@ -118,9 +89,8 @@ jni::local_ref NativeProxy::initHybrid( jlong jsContext, jni::alias_ref jsCallInvokerHolder, - jni::alias_ref androidUiScheduler, jni::alias_ref layoutAnimations, - jni::alias_ref messageQueueThread + bool isBridgeless #ifdef RCT_NEW_ARCH_ENABLED , jni::alias_ref @@ -128,17 +98,14 @@ jni::local_ref NativeProxy::initHybrid( #endif ) { auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker(); - auto uiScheduler = androidUiScheduler->cthis()->getUIScheduler(); - auto nativeWorkletsModule = - jWorkletsModule->cthis()->getNativeWorkletsModule(); + auto workletsModuleProxy = jWorkletsModule->cthis()->getWorkletsModuleProxy(); return makeCxxInstance( jThis, - nativeWorkletsModule, + workletsModuleProxy, (jsi::Runtime *)jsContext, jsCallInvoker, - uiScheduler, make_global(layoutAnimations), - messageQueueThread + isBridgeless #ifdef RCT_NEW_ARCH_ENABLED , fabricUIManager @@ -146,33 +113,6 @@ jni::local_ref NativeProxy::initHybrid( ); } -#ifdef RCT_NEW_ARCH_ENABLED -jni::local_ref NativeProxy::initHybridBridgeless( - jni::alias_ref jThis, - jni::alias_ref jWorkletsModule, - jlong jsContext, - jni::alias_ref runtimeExecutorHolder, - jni::alias_ref androidUiScheduler, - jni::alias_ref layoutAnimations, - jni::alias_ref messageQueueThread, - jni::alias_ref - fabricUIManager) { - auto uiScheduler = androidUiScheduler->cthis()->getUIScheduler(); - auto runtimeExecutor = runtimeExecutorHolder->cthis()->get(); - auto nativeWorkletsModule = - jWorkletsModule->cthis()->getNativeWorkletsModule(); - return makeCxxInstance( - jThis, - nativeWorkletsModule, - (jsi::Runtime *)jsContext, - runtimeExecutor, - uiScheduler, - make_global(layoutAnimations), - messageQueueThread, - fabricUIManager); -} -#endif // RCT_NEW_ARCH_ENABLED - #ifndef NDEBUG void NativeProxy::checkJavaVersion(jsi::Runtime &rnRuntime) { std::string javaVersion; @@ -215,7 +155,7 @@ void NativeProxy::injectCppVersion() { void NativeProxy::installJSIBindings() { jsi::Runtime &rnRuntime = *rnRuntime_; WorkletRuntimeCollector::install(rnRuntime); - RNRuntimeDecorator::decorate(rnRuntime, nativeReanimatedModule_); + RNRuntimeDecorator::decorate(rnRuntime, reanimatedModuleProxy_); #ifndef NDEBUG checkJavaVersion(rnRuntime); injectCppVersion(); @@ -228,13 +168,13 @@ void NativeProxy::installJSIBindings() { bool NativeProxy::isAnyHandlerWaitingForEvent( const std::string &eventName, const int emitterReactTag) { - return nativeReanimatedModule_->isAnyHandlerWaitingForEvent( + return reanimatedModuleProxy_->isAnyHandlerWaitingForEvent( eventName, emitterReactTag); } void NativeProxy::performOperations() { #ifdef RCT_NEW_ARCH_ENABLED - nativeReanimatedModule_->performOperations(); + reanimatedModuleProxy_->performOperations(); #endif } @@ -246,10 +186,6 @@ bool NativeProxy::getIsReducedMotion() { void NativeProxy::registerNatives() { registerHybrid( {makeNativeMethod("initHybrid", NativeProxy::initHybrid), -#ifdef RCT_NEW_ARCH_ENABLED - makeNativeMethod( - "initHybridBridgeless", NativeProxy::initHybridBridgeless), -#endif // RCT_NEW_ARCH_ENABLED makeNativeMethod("installJSIBindings", NativeProxy::installJSIBindings), makeNativeMethod( "isAnyHandlerWaitingForEvent", @@ -471,7 +407,7 @@ void NativeProxy::handleEvent( return; } - jsi::Runtime &rt = nativeReanimatedModule_->getUIRuntime(); + jsi::Runtime &rt = reanimatedModuleProxy_->getUIRuntime(); jsi::Value payload; try { payload = jsi::Value::createFromJsonUtf8( @@ -481,7 +417,7 @@ void NativeProxy::handleEvent( return; } - nativeReanimatedModule_->handleEvent( + reanimatedModuleProxy_->handleEvent( eventName->toString(), emitterReactTag, payload, getAnimationTimestamp()); } @@ -567,14 +503,14 @@ PlatformDepMethodsHolder NativeProxy::getPlatformDependentMethods() { } void NativeProxy::setupLayoutAnimations() { - auto weakNativeReanimatedModule = - std::weak_ptr(nativeReanimatedModule_); + auto weakReanimatedModuleProxy = + std::weak_ptr(reanimatedModuleProxy_); layoutAnimations_->cthis()->setAnimationStartingBlock( - [weakNativeReanimatedModule]( + [weakReanimatedModuleProxy]( int tag, int type, alias_ref> values) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - jsi::Runtime &rt = nativeReanimatedModule->getUIRuntime(); + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + jsi::Runtime &rt = reanimatedModuleProxy->getUIRuntime(); jsi::Object yogaValues(rt); for (const auto &entry : *values) { try { @@ -595,25 +531,24 @@ void NativeProxy::setupLayoutAnimations() { "[Reanimated] Failed to convert value to number."); } } - nativeReanimatedModule->layoutAnimationsManager() - .startLayoutAnimation( - rt, tag, static_cast(type), yogaValues); + reanimatedModuleProxy->layoutAnimationsManager().startLayoutAnimation( + rt, tag, static_cast(type), yogaValues); } }); layoutAnimations_->cthis()->setHasAnimationBlock( - [weakNativeReanimatedModule](int tag, int type) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - return nativeReanimatedModule->layoutAnimationsManager() + [weakReanimatedModuleProxy](int tag, int type) { + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + return reanimatedModuleProxy->layoutAnimationsManager() .hasLayoutAnimation(tag, static_cast(type)); } return false; }); layoutAnimations_->cthis()->setShouldAnimateExitingBlock( - [weakNativeReanimatedModule](int tag, bool shouldAnimate) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - return nativeReanimatedModule->layoutAnimationsManager() + [weakReanimatedModuleProxy](int tag, bool shouldAnimate) { + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + return reanimatedModuleProxy->layoutAnimationsManager() .shouldAnimateExiting(tag, shouldAnimate); } return false; @@ -621,35 +556,35 @@ void NativeProxy::setupLayoutAnimations() { #ifndef NDEBUG layoutAnimations_->cthis()->setCheckDuplicateSharedTag( - [weakNativeReanimatedModule](int viewTag, int screenTag) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - nativeReanimatedModule->layoutAnimationsManager() + [weakReanimatedModuleProxy](int viewTag, int screenTag) { + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + reanimatedModuleProxy->layoutAnimationsManager() .checkDuplicateSharedTag(viewTag, screenTag); } }); #endif layoutAnimations_->cthis()->setClearAnimationConfigBlock( - [weakNativeReanimatedModule](int tag) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - nativeReanimatedModule->layoutAnimationsManager() + [weakReanimatedModuleProxy](int tag) { + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + reanimatedModuleProxy->layoutAnimationsManager() .clearLayoutAnimationConfig(tag); } }); layoutAnimations_->cthis()->setCancelAnimationForTag( - [weakNativeReanimatedModule](int tag) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - jsi::Runtime &rt = nativeReanimatedModule->getUIRuntime(); - nativeReanimatedModule->layoutAnimationsManager() + [weakReanimatedModuleProxy](int tag) { + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + jsi::Runtime &rt = reanimatedModuleProxy->getUIRuntime(); + reanimatedModuleProxy->layoutAnimationsManager() .cancelLayoutAnimation(rt, tag); } }); layoutAnimations_->cthis()->setFindPrecedingViewTagForTransition( - [weakNativeReanimatedModule](int tag) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - return nativeReanimatedModule->layoutAnimationsManager() + [weakReanimatedModuleProxy](int tag) { + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + return reanimatedModuleProxy->layoutAnimationsManager() .findPrecedingViewTagForTransition(tag); } else { return -1; @@ -657,9 +592,9 @@ void NativeProxy::setupLayoutAnimations() { }); layoutAnimations_->cthis()->setGetSharedGroupBlock( - [weakNativeReanimatedModule](int tag) -> std::vector { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - return nativeReanimatedModule->layoutAnimationsManager() + [weakReanimatedModuleProxy](int tag) -> std::vector { + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + return reanimatedModuleProxy->layoutAnimationsManager() .getSharedGroup(tag); } else { return {}; 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 1f2ac757987..318e3acf7a7 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,18 +1,15 @@ #pragma once -#include -#include +#include #include #include -#include #include #include #include #include #include -#include #include #include @@ -154,9 +151,8 @@ class NativeProxy : public jni::HybridClass { jlong jsContext, jni::alias_ref jsCallInvokerHolder, - jni::alias_ref androidUiScheduler, jni::alias_ref layoutAnimations, - jni::alias_ref messageQueueThread + const bool isBridgeless #ifdef RCT_NEW_ARCH_ENABLED , jni::alias_ref @@ -164,18 +160,6 @@ class NativeProxy : public jni::HybridClass { #endif ); -#ifdef RCT_NEW_ARCH_ENABLED - static jni::local_ref initHybridBridgeless( - jni::alias_ref jThis, - jni::alias_ref jWorkletsModule, - jlong jsContext, - jni::alias_ref runtimeExecutorHolder, - jni::alias_ref androidUiScheduler, - jni::alias_ref layoutAnimations, - jni::alias_ref messageQueueThread, - jni::alias_ref - fabricUIManager); -#endif // RCT_NEW_ARCH_ENABLED static void registerNatives(); ~NativeProxy(); @@ -184,7 +168,7 @@ class NativeProxy : public jni::HybridClass { friend HybridBase; jni::global_ref javaPart_; jsi::Runtime *rnRuntime_; - std::shared_ptr nativeReanimatedModule_; + std::shared_ptr reanimatedModuleProxy_; jni::global_ref layoutAnimations_; #ifndef NDEBUG void checkJavaVersion(jsi::Runtime &); @@ -258,7 +242,7 @@ class NativeProxy : public jni::HybridClass { /*** * Wraps a method of `NativeProxy` in a function object capturing `this` * @tparam TReturn return type of passed method - * @tparam TParams paramater types of passed method + * @tparam TParams parameter types of passed method * @param methodPtr pointer to method to be wrapped * @return a function object with the same signature as the method, calling * that method on `this` @@ -278,12 +262,11 @@ class NativeProxy : public jni::HybridClass { explicit NativeProxy( jni::alias_ref jThis, - const std::shared_ptr &nativeWorkletsModule, + const std::shared_ptr &workletsModuleProxy, jsi::Runtime *rnRuntime, const std::shared_ptr &jsCallInvoker, - const std::shared_ptr &uiScheduler, jni::global_ref layoutAnimations, - jni::alias_ref messageQueueThread + const bool isBridgeless #ifdef RCT_NEW_ARCH_ENABLED , jni::alias_ref @@ -292,17 +275,6 @@ class NativeProxy : public jni::HybridClass { ); #ifdef RCT_NEW_ARCH_ENABLED - explicit NativeProxy( - jni::alias_ref jThis, - const std::shared_ptr &nativeWorkletsModule, - jsi::Runtime *rnRuntime, - RuntimeExecutor runtimeExecutor, - const std::shared_ptr &uiScheduler, - jni::global_ref layoutAnimations, - jni::alias_ref messageQueueThread, - jni::alias_ref - fabricUIManager); - void commonInit(jni::alias_ref &fabricUIManager); #endif // RCT_NEW_ARCH_ENABLED diff --git a/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/OnLoad.cpp b/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/OnLoad.cpp index c1d1b783582..590d598f73b 100644 --- a/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/OnLoad.cpp +++ b/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/OnLoad.cpp @@ -1,6 +1,5 @@ #include -#include #include #include @@ -9,7 +8,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { reanimated::NativeProxy::registerNatives(); reanimated::AnimationFrameCallback::registerNatives(); reanimated::EventHandler::registerNatives(); - reanimated::AndroidUIScheduler::registerNatives(); reanimated::LayoutAnimations::registerNatives(); reanimated::SensorSetter::registerNatives(); reanimated::KeyboardWorkletWrapper::registerNatives(); diff --git a/packages/react-native-reanimated/android/src/main/cpp/worklets/CMakeLists.txt b/packages/react-native-reanimated/android/src/main/cpp/worklets/CMakeLists.txt index 26edc5003d3..9959ddefc6e 100644 --- a/packages/react-native-reanimated/android/src/main/cpp/worklets/CMakeLists.txt +++ b/packages/react-native-reanimated/android/src/main/cpp/worklets/CMakeLists.txt @@ -124,7 +124,7 @@ elseif(${JS_RUNTIME} STREQUAL "v8") PRIVATE "${JS_RUNTIME_DIR}/src" ) - file(GLOB V8_SO_DIR "${JS_RUNTIME_DIR}/android/build/intermediates/library_jni/*/jni/${ANDROID_ABI}") + file(GLOB V8_SO_DIR "${JS_RUNTIME_DIR}/android/build/intermediates/library_jni/**/jni/${ANDROID_ABI}") find_library( V8EXECUTOR_LIB v8executor diff --git a/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/AndroidUIScheduler.cpp b/packages/react-native-reanimated/android/src/main/cpp/worklets/android/AndroidUIScheduler.cpp similarity index 93% rename from packages/react-native-reanimated/android/src/main/cpp/reanimated/android/AndroidUIScheduler.cpp rename to packages/react-native-reanimated/android/src/main/cpp/worklets/android/AndroidUIScheduler.cpp index 039028d8f9a..595c09f7e70 100644 --- a/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/AndroidUIScheduler.cpp +++ b/packages/react-native-reanimated/android/src/main/cpp/worklets/android/AndroidUIScheduler.cpp @@ -1,10 +1,10 @@ -#include +#include #include #include #include -namespace reanimated { +namespace worklets { using namespace facebook; using namespace react; @@ -57,4 +57,4 @@ void AndroidUIScheduler::registerNatives() { }); } -} // namespace reanimated +} // namespace worklets diff --git a/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/AndroidUIScheduler.h b/packages/react-native-reanimated/android/src/main/cpp/worklets/android/AndroidUIScheduler.h similarity index 89% rename from packages/react-native-reanimated/android/src/main/cpp/reanimated/android/AndroidUIScheduler.h rename to packages/react-native-reanimated/android/src/main/cpp/worklets/android/AndroidUIScheduler.h index e9e7c940dc8..c38b0ac5a96 100644 --- a/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/AndroidUIScheduler.h +++ b/packages/react-native-reanimated/android/src/main/cpp/worklets/android/AndroidUIScheduler.h @@ -10,7 +10,7 @@ #include -namespace reanimated { +namespace worklets { using namespace facebook; using namespace worklets; @@ -18,7 +18,7 @@ using namespace worklets; class AndroidUIScheduler : public jni::HybridClass { public: static auto constexpr kJavaDescriptor = - "Lcom/swmansion/reanimated/AndroidUIScheduler;"; + "Lcom/swmansion/worklets/AndroidUIScheduler;"; static jni::local_ref initHybrid( jni::alias_ref jThis); static void registerNatives(); @@ -41,4 +41,4 @@ class AndroidUIScheduler : public jni::HybridClass { jni::alias_ref jThis); }; -} // namespace reanimated +} // namespace worklets diff --git a/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/PlatformLogger.cpp b/packages/react-native-reanimated/android/src/main/cpp/worklets/android/PlatformLogger.cpp similarity index 78% rename from packages/react-native-reanimated/android/src/main/cpp/reanimated/android/PlatformLogger.cpp rename to packages/react-native-reanimated/android/src/main/cpp/worklets/android/PlatformLogger.cpp index 03495d35a03..e931aa904a4 100644 --- a/packages/react-native-reanimated/android/src/main/cpp/reanimated/android/PlatformLogger.cpp +++ b/packages/react-native-reanimated/android/src/main/cpp/worklets/android/PlatformLogger.cpp @@ -1,10 +1,10 @@ #include -#include +#include -constexpr const auto tag = "Reanimated"; +constexpr const auto tag = "Worklets"; -namespace reanimated { +namespace worklets { void PlatformLogger::log(const char *str) { __android_log_print(ANDROID_LOG_VERBOSE, tag, "%s", str); @@ -26,4 +26,4 @@ void PlatformLogger::log(const bool b) { log(b ? "true" : "false"); } -} // namespace reanimated +} // namespace worklets diff --git a/packages/react-native-reanimated/android/src/main/cpp/worklets/android/WorkletsModule.cpp b/packages/react-native-reanimated/android/src/main/cpp/worklets/android/WorkletsModule.cpp index 6be7b73349d..ac8b1c34519 100644 --- a/packages/react-native-reanimated/android/src/main/cpp/worklets/android/WorkletsModule.cpp +++ b/packages/react-native-reanimated/android/src/main/cpp/worklets/android/WorkletsModule.cpp @@ -20,19 +20,44 @@ using namespace react; WorkletsModule::WorkletsModule( jni::alias_ref jThis, jsi::Runtime *rnRuntime, - const std::string &valueUnpackerCode) + const std::string &valueUnpackerCode, + jni::alias_ref messageQueueThread, + const std::shared_ptr &jsCallInvoker, + const std::shared_ptr &jsScheduler, + const std::shared_ptr &uiScheduler) : javaPart_(jni::make_global(jThis)), rnRuntime_(rnRuntime), - nativeWorkletsModule_( - std::make_shared(valueUnpackerCode)) { - RNRuntimeWorkletDecorator::decorate(*rnRuntime_, nativeWorkletsModule_); + workletsModuleProxy_(std::make_shared( + valueUnpackerCode, + std::make_shared(messageQueueThread), + jsCallInvoker, + jsScheduler, + uiScheduler)) { + RNRuntimeWorkletDecorator::decorate(*rnRuntime_, workletsModuleProxy_); } jni::local_ref WorkletsModule::initHybrid( jni::alias_ref jThis, jlong jsContext, - const std::string &valueUnpackerCode) { - return makeCxxInstance(jThis, (jsi::Runtime *)jsContext, valueUnpackerCode); + const std::string &valueUnpackerCode, + jni::alias_ref messageQueueThread, + jni::alias_ref + jsCallInvokerHolder, + jni::alias_ref + androidUIScheduler) { + auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker(); + auto rnRuntime = reinterpret_cast(jsContext); + auto jsScheduler = + std::make_shared(*rnRuntime, jsCallInvoker); + auto uiScheduler = androidUIScheduler->cthis()->getUIScheduler(); + return makeCxxInstance( + jThis, + rnRuntime, + valueUnpackerCode, + messageQueueThread, + jsCallInvoker, + jsScheduler, + uiScheduler); } void WorkletsModule::registerNatives() { diff --git a/packages/react-native-reanimated/android/src/main/cpp/worklets/android/WorkletsModule.h b/packages/react-native-reanimated/android/src/main/cpp/worklets/android/WorkletsModule.h index 349853b1edb..094287271fc 100644 --- a/packages/react-native-reanimated/android/src/main/cpp/worklets/android/WorkletsModule.h +++ b/packages/react-native-reanimated/android/src/main/cpp/worklets/android/WorkletsModule.h @@ -13,7 +13,8 @@ #include #include -#include +#include +#include #include #include @@ -31,24 +32,33 @@ class WorkletsModule : public jni::HybridClass { static jni::local_ref initHybrid( jni::alias_ref jThis, jlong jsContext, - const std::string &valueUnpackerCode); + const std::string &valueUnpackerCode, + jni::alias_ref messageQueueThread, + jni::alias_ref + jsCallInvokerHolder, + jni::alias_ref + androidUIScheduler); static void registerNatives(); - inline std::shared_ptr getNativeWorkletsModule() { - return nativeWorkletsModule_; + inline std::shared_ptr getWorkletsModuleProxy() { + return workletsModuleProxy_; } private: friend HybridBase; jni::global_ref javaPart_; jsi::Runtime *rnRuntime_; - std::shared_ptr nativeWorkletsModule_; + std::shared_ptr workletsModuleProxy_; explicit WorkletsModule( jni::alias_ref jThis, jsi::Runtime *rnRuntime, - const std::string &valueUnpackerCode); + const std::string &valueUnpackerCode, + jni::alias_ref messageQueueThread, + const std::shared_ptr &jsCallInvoker, + const std::shared_ptr &jsScheduler, + const std::shared_ptr &uiScheduler); }; } // namespace worklets diff --git a/packages/react-native-reanimated/android/src/main/cpp/worklets/android/WorkletsOnLoad.cpp b/packages/react-native-reanimated/android/src/main/cpp/worklets/android/WorkletsOnLoad.cpp index 3c35b26e36a..9d441273714 100644 --- a/packages/react-native-reanimated/android/src/main/cpp/worklets/android/WorkletsOnLoad.cpp +++ b/packages/react-native-reanimated/android/src/main/cpp/worklets/android/WorkletsOnLoad.cpp @@ -1,8 +1,11 @@ #include +#include #include JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { - return facebook::jni::initialize( - vm, [] { worklets::WorkletsModule::registerNatives(); }); + return facebook::jni::initialize(vm, [] { + worklets::WorkletsModule::registerNatives(); + worklets::AndroidUIScheduler::registerNatives(); + }); } diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/NodesManager.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/NodesManager.java index 9ca28144b94..58cdc88924d 100644 --- a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/NodesManager.java +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/NodesManager.java @@ -128,14 +128,13 @@ public void invalidate() { } if (mNativeProxy != null) { - mNativeProxy.invalidate(); mNativeProxy = null; } } public void initWithContext(ReactApplicationContext reactApplicationContext) { mNativeProxy = new NativeProxy(reactApplicationContext, mWorkletsModule); - mAnimationManager.setAndroidUIScheduler(getNativeProxy().getAndroidUIScheduler()); + mAnimationManager.setAndroidUIScheduler(mWorkletsModule.getAndroidUIScheduler()); compatibility = new ReaCompatibility(reactApplicationContext); compatibility.registerFabricEventListener(this); } diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/layoutReanimation/AnimationsManager.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/layoutReanimation/AnimationsManager.java index 15afbede935..07e4ebaeedb 100644 --- a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/layoutReanimation/AnimationsManager.java +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/layoutReanimation/AnimationsManager.java @@ -22,8 +22,8 @@ import com.facebook.react.uimanager.ReactStylesDiffMap; import com.facebook.react.uimanager.RootView; import com.facebook.react.uimanager.ViewManager; -import com.swmansion.reanimated.AndroidUIScheduler; import com.swmansion.reanimated.Utils; +import com.swmansion.worklets.AndroidUIScheduler; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; 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 741e096f6e0..3b291a92d0c 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 @@ -13,7 +13,6 @@ import com.facebook.react.bridge.ReadableNativeArray; import com.facebook.soloader.SoLoader; import com.swmansion.common.GestureHandlerStateManager; -import com.swmansion.reanimated.AndroidUIScheduler; import com.swmansion.reanimated.BuildConfig; import com.swmansion.reanimated.DevMenuUtils; import com.swmansion.reanimated.NativeProxy; @@ -34,6 +33,9 @@ import java.util.Objects; import java.util.Set; +/** + * @noinspection JavaJniMissingFunction + */ public abstract class NativeProxyCommon { static { SoLoader.loadLibrary("reanimated"); @@ -42,7 +44,6 @@ public abstract class NativeProxyCommon { protected final WorkletsModule mWorkletsModule; protected NodesManager mNodesManager; protected final WeakReference mContext; - protected final AndroidUIScheduler mAndroidUIScheduler; private final ReanimatedSensorContainer reanimatedSensorContainer; private final GestureHandlerStateManager gestureHandlerStateManager; private final KeyboardAnimationManager keyboardAnimationManager; @@ -54,7 +55,6 @@ public abstract class NativeProxyCommon { protected NativeProxyCommon(ReactApplicationContext context) { mWorkletsModule = Objects.requireNonNull(context.getNativeModule(ReanimatedModule.class)).getWorkletsModule(); - mAndroidUIScheduler = new AndroidUIScheduler(context); mContext = new WeakReference<>(context); reanimatedSensorContainer = new ReanimatedSensorContainer(mContext); keyboardAnimationManager = new KeyboardAnimationManager(mContext); @@ -75,10 +75,6 @@ protected NativeProxyCommon(ReactApplicationContext context) { protected native void installJSIBindings(); - public AndroidUIScheduler getAndroidUIScheduler() { - return mAndroidUIScheduler; - } - private void toggleSlowAnimations() { slowAnimationsEnabled = !slowAnimationsEnabled; if (slowAnimationsEnabled) { @@ -224,10 +220,6 @@ public void unsubscribeFromKeyboardEvents(int listenerId) { protected abstract HybridData getHybridData(); - public void invalidate() { - mAndroidUIScheduler.deactivate(); - } - public void prepareLayoutAnimations(LayoutAnimations layoutAnimations) { if (Utils.isChromeDebugger) { Log.w("[REANIMATED]", "You can not use LayoutAnimation with enabled Chrome Debugger"); diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/AndroidUIScheduler.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/AndroidUIScheduler.java similarity index 97% rename from packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/AndroidUIScheduler.java rename to packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/AndroidUIScheduler.java index 126c270a4cd..7a8c4f63201 100644 --- a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/AndroidUIScheduler.java +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/AndroidUIScheduler.java @@ -1,4 +1,4 @@ -package com.swmansion.reanimated; +package com.swmansion.worklets; import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/JSCallInvokerResolver.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/JSCallInvokerResolver.java new file mode 100644 index 00000000000..b8f77c550e0 --- /dev/null +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/JSCallInvokerResolver.java @@ -0,0 +1,27 @@ +package com.swmansion.worklets; + +import androidx.annotation.OptIn; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.common.annotations.FrameworkAPI; +import com.facebook.react.turbomodule.core.CallInvokerHolderImpl; + +public class JSCallInvokerResolver { + + @OptIn(markerClass = FrameworkAPI.class) + public static CallInvokerHolderImpl getJSCallInvokerHolder(ReactApplicationContext context) { + try { + var method = context.getClass().getMethod("getJSCallInvokerHolder"); + return (CallInvokerHolderImpl) method.invoke(context); + } catch (Exception ignored) { + // In newer implementations, the method is in CatalystInstance, continue. + } + try { + var catalystInstance = context.getClass().getMethod("getCatalystInstance").invoke(context); + assert catalystInstance != null; + var method = catalystInstance.getClass().getMethod("getJSCallInvokerHolder"); + return (CallInvokerHolderImpl) method.invoke(catalystInstance); + } catch (Exception e) { + throw new RuntimeException("Failed to get JSCallInvokerHolder", e); + } + } +} diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/ReanimatedMessageQueueThread.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/WorkletsMessageQueueThread.java similarity index 69% rename from packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/ReanimatedMessageQueueThread.java rename to packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/WorkletsMessageQueueThread.java index e6a4cd1c37b..b1ccdf8b59d 100644 --- a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/ReanimatedMessageQueueThread.java +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/WorkletsMessageQueueThread.java @@ -1,9 +1,9 @@ -package com.swmansion.reanimated; +package com.swmansion.worklets; import com.facebook.proguard.annotations.DoNotStrip; @DoNotStrip -public class ReanimatedMessageQueueThread extends ReanimatedMessageQueueThreadBase { +public class WorkletsMessageQueueThread extends WorkletsMessageQueueThreadBase { @Override public boolean runOnQueue(Runnable runnable) { return messageQueueThread.runOnQueue(runnable); diff --git a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/ReanimatedMessageQueueThreadBase.java b/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/WorkletsMessageQueueThreadBase.java similarity index 93% rename from packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/ReanimatedMessageQueueThreadBase.java rename to packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/WorkletsMessageQueueThreadBase.java index ca7733d0cbc..ade92c5b8b2 100644 --- a/packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/ReanimatedMessageQueueThreadBase.java +++ b/packages/react-native-reanimated/android/src/main/java/com/swmansion/worklets/WorkletsMessageQueueThreadBase.java @@ -1,4 +1,4 @@ -package com.swmansion.reanimated; +package com.swmansion.worklets; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.queue.MessageQueueThread; @@ -14,10 +14,10 @@ // The only method that has changed is `quitSynchronous()` (see comment above // function implementation for details). @DoNotStrip -public abstract class ReanimatedMessageQueueThreadBase implements MessageQueueThread { +public abstract class WorkletsMessageQueueThreadBase implements MessageQueueThread { protected final MessageQueueThreadImpl messageQueueThread; - public ReanimatedMessageQueueThreadBase() { + public WorkletsMessageQueueThreadBase() { messageQueueThread = MessageQueueThreadImpl.create( MessageQueueThreadSpec.mainThreadSpec(), 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 2da67792f66..35cf7d07829 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 @@ -5,12 +5,17 @@ import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.queue.MessageQueueThread; import com.facebook.react.common.annotations.FrameworkAPI; import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.turbomodule.core.CallInvokerHolderImpl; import com.facebook.soloader.SoLoader; import com.swmansion.reanimated.NativeWorkletsModuleSpec; import java.util.Objects; +/** + * @noinspection JavaJniMissingFunction + */ @ReactModule(name = WorkletsModule.NAME) public class WorkletsModule extends NativeWorkletsModuleSpec { static { @@ -21,21 +26,29 @@ public class WorkletsModule extends NativeWorkletsModuleSpec { @SuppressWarnings("unused") private HybridData mHybridData; - /** - * @noinspection unused - */ + @SuppressWarnings("unused") protected HybridData getHybridData() { return mHybridData; } - /** - * @noinspection JavaJniMissingFunction - */ + private final WorkletsMessageQueueThread mMessageQueueThread = new WorkletsMessageQueueThread(); + private final AndroidUIScheduler mAndroidUIScheduler; + + public AndroidUIScheduler getAndroidUIScheduler() { + return mAndroidUIScheduler; + } + @OptIn(markerClass = FrameworkAPI.class) - private native HybridData initHybrid(long jsContext, String valueUnpackerCode); + private native HybridData initHybrid( + long jsContext, + String valueUnpackerCode, + MessageQueueThread messageQueueThread, + CallInvokerHolderImpl jsCallInvokerHolder, + AndroidUIScheduler androidUIScheduler); public WorkletsModule(ReactApplicationContext reactContext) { super(reactContext); + mAndroidUIScheduler = new AndroidUIScheduler(reactContext); } @OptIn(markerClass = FrameworkAPI.class) @@ -43,9 +56,19 @@ public WorkletsModule(ReactApplicationContext reactContext) { public boolean installTurboModule(String valueUnpackerCode) { var context = getReactApplicationContext(); var jsContext = Objects.requireNonNull(context.getJavaScriptContextHolder()).get(); + var jsCallInvokerHolder = JSCallInvokerResolver.getJSCallInvokerHolder(context); - mHybridData = initHybrid(jsContext, valueUnpackerCode); - + mHybridData = + initHybrid( + jsContext, + valueUnpackerCode, + mMessageQueueThread, + jsCallInvokerHolder, + mAndroidUIScheduler); return true; } + + public void invalidate() { + mAndroidUIScheduler.deactivate(); + } } diff --git a/packages/react-native-reanimated/android/src/reactNativeVersionPatch/NativeProxyPaper/latest/com/swmansion/reanimated/NativeProxy.java b/packages/react-native-reanimated/android/src/paper/java/com/swmansion/reanimated/NativeProxy.java similarity index 92% rename from packages/react-native-reanimated/android/src/reactNativeVersionPatch/NativeProxyPaper/latest/com/swmansion/reanimated/NativeProxy.java rename to packages/react-native-reanimated/android/src/paper/java/com/swmansion/reanimated/NativeProxy.java index f4bfa2c555c..7f7ebada7cd 100644 --- a/packages/react-native-reanimated/android/src/reactNativeVersionPatch/NativeProxyPaper/latest/com/swmansion/reanimated/NativeProxy.java +++ b/packages/react-native-reanimated/android/src/paper/java/com/swmansion/reanimated/NativeProxy.java @@ -6,17 +6,20 @@ import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.queue.MessageQueueThread; import com.facebook.react.common.annotations.FrameworkAPI; import com.facebook.react.turbomodule.core.CallInvokerHolderImpl; import com.swmansion.reanimated.layoutReanimation.LayoutAnimations; import com.swmansion.reanimated.layoutReanimation.NativeMethodsHolder; import com.swmansion.reanimated.nativeProxy.NativeProxyCommon; +import com.swmansion.worklets.JSCallInvokerResolver; import com.swmansion.worklets.WorkletsModule; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Objects; +/** + * @noinspection JavaJniMissingFunction + */ public class NativeProxy extends NativeProxyCommon { @DoNotStrip @SuppressWarnings("unused") @@ -25,17 +28,15 @@ public class NativeProxy extends NativeProxyCommon { @OptIn(markerClass = FrameworkAPI.class) public NativeProxy(ReactApplicationContext context, WorkletsModule workletsModule) { super(context); - CallInvokerHolderImpl holder = (CallInvokerHolderImpl) context.getJSCallInvokerHolder(); + CallInvokerHolderImpl holder = JSCallInvokerResolver.getJSCallInvokerHolder(context); LayoutAnimations LayoutAnimations = new LayoutAnimations(context); - ReanimatedMessageQueueThread messageQueueThread = new ReanimatedMessageQueueThread(); mHybridData = initHybrid( workletsModule, Objects.requireNonNull(context.getJavaScriptContextHolder()).get(), holder, - mAndroidUIScheduler, LayoutAnimations, - messageQueueThread); + /* isBridgeless */ false); prepareLayoutAnimations(LayoutAnimations); installJSIBindings(); if (BuildConfig.DEBUG) { @@ -48,9 +49,8 @@ private native HybridData initHybrid( WorkletsModule workletsModule, long jsContext, CallInvokerHolderImpl jsCallInvokerHolder, - AndroidUIScheduler androidUIScheduler, LayoutAnimations LayoutAnimations, - MessageQueueThread messageQueueThread); + boolean isBridgeless); public native boolean isAnyHandlerWaitingForEvent(String eventName, int emitterReactTag); diff --git a/packages/react-native-reanimated/android/src/reactNativeVersionPatch/NativeProxyFabric/latest/com/swmansion/reanimated/NativeProxy.java b/packages/react-native-reanimated/android/src/reactNativeVersionPatch/NativeProxyFabric/latest/com/swmansion/reanimated/NativeProxy.java deleted file mode 100644 index ddfb00aceb5..00000000000 --- a/packages/react-native-reanimated/android/src/reactNativeVersionPatch/NativeProxyFabric/latest/com/swmansion/reanimated/NativeProxy.java +++ /dev/null @@ -1,149 +0,0 @@ -package com.swmansion.reanimated; - -import androidx.annotation.OptIn; -import com.facebook.jni.HybridData; -import com.facebook.proguard.annotations.DoNotStrip; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.RuntimeExecutor; -import com.facebook.react.bridge.queue.MessageQueueThread; -import com.facebook.react.common.annotations.FrameworkAPI; -import com.facebook.react.fabric.FabricUIManager; -import com.facebook.react.turbomodule.core.CallInvokerHolderImpl; -import com.facebook.react.uimanager.UIManagerHelper; -import com.facebook.react.uimanager.common.UIManagerType; -import com.swmansion.reanimated.layoutReanimation.LayoutAnimations; -import com.swmansion.reanimated.layoutReanimation.NativeMethodsHolder; -import com.swmansion.reanimated.nativeProxy.NativeProxyCommon; -import com.swmansion.worklets.WorkletsModule; -import java.util.HashMap; -import java.util.Objects; - -public class NativeProxy extends NativeProxyCommon { - @DoNotStrip - @SuppressWarnings("unused") - private final HybridData mHybridData; - - public @OptIn(markerClass = FrameworkAPI.class) NativeProxy( - ReactApplicationContext context, WorkletsModule workletsModule) { - super(context); - ReactFeatureFlagsWrapper.enableMountHooks(); - - FabricUIManager fabricUIManager = - (FabricUIManager) UIManagerHelper.getUIManager(context, UIManagerType.FABRIC); - - LayoutAnimations LayoutAnimations = new LayoutAnimations(context); - - ReanimatedMessageQueueThread messageQueueThread = new ReanimatedMessageQueueThread(); - - if (context.isBridgeless()) { - RuntimeExecutor runtimeExecutor = context.getCatalystInstance().getRuntimeExecutor(); - mHybridData = - initHybridBridgeless( - workletsModule, - Objects.requireNonNull(context.getJavaScriptContextHolder()).get(), - runtimeExecutor, - mAndroidUIScheduler, - LayoutAnimations, - messageQueueThread, - fabricUIManager); - } else { - CallInvokerHolderImpl callInvokerHolder = - (CallInvokerHolderImpl) context.getJSCallInvokerHolder(); - mHybridData = - initHybrid( - workletsModule, - Objects.requireNonNull(context.getJavaScriptContextHolder()).get(), - callInvokerHolder, - mAndroidUIScheduler, - LayoutAnimations, - messageQueueThread, - fabricUIManager); - } - prepareLayoutAnimations(LayoutAnimations); - installJSIBindings(); - if (BuildConfig.DEBUG) { - checkCppVersion(); - } - } - - private native HybridData initHybrid( - WorkletsModule workletsModule, - long jsContext, - CallInvokerHolderImpl jsCallInvokerHolder, - AndroidUIScheduler androidUIScheduler, - LayoutAnimations LayoutAnimations, - MessageQueueThread messageQueueThread, - FabricUIManager fabricUIManager); - - private native HybridData initHybridBridgeless( - WorkletsModule workletsModule, - long jsContext, - RuntimeExecutor runtimeExecutor, - AndroidUIScheduler androidUIScheduler, - LayoutAnimations LayoutAnimations, - MessageQueueThread messageQueueThread, - FabricUIManager fabricUIManager); - - public native boolean isAnyHandlerWaitingForEvent(String eventName, int emitterReactTag); - - public native void performOperations(); - - @Override - protected HybridData getHybridData() { - return mHybridData; - } - - public static NativeMethodsHolder createNativeMethodsHolder(LayoutAnimations layoutAnimations) { - return new NativeMethodsHolder() { - @Override - public void startAnimation(int tag, int type, HashMap values) { - // NOT IMPLEMENTED - } - - @Override - public boolean isLayoutAnimationEnabled() { - // NOT IMPLEMENTED - return false; - } - - @Override - public int findPrecedingViewTagForTransition(int tag) { - // NOT IMPLEMENTED - return -1; - } - - @Override - public boolean shouldAnimateExiting(int tag, boolean shouldAnimate) { - // NOT IMPLEMENTED - return false; - } - - @Override - public boolean hasAnimation(int tag, int type) { - // NOT IMPLEMENTED - return false; - } - - @Override - public void clearAnimationConfig(int tag) { - // NOT IMPLEMENTED - } - - @Override - public void cancelAnimation(int tag) { - // NOT IMPLEMENTED - } - - @Override - public void checkDuplicateSharedTag(int viewTag, int screenTag) { - // NOT IMPLEMENTED - } - - @Override - public int[] getSharedGroup(int viewTag) { - // NOT IMPLEMENTED - return new int[] {}; - } - }; - } -} diff --git a/packages/react-native-reanimated/android/src/reactNativeVersionPatch/NativeProxyPaper/74/com/swmansion/reanimated/NativeProxy.java b/packages/react-native-reanimated/android/src/reactNativeVersionPatch/NativeProxyPaper/74/com/swmansion/reanimated/NativeProxy.java deleted file mode 100644 index 9078f5486c6..00000000000 --- a/packages/react-native-reanimated/android/src/reactNativeVersionPatch/NativeProxyPaper/74/com/swmansion/reanimated/NativeProxy.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.swmansion.reanimated; - -import static com.swmansion.reanimated.Utils.simplifyStringNumbersList; - -import androidx.annotation.OptIn; -import com.facebook.jni.HybridData; -import com.facebook.proguard.annotations.DoNotStrip; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.queue.MessageQueueThread; -import com.facebook.react.common.annotations.FrameworkAPI; -import com.facebook.react.turbomodule.core.CallInvokerHolderImpl; -import com.swmansion.reanimated.layoutReanimation.LayoutAnimations; -import com.swmansion.reanimated.layoutReanimation.NativeMethodsHolder; -import com.swmansion.reanimated.nativeProxy.NativeProxyCommon; -import com.swmansion.worklets.WorkletsModule; -import java.lang.ref.WeakReference; -import java.util.HashMap; -import java.util.Objects; - -public class NativeProxy extends NativeProxyCommon { - @DoNotStrip - @SuppressWarnings("unused") - private final HybridData mHybridData; - - @OptIn(markerClass = FrameworkAPI.class) - public NativeProxy(ReactApplicationContext context, WorkletsModule workletsModule) { - super(context); - CallInvokerHolderImpl holder = - (CallInvokerHolderImpl) context.getCatalystInstance().getJSCallInvokerHolder(); - LayoutAnimations LayoutAnimations = new LayoutAnimations(context); - ReanimatedMessageQueueThread messageQueueThread = new ReanimatedMessageQueueThread(); - mHybridData = - initHybrid( - workletsModule, - Objects.requireNonNull(context.getJavaScriptContextHolder()).get(), - holder, - mAndroidUIScheduler, - LayoutAnimations, - messageQueueThread); - prepareLayoutAnimations(LayoutAnimations); - installJSIBindings(); - if (BuildConfig.DEBUG) { - checkCppVersion(); - } - } - - @OptIn(markerClass = FrameworkAPI.class) - private native HybridData initHybrid( - WorkletsModule workletsModule, - long jsContext, - CallInvokerHolderImpl jsCallInvokerHolder, - AndroidUIScheduler androidUIScheduler, - LayoutAnimations LayoutAnimations, - MessageQueueThread messageQueueThread); - - public native boolean isAnyHandlerWaitingForEvent(String eventName, int emitterReactTag); - - public native void performOperations(); - - @Override - protected HybridData getHybridData() { - return mHybridData; - } - - public static NativeMethodsHolder createNativeMethodsHolder(LayoutAnimations layoutAnimations) { - WeakReference weakLayoutAnimations = new WeakReference<>(layoutAnimations); - return new NativeMethodsHolder() { - @Override - public void startAnimation(int tag, int type, HashMap values) { - LayoutAnimations layoutAnimations = weakLayoutAnimations.get(); - if (layoutAnimations != null) { - HashMap preparedValues = new HashMap<>(); - for (String key : values.keySet()) { - String stringValue = values.get(key).toString(); - if (key.endsWith("TransformMatrix")) { - preparedValues.put(key, simplifyStringNumbersList(stringValue)); - } else { - preparedValues.put(key, stringValue); - } - } - layoutAnimations.startAnimationForTag(tag, type, preparedValues); - } - } - - @Override - public boolean shouldAnimateExiting(int tag, boolean shouldAnimate) { - LayoutAnimations layoutAnimations = weakLayoutAnimations.get(); - if (layoutAnimations != null) { - return layoutAnimations.shouldAnimateExiting(tag, shouldAnimate); - } - return false; - } - - @Override - public boolean isLayoutAnimationEnabled() { - LayoutAnimations layoutAnimations = weakLayoutAnimations.get(); - if (layoutAnimations != null) { - return layoutAnimations.isLayoutAnimationEnabled(); - } - return false; - } - - @Override - public boolean hasAnimation(int tag, int type) { - LayoutAnimations layoutAnimations = weakLayoutAnimations.get(); - if (layoutAnimations != null) { - return layoutAnimations.hasAnimationForTag(tag, type); - } - return false; - } - - @Override - public void clearAnimationConfig(int tag) { - LayoutAnimations layoutAnimations = weakLayoutAnimations.get(); - if (layoutAnimations != null) { - layoutAnimations.clearAnimationConfigForTag(tag); - } - } - - @Override - public void cancelAnimation(int tag) { - LayoutAnimations layoutAnimations = weakLayoutAnimations.get(); - if (layoutAnimations != null) { - layoutAnimations.cancelAnimationForTag(tag); - } - } - - @Override - public int findPrecedingViewTagForTransition(int tag) { - LayoutAnimations layoutAnimations = weakLayoutAnimations.get(); - if (layoutAnimations != null) { - return layoutAnimations.findPrecedingViewTagForTransition(tag); - } - return -1; - } - - public void checkDuplicateSharedTag(int viewTag, int screenTag) { - LayoutAnimations layoutAnimations = weakLayoutAnimations.get(); - if (layoutAnimations != null) { - layoutAnimations.checkDuplicateSharedTag(viewTag, screenTag); - } - } - - public int[] getSharedGroup(int viewTag) { - LayoutAnimations layoutAnimations = weakLayoutAnimations.get(); - if (layoutAnimations != null) { - return layoutAnimations.getSharedGroup(viewTag); - } - return new int[] {}; - } - }; - } -} diff --git a/packages/react-native-reanimated/apple/reanimated/apple/Fabric/REAInitializerRCTFabricSurface.h b/packages/react-native-reanimated/apple/reanimated/apple/Fabric/REAInitializerRCTFabricSurface.h index bd8143b8a55..c27c2106790 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/Fabric/REAInitializerRCTFabricSurface.h +++ b/packages/react-native-reanimated/apple/reanimated/apple/Fabric/REAInitializerRCTFabricSurface.h @@ -5,7 +5,7 @@ @interface REAInitializerRCTFabricSurface : RCTFabricSurface -@property REAModule *reaModule; +@property __weak REAModule *reaModule; @end diff --git a/packages/react-native-reanimated/apple/reanimated/apple/LayoutReanimation/REASharedTransitionManager.m b/packages/react-native-reanimated/apple/reanimated/apple/LayoutReanimation/REASharedTransitionManager.m index 1bf674b1d6b..7e35f1bbdbd 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/LayoutReanimation/REASharedTransitionManager.m +++ b/packages/react-native-reanimated/apple/reanimated/apple/LayoutReanimation/REASharedTransitionManager.m @@ -576,10 +576,26 @@ - (void)handleTabNavigatorChange:(REAUIView *)layoutedScreen return; } + // Add safety check for disappearing screens + if ([_disappearingScreens count] == 0) { + return; + } + REAUIView *navTabScreen = _disappearingScreens[0]; REAUIView *sourceScreen = _disappearingScreens[[_disappearingScreens count] - 1]; + + // Add null checks + if (!navTabScreen || !navTabScreen.reactSuperview) { + return; + } + REAUIView *targetTabScreen = [REAScreensHelper getActiveTabForTabNavigator:navTabScreen.reactSuperview]; REAUIView *targetScreen = [REAScreensHelper findTopScreenInChildren:targetTabScreen]; + + if (!targetScreen) { + return; + } + if (!layoutedScreen && _isTabNavigator) { // just wait for the next layout computation for your screen return; diff --git a/packages/react-native-reanimated/apple/reanimated/apple/REAModule.h b/packages/react-native-reanimated/apple/reanimated/apple/REAModule.h index 6824efa838e..634be0f8932 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/REAModule.h +++ b/packages/react-native-reanimated/apple/reanimated/apple/REAModule.h @@ -1,7 +1,7 @@ #ifdef RCT_NEW_ARCH_ENABLED -#import -#import -#import +#if REACT_NATIVE_MINOR_VERSION >= 75 +#import +#endif // REACT_NATIVE_MINOR_VERSION >= 75 #import #else // RCT_NEW_ARCH_ENABLED #import @@ -18,8 +18,9 @@ @interface REAModule : RCTEventEmitter #ifdef RCT_NEW_ARCH_ENABLED = 75 + RCTCallInvokerModule, +#endif // REACT_NATIVE_MINOR_VERSION >= 75 #else #import #import +#if REACT_NATIVE_MINOR_VERSION >= 75 +#import +#endif // REACT_NATIVE_MINOR_VERSION >= 75 #endif // RCT_NEW_ARCH_ENABLED #ifdef RCT_NEW_ARCH_ENABLED @@ -36,10 +39,14 @@ @interface RCTBridge (JSIRuntime) - (void *)runtime; @end +#if defined(RCT_NEW_ARCH_ENABLED) && REACT_NATIVE_MINOR_VERSION >= 75 +// nothing +#else // defined(RCT_NEW_ARCH_ENABLED) && REACT_NATIVE_MINOR_VERSION >= 75 @interface RCTBridge (RCTTurboModule) - (std::shared_ptr)jsCallInvoker; - (void)_tryAndHandleError:(dispatch_block_t)block; @end +#endif // RCT_NEW_ARCH_ENABLED #ifdef RCT_NEW_ARCH_ENABLED static __strong REAInitializerRCTFabricSurface *reaSurface; @@ -50,7 +57,7 @@ - (void)_tryAndHandleError:(dispatch_block_t)block; @implementation REAModule { #ifdef RCT_NEW_ARCH_ENABLED __weak RCTSurfacePresenter *_surfacePresenter; - std::weak_ptr weakNativeReanimatedModule_; + std::weak_ptr weakReanimatedModuleProxy_; #else NSMutableArray *_operations; #endif // RCT_NEW_ARCH_ENABLED @@ -58,13 +65,12 @@ @implementation REAModule { SingleInstanceChecker singleInstanceChecker_; #endif // NDEBUG bool hasListeners; - bool _isBridgeless; } @synthesize moduleRegistry = _moduleRegistry; -#ifdef RCT_NEW_ARCH_ENABLED -@synthesize runtimeExecutor = _runtimeExecutor; -#endif // RCT_NEW_ARCH_ENABLED +#if defined(RCT_NEW_ARCH_ENABLED) && REACT_NATIVE_MINOR_VERSION >= 75 +@synthesize callInvoker = _callInvoker; +#endif // defined(RCT_NEW_ARCH_ENABLED) && REACT_NATIVE_MINOR_VERSION >= 75 RCT_EXPORT_MODULE(ReanimatedModule); @@ -104,8 +110,8 @@ - (void)injectDependencies:(jsi::Runtime &)runtime { const auto &uiManager = [self getUIManager]; react_native_assert(uiManager.get() != nil); - if (auto nativeReanimatedModule = weakNativeReanimatedModule_.lock()) { - nativeReanimatedModule->initializeFabric(uiManager); + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy_.lock()) { + reanimatedModuleProxy->initializeFabric(uiManager); } } @@ -142,16 +148,16 @@ - (void)attachReactEventListener if (strongSelf == nil) { return; } - if (auto nativeReanimatedModule = strongSelf->weakNativeReanimatedModule_.lock()) { + if (auto reanimatedModuleProxy = strongSelf->weakReanimatedModuleProxy_.lock()) { auto eventListener = - std::make_shared([nativeReanimatedModule](const RawEvent &rawEvent) { + std::make_shared([reanimatedModuleProxy](const RawEvent &rawEvent) { if (!RCTIsMainQueue()) { // event listener called on the JS thread, let's ignore this event // as we cannot safely access worklet runtime here // and also we don't care about topLayout events return false; } - return nativeReanimatedModule->handleRawEvent(rawEvent, CACurrentMediaTime() * 1000); + return reanimatedModuleProxy->handleRawEvent(rawEvent, CACurrentMediaTime() * 1000); }); [scheduler addEventListener:eventListener]; } @@ -167,7 +173,6 @@ - (void)attachReactEventListener - (void)setSurfacePresenter:(id)surfacePresenter { _surfacePresenter = surfacePresenter; - _isBridgeless = true; } - (void)setBridge:(RCTBridge *)bridge @@ -193,9 +198,10 @@ - (void)setBridge:(RCTBridge *)bridge - (void)setReaSurfacePresenter { - if (reaSurface == nil) { - // we need only one instance because SurfacePresenter is the same during the application lifetime - reaSurface = [[REAInitializerRCTFabricSurface alloc] init]; + if (_surfacePresenter && ![_surfacePresenter surfaceForRootTag:reaSurface.rootTag]) { + if (!reaSurface) { + reaSurface = [[REAInitializerRCTFabricSurface alloc] init]; + } [_surfacePresenter registerSurface:reaSurface]; } reaSurface.reaModule = self; @@ -279,38 +285,23 @@ - (void)sendEventWithName:(NSString *)eventName body:(id)body RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(installTurboModule) { WorkletsModule *workletsModule = [_moduleRegistry moduleForName:"WorkletsModule"]; - if (_isBridgeless) { -#ifdef RCT_NEW_ARCH_ENABLED - RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge; - auto &rnRuntime = *(jsi::Runtime *)cxxBridge.runtime; - auto executorFunction = ([executor = _runtimeExecutor](std::function &&callback) { - // Convert to Objective-C block so it can be captured properly. - __block auto callbackBlock = callback; - - [executor execute:^(jsi::Runtime &runtime) { - callbackBlock(runtime); - }]; - }); - auto nativeReanimatedModule = reanimated::createReanimatedModuleBridgeless( - self, _moduleRegistry, rnRuntime, workletsModule, executorFunction); - [self attachReactEventListener]; - [self commonInit:nativeReanimatedModule withRnRuntime:rnRuntime]; -#else - [NSException raise:@"Missing bridge" format:@"[Reanimated] Failed to obtain the bridge."]; -#endif // RCT_NEW_ARCH_ENABLED - } else { - facebook::jsi::Runtime *jsiRuntime = [self.bridge respondsToSelector:@selector(runtime)] - ? reinterpret_cast(self.bridge.runtime) - : nullptr; - if (jsiRuntime) { - auto nativeReanimatedModule = - reanimated::createReanimatedModule(self, self.bridge, self.bridge.jsCallInvoker, workletsModule); - jsi::Runtime &rnRuntime = *jsiRuntime; +#if defined(RCT_NEW_ARCH_ENABLED) && REACT_NATIVE_MINOR_VERSION >= 75 + auto jsCallInvoker = _callInvoker.callInvoker; +#else // defined(RCT_NEW_ARCH_ENABLED) && REACT_NATIVE_MINOR_VERSION >= 75 + auto jsCallInvoker = self.bridge.jsCallInvoker; +#endif // defined(RCT_NEW_ARCH_ENABLED) && REACT_NATIVE_MINOR_VERSION >= 75 + auto jsiRuntime = reinterpret_cast(self.bridge.runtime); + auto isBridgeless = ![self.bridge isKindOfClass:[RCTCxxBridge class]]; + + assert(jsiRuntime != nullptr); + + auto reanimatedModuleProxy = + reanimated::createReanimatedModule(self, self.bridge, jsCallInvoker, workletsModule, isBridgeless); + + jsi::Runtime &rnRuntime = *jsiRuntime; + [self commonInit:reanimatedModuleProxy withRnRuntime:rnRuntime]; - [self commonInit:nativeReanimatedModule withRnRuntime:rnRuntime]; - } - } return @YES; } @@ -322,13 +313,13 @@ - (void)sendEventWithName:(NSString *)eventName body:(id)body } #endif // RCT_NEW_ARCH_ENABLED -- (void)commonInit:(std::shared_ptr)nativeReanimatedModule - withRnRuntime:(jsi::Runtime &)rnRuntime +- (void)commonInit:(std::shared_ptr)reanimatedModuleProxy withRnRuntime:(jsi::Runtime &)rnRuntime { WorkletRuntimeCollector::install(rnRuntime); - RNRuntimeDecorator::decorate(rnRuntime, nativeReanimatedModule); + RNRuntimeDecorator::decorate(rnRuntime, reanimatedModuleProxy); #ifdef RCT_NEW_ARCH_ENABLED - weakNativeReanimatedModule_ = nativeReanimatedModule; + [self attachReactEventListener]; + weakReanimatedModuleProxy_ = reanimatedModuleProxy; if (self->_surfacePresenter != nil) { // reload, uiManager is null right now, we need to wait for `installReanimatedAfterReload` [self injectDependencies:rnRuntime]; diff --git a/packages/react-native-reanimated/apple/reanimated/apple/REANodesManager.mm b/packages/react-native-reanimated/apple/reanimated/apple/REANodesManager.mm index ec86d086b92..eb0a70c50ac 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/REANodesManager.mm +++ b/packages/react-native-reanimated/apple/reanimated/apple/REANodesManager.mm @@ -313,7 +313,7 @@ - (void)onAnimationFrame:(READisplayLink *)displayLink - (void)performOperations { #ifdef RCT_NEW_ARCH_ENABLED - _performOperations(); // calls NativeReanimatedModule::performOperations + _performOperations(); // calls ReanimatedModuleProxy::performOperations _wantRunUpdates = NO; #else if (_operationsInBatch.count != 0) { diff --git a/packages/react-native-reanimated/apple/reanimated/apple/native/NativeProxy.h b/packages/react-native-reanimated/apple/reanimated/apple/native/NativeProxy.h index 7e68aa16cd6..41fad911f95 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/native/NativeProxy.h +++ b/packages/react-native-reanimated/apple/reanimated/apple/native/NativeProxy.h @@ -1,7 +1,7 @@ #if __cplusplus #import -#import +#import #import #import #import @@ -14,31 +14,22 @@ namespace reanimated { static inline bool getIsReducedMotion(); -std::shared_ptr createReanimatedModule( +std::shared_ptr createReanimatedModule( REAModule *reaModule, RCTBridge *bridge, const std::shared_ptr &jsInvoker, - WorkletsModule *workletsModule); - -#ifdef RCT_NEW_ARCH_ENABLED -std::shared_ptr -createReanimatedModuleBridgeless( - REAModule *reaModule, - RCTModuleRegistry *moduleRegistry, - jsi::Runtime &runtime, WorkletsModule *workletsModule, - RuntimeExecutor runtimeExecutor); -#endif // RCT_NEW_ARCH_ENABLED + bool isBridgeless); void commonInit( REAModule *reaModule, - std::shared_ptr nativeReanimatedModule); + std::shared_ptr reanimatedModuleProxy); #ifdef RCT_NEW_ARCH_ENABLED // nothing #else // RCT_NEW_ARCH_ENABLED void setupLayoutAnimationCallbacks( - std::shared_ptr nativeReanimatedModule, + std::shared_ptr reanimatedModuleProxy, REAAnimationsManager *animationsManager); #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 b960c6c642b..3e66116c319 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/native/NativeProxy.mm +++ b/packages/react-native-reanimated/apple/reanimated/apple/native/NativeProxy.mm @@ -1,5 +1,5 @@ #import -#import +#import #import #import #import @@ -12,9 +12,8 @@ #import #import #import -#import #import -#import + #import #ifndef NDEBUG @@ -22,6 +21,7 @@ #endif #import +#import #ifdef RCT_NEW_ARCH_ENABLED #import @@ -55,107 +55,55 @@ static inline bool getIsReducedMotion() #endif // __has_include() } -std::shared_ptr createReanimatedModule( +std::shared_ptr createReanimatedModule( REAModule *reaModule, RCTBridge *bridge, const std::shared_ptr &jsInvoker, - WorkletsModule *workletsModule) + WorkletsModule *workletsModule, + bool isBridgeless) { auto nodesManager = reaModule.nodesManager; jsi::Runtime &rnRuntime = *reinterpret_cast(reaModule.bridge.runtime); - auto jsQueue = std::make_shared([NSRunLoop currentRunLoop], ^(NSError *error) { - throw error; - }); - PlatformDepMethodsHolder platformDepMethodsHolder = makePlatformDepMethodsHolder(bridge, nodesManager, reaModule); - std::shared_ptr uiScheduler = std::make_shared(); - std::shared_ptr jsScheduler = std::make_shared(rnRuntime, jsInvoker); - constexpr auto isBridgeless = false; + const auto workletsModuleProxy = [workletsModule getWorkletsModuleProxy]; - const auto nativeWorkletsModule = [workletsModule getNativeWorkletsModule]; + auto reanimatedModuleProxy = std::make_shared( + workletsModuleProxy, rnRuntime, jsInvoker, platformDepMethodsHolder, isBridgeless, getIsReducedMotion()); - auto nativeReanimatedModule = std::make_shared( - nativeWorkletsModule, - rnRuntime, - jsScheduler, - jsQueue, - uiScheduler, - platformDepMethodsHolder, - isBridgeless, - getIsReducedMotion()); - - commonInit(reaModule, nativeReanimatedModule); + commonInit(reaModule, reanimatedModuleProxy); // Layout Animation callbacks setup #ifdef RCT_NEW_ARCH_ENABLED // nothing #else REAAnimationsManager *animationsManager = reaModule.animationsManager; - setupLayoutAnimationCallbacks(nativeReanimatedModule, animationsManager); + setupLayoutAnimationCallbacks(reanimatedModuleProxy, animationsManager); #endif // RCT_NEW_ARCH_ENABLED - return nativeReanimatedModule; -} - -#ifdef RCT_NEW_ARCH_ENABLED -std::shared_ptr createReanimatedModuleBridgeless( - REAModule *reaModule, - RCTModuleRegistry *moduleRegistry, - jsi::Runtime &runtime, - WorkletsModule *workletsModule, - RuntimeExecutor runtimeExecutor) -{ - auto nodesManager = reaModule.nodesManager; - - auto jsQueue = std::make_shared([NSRunLoop currentRunLoop], ^(NSError *error) { - throw error; - }); - - PlatformDepMethodsHolder platformDepMethodsHolder = - makePlatformDepMethodsHolderBridgeless(moduleRegistry, nodesManager, reaModule); - - const auto nativeWorkletsModule = [workletsModule getNativeWorkletsModule]; - auto uiScheduler = std::make_shared(); - auto jsScheduler = std::make_shared(runtime, runtimeExecutor); - constexpr auto isBridgeless = true; - - auto nativeReanimatedModule = std::make_shared( - nativeWorkletsModule, - runtime, - jsScheduler, - jsQueue, - uiScheduler, - platformDepMethodsHolder, - isBridgeless, - getIsReducedMotion()); - - commonInit(reaModule, nativeReanimatedModule); - - return nativeReanimatedModule; + return reanimatedModuleProxy; } -#endif // RCT_NEW_ARCH_ENABLED -void commonInit(REAModule *reaModule, std::shared_ptr nativeReanimatedModule) +void commonInit(REAModule *reaModule, std::shared_ptr reanimatedModuleProxy) { [reaModule.nodesManager registerEventHandler:^(id event) { // handles RCTEvents from RNGestureHandler std::string eventName = [event.eventName UTF8String]; int emitterReactTag = [event.viewTag intValue]; id eventData = [event arguments][2]; - jsi::Runtime &uiRuntime = nativeReanimatedModule->getUIRuntime(); + jsi::Runtime &uiRuntime = reanimatedModuleProxy->getUIRuntime(); jsi::Value payload = convertObjCObjectToJSIValue(uiRuntime, eventData); double currentTime = CACurrentMediaTime() * 1000; - nativeReanimatedModule->handleEvent(eventName, emitterReactTag, payload, currentTime); + reanimatedModuleProxy->handleEvent(eventName, emitterReactTag, payload, currentTime); }]; #ifdef RCT_NEW_ARCH_ENABLED - std::weak_ptr weakNativeReanimatedModule = nativeReanimatedModule; // to avoid retain cycle + std::weak_ptr weakReanimatedModuleProxy = reanimatedModuleProxy; // to avoid retain cycle [reaModule.nodesManager registerPerformOperations:^() { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - nativeReanimatedModule->performOperations(); + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + reanimatedModuleProxy->performOperations(); } }]; #endif // RCT_NEW_ARCH_ENABLED @@ -165,14 +113,14 @@ void commonInit(REAModule *reaModule, std::shared_ptr na // nothing #else // RCT_NEW_ARCH_ENABLED void setupLayoutAnimationCallbacks( - std::shared_ptr nativeReanimatedModule, + std::shared_ptr reanimatedModuleProxy, REAAnimationsManager *animationsManager) { - std::weak_ptr weakNativeReanimatedModule = nativeReanimatedModule; // to avoid retain cycle + std::weak_ptr weakReanimatedModuleProxy = reanimatedModuleProxy; // to avoid retain cycle [animationsManager setAnimationStartingBlock:^(NSNumber *_Nonnull tag, LayoutAnimationType type, NSDictionary *_Nonnull values) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - jsi::Runtime &rt = nativeReanimatedModule->getUIRuntime(); + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + jsi::Runtime &rt = reanimatedModuleProxy->getUIRuntime(); jsi::Object yogaValues(rt); for (NSString *key in values.allKeys) { NSObject *value = values[key]; @@ -187,59 +135,59 @@ void setupLayoutAnimationCallbacks( yogaValues.setProperty(rt, [key UTF8String], [(NSNumber *)value doubleValue]); } } - nativeReanimatedModule->layoutAnimationsManager().startLayoutAnimation(rt, [tag intValue], type, yogaValues); + reanimatedModuleProxy->layoutAnimationsManager().startLayoutAnimation(rt, [tag intValue], type, yogaValues); } }]; [animationsManager setHasAnimationBlock:^(NSNumber *_Nonnull tag, LayoutAnimationType type) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { bool hasLayoutAnimation = - nativeReanimatedModule->layoutAnimationsManager().hasLayoutAnimation([tag intValue], type); + reanimatedModuleProxy->layoutAnimationsManager().hasLayoutAnimation([tag intValue], type); return hasLayoutAnimation ? YES : NO; } return NO; }]; [animationsManager setShouldAnimateExitingBlock:^(NSNumber *_Nonnull tag, BOOL shouldAnimate) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { bool shouldAnimateExiting = - nativeReanimatedModule->layoutAnimationsManager().shouldAnimateExiting([tag intValue], shouldAnimate); + reanimatedModuleProxy->layoutAnimationsManager().shouldAnimateExiting([tag intValue], shouldAnimate); return shouldAnimateExiting ? YES : NO; } return NO; }]; [animationsManager setAnimationRemovingBlock:^(NSNumber *_Nonnull tag) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - nativeReanimatedModule->layoutAnimationsManager().clearLayoutAnimationConfig([tag intValue]); + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + reanimatedModuleProxy->layoutAnimationsManager().clearLayoutAnimationConfig([tag intValue]); } }]; [animationsManager setSharedTransitionRemovingBlock:^(NSNumber *_Nonnull tag) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - nativeReanimatedModule->layoutAnimationsManager().clearSharedTransitionConfig([tag intValue]); + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + reanimatedModuleProxy->layoutAnimationsManager().clearSharedTransitionConfig([tag intValue]); } }]; [animationsManager setCancelAnimationBlock:^(NSNumber *_Nonnull tag) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - jsi::Runtime &rt = nativeReanimatedModule->getUIRuntime(); - nativeReanimatedModule->layoutAnimationsManager().cancelLayoutAnimation(rt, [tag intValue]); + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + jsi::Runtime &rt = reanimatedModuleProxy->getUIRuntime(); + reanimatedModuleProxy->layoutAnimationsManager().cancelLayoutAnimation(rt, [tag intValue]); } }]; [animationsManager setFindPrecedingViewTagForTransitionBlock:^NSNumber *_Nullable(NSNumber *_Nonnull tag) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { int resultTag = - nativeReanimatedModule->layoutAnimationsManager().findPrecedingViewTagForTransition([tag intValue]); + reanimatedModuleProxy->layoutAnimationsManager().findPrecedingViewTagForTransition([tag intValue]); return resultTag == -1 ? nil : @(resultTag); } return nil; }]; [animationsManager setGetSharedGroupBlock:^NSArray *_Nullable(NSNumber *_Nonnull tag) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { - const auto &results = nativeReanimatedModule->layoutAnimationsManager().getSharedGroup([tag intValue]); + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { + const auto &results = reanimatedModuleProxy->layoutAnimationsManager().getSharedGroup([tag intValue]); NSMutableArray *convertedResult = [NSMutableArray new]; for (const int tag : results) { [convertedResult addObject:@(tag)]; @@ -250,11 +198,11 @@ void setupLayoutAnimationCallbacks( }]; #ifndef NDEBUG [animationsManager setCheckDuplicateSharedTagBlock:^(REAUIView *view, NSNumber *_Nonnull viewTag) { - if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { + if (auto reanimatedModuleProxy = weakReanimatedModuleProxy.lock()) { REAUIView *screen = [REAScreensHelper getScreenForView:(REAUIView *)view]; auto screenTag = [screen.reactTag intValue]; // Here we check if there are duplicate tags (we don't use return bool value currently) - nativeReanimatedModule->layoutAnimationsManager().checkDuplicateSharedTag([viewTag intValue], screenTag); + reanimatedModuleProxy->layoutAnimationsManager().checkDuplicateSharedTag([viewTag intValue], screenTag); } }]; #endif // NDEBUG diff --git a/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.h b/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.h index be566d82d4d..89c937dd8fa 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.h +++ b/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.h @@ -5,7 +5,7 @@ #import #import #import -#import +#import #import #import diff --git a/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.mm b/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.mm index c1a6789aaab..4edfe55cc73 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.mm +++ b/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.mm @@ -10,9 +10,7 @@ #import #import #import -#import #import -#import #import #ifndef NDEBUG @@ -20,6 +18,7 @@ #endif #import +#import #ifdef RCT_NEW_ARCH_ENABLED #import diff --git a/packages/react-native-reanimated/apple/reanimated/apple/native/REAIOSUIScheduler.h b/packages/react-native-reanimated/apple/worklets/apple/IOSUIScheduler.h similarity index 73% rename from packages/react-native-reanimated/apple/reanimated/apple/native/REAIOSUIScheduler.h rename to packages/react-native-reanimated/apple/worklets/apple/IOSUIScheduler.h index 9b7e0ff3db8..b6702fba08f 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/native/REAIOSUIScheduler.h +++ b/packages/react-native-reanimated/apple/worklets/apple/IOSUIScheduler.h @@ -5,15 +5,15 @@ #import -namespace reanimated { +namespace worklets { using namespace facebook; using namespace react; using namespace worklets; -class REAIOSUIScheduler : public UIScheduler { +class IOSUIScheduler : public UIScheduler { public: void scheduleOnUI(std::function job) override; }; -} // namespace reanimated +} // namespace worklets diff --git a/packages/react-native-reanimated/apple/reanimated/apple/native/REAIOSUIScheduler.mm b/packages/react-native-reanimated/apple/worklets/apple/IOSUIScheduler.mm similarity index 60% rename from packages/react-native-reanimated/apple/reanimated/apple/native/REAIOSUIScheduler.mm rename to packages/react-native-reanimated/apple/worklets/apple/IOSUIScheduler.mm index dbf1f050dfe..ced9f39b2f8 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/native/REAIOSUIScheduler.mm +++ b/packages/react-native-reanimated/apple/worklets/apple/IOSUIScheduler.mm @@ -1,11 +1,11 @@ -#import +#import -namespace reanimated { +namespace worklets { using namespace facebook; using namespace react; -void REAIOSUIScheduler::scheduleOnUI(std::function job) +void IOSUIScheduler::scheduleOnUI(std::function job) { if ([NSThread isMainThread]) { job(); @@ -21,4 +21,4 @@ } } -} // namespace reanimated +} // namespace worklets diff --git a/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformLogger.mm b/packages/react-native-reanimated/apple/worklets/apple/PlatformLogger.mm similarity index 82% rename from packages/react-native-reanimated/apple/reanimated/apple/native/PlatformLogger.mm rename to packages/react-native-reanimated/apple/worklets/apple/PlatformLogger.mm index bca68ec2dd3..129180e05c5 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformLogger.mm +++ b/packages/react-native-reanimated/apple/worklets/apple/PlatformLogger.mm @@ -1,7 +1,7 @@ #import -#import +#import -namespace reanimated { +namespace worklets { void PlatformLogger::log(const char *str) { @@ -28,4 +28,4 @@ log(b ? "true" : "false"); } -} // namespace reanimated +} // namespace worklets diff --git a/packages/react-native-reanimated/apple/reanimated/apple/native/REAMessageThread.h b/packages/react-native-reanimated/apple/worklets/apple/WorkletsMessageThread.h similarity index 87% rename from packages/react-native-reanimated/apple/reanimated/apple/native/REAMessageThread.h rename to packages/react-native-reanimated/apple/worklets/apple/WorkletsMessageThread.h index 16f47378a33..2ac8055a090 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/native/REAMessageThread.h +++ b/packages/react-native-reanimated/apple/worklets/apple/WorkletsMessageThread.h @@ -10,7 +10,7 @@ namespace facebook { namespace react { -class REAMessageThread : public RCTMessageThread { +class WorkletsMessageThread : public RCTMessageThread { public: using RCTMessageThread::RCTMessageThread; virtual void quitSynchronous() override; diff --git a/packages/react-native-reanimated/apple/reanimated/apple/native/REAMessageThread.mm b/packages/react-native-reanimated/apple/worklets/apple/WorkletsMessageThread.mm similarity index 67% rename from packages/react-native-reanimated/apple/reanimated/apple/native/REAMessageThread.mm rename to packages/react-native-reanimated/apple/worklets/apple/WorkletsMessageThread.mm index be19a904ef4..b5e46a10e56 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/native/REAMessageThread.mm +++ b/packages/react-native-reanimated/apple/worklets/apple/WorkletsMessageThread.mm @@ -1,4 +1,4 @@ -#import +#import #import #import @@ -11,10 +11,10 @@ namespace react { // Essentially the same as RCTMessageThread, but with public fields. -struct REAMessageThreadPublic { - // I don't know why we need three vtables (if you know then feel free to - // explain it instead of this message), but this is what makes the casts in - // quitSynchronous() work correctly. +struct WorkletsMessageThreadPublic { + // I don't know why we need three vtables (if you know then feel free to#import + // explain it instead of this message), but this is what makes the casts + // in quitSynchronous() work correctly. void *vtable1; void *vtable2; void *vtable3; @@ -27,10 +27,10 @@ // is an assertion for that in the destructor of RCTMessageThread, but we have // to override quitSynchronous() as it would quit the main looper and freeze // the app. -void REAMessageThread::quitSynchronous() +void WorkletsMessageThread::quitSynchronous() { RCTMessageThread *rctThread = static_cast(this); - REAMessageThreadPublic *rctThreadPublic = reinterpret_cast(rctThread); + WorkletsMessageThreadPublic *rctThreadPublic = reinterpret_cast(rctThread); rctThreadPublic->m_shutdown = true; } diff --git a/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.h b/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.h index 25b96da8b9b..5a3b01a10dd 100644 --- a/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.h +++ b/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.h @@ -1,9 +1,9 @@ #import #import -#import +#import @interface WorkletsModule : RCTEventEmitter -- (std::shared_ptr)getNativeWorkletsModule; +- (std::shared_ptr)getWorkletsModuleProxy; @end diff --git a/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.mm b/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.mm index 3872f949ef2..15c03eaf398 100644 --- a/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.mm +++ b/packages/react-native-reanimated/apple/worklets/apple/WorkletsModule.mm @@ -1,10 +1,12 @@ #import +#import #import +#import +#import #import -#import -using worklets::NativeWorkletsModule; using worklets::RNRuntimeWorkletDecorator; +using worklets::WorkletsModuleProxy; @interface RCTBridge (JSIRuntime) - (void *)runtime; @@ -16,15 +18,15 @@ - (void)_tryAndHandleError:(dispatch_block_t)block; @end @implementation WorkletsModule { - std::shared_ptr nativeWorkletsModule_; + std::shared_ptr workletsModuleProxy_; #ifndef NDEBUG worklets::SingleInstanceChecker singleInstanceChecker_; #endif // NDEBUG } -- (std::shared_ptr)getNativeWorkletsModule +- (std::shared_ptr)getWorkletsModuleProxy { - return nativeWorkletsModule_; + return workletsModuleProxy_; } @synthesize moduleRegistry = _moduleRegistry; @@ -35,8 +37,17 @@ @implementation WorkletsModule { { auto *bridge = self.bridge; auto &rnRuntime = *(jsi::Runtime *)bridge.runtime; - nativeWorkletsModule_ = std::make_shared(std::string([valueUnpackerCode UTF8String])); - RNRuntimeWorkletDecorator::decorate(rnRuntime, nativeWorkletsModule_); + auto jsQueue = std::make_shared([NSRunLoop currentRunLoop], ^(NSError *error) { + throw error; + }); + + std::string valueUnpackerCodeStr = [valueUnpackerCode UTF8String]; + auto jsCallInvoker = bridge.jsCallInvoker; + auto jsScheduler = std::make_shared(rnRuntime, jsCallInvoker); + auto uiScheduler = std::make_shared(); + workletsModuleProxy_ = + std::make_shared(valueUnpackerCodeStr, jsQueue, jsCallInvoker, jsScheduler, uiScheduler); + RNRuntimeWorkletDecorator::decorate(rnRuntime, workletsModuleProxy_); return @YES; } diff --git a/packages/react-native-reanimated/package.json b/packages/react-native-reanimated/package.json index e5ef4202800..039ad59842d 100644 --- a/packages/react-native-reanimated/package.json +++ b/packages/react-native-reanimated/package.json @@ -143,7 +143,7 @@ "prettier": "^3.3.3", "react": "18.3.1", "react-native": "^0.76.1", - "react-native-builder-bob": "0.30.2", + "react-native-builder-bob": "0.33.1", "react-native-gesture-handler": "2.20.2", "react-native-web": "0.19.11", "react-test-renderer": "18.2.0", @@ -158,7 +158,8 @@ [ "module", { - "esm": true + "esm": true, + "jsxRuntime": "classic" } ], "typescript" @@ -177,6 +178,5 @@ "./lib/module/core.js", "./lib/module/initializers.js", "./lib/module/index.js" - ], - "packageManager": "yarn@4.1.1" + ] } diff --git a/packages/react-native-reanimated/src/ReanimatedModule/NativeReanimated.ts b/packages/react-native-reanimated/src/ReanimatedModule/NativeReanimated.ts index 364a1126da4..3ff0ef98727 100644 --- a/packages/react-native-reanimated/src/ReanimatedModule/NativeReanimated.ts +++ b/packages/react-native-reanimated/src/ReanimatedModule/NativeReanimated.ts @@ -6,6 +6,7 @@ import type { LayoutAnimationBatchItem, IReanimatedModule, IWorkletsModule, + WorkletFunction, } from '../commonTypes'; import { checkCppVersion } from '../platform-specific/checkCppVersion'; import { jsVersion } from '../platform-specific/jsVersion'; @@ -165,7 +166,7 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti } subscribeForKeyboardEvents( - handler: ShareableRef, + handler: ShareableRef, isStatusBarTranslucent: boolean, isNavigationBarTranslucent: boolean ) { diff --git a/packages/react-native-reanimated/src/ReanimatedModule/js-reanimated/JSReanimated.ts b/packages/react-native-reanimated/src/ReanimatedModule/js-reanimated/JSReanimated.ts index f54963b6171..e4fa3b8a008 100644 --- a/packages/react-native-reanimated/src/ReanimatedModule/js-reanimated/JSReanimated.ts +++ b/packages/react-native-reanimated/src/ReanimatedModule/js-reanimated/JSReanimated.ts @@ -12,6 +12,7 @@ import type { ShareableRef, Value3D, ValueRotation, + WorkletFunction, } from '../../commonTypes'; import type { WebSensor } from './WebSensor'; import { mockedRequestAnimationFrame } from '../../mockedRequestAnimationFrame'; @@ -210,7 +211,7 @@ class JSReanimated implements IReanimatedModule { } } - subscribeForKeyboardEvents(_: ShareableRef): number { + subscribeForKeyboardEvents(_: ShareableRef): number { if (isWeb()) { logger.warn('useAnimatedKeyboard is not available on web yet.'); } else if (isJest()) { diff --git a/packages/react-native-reanimated/src/ReanimatedModule/reanimatedModuleProxy.ts b/packages/react-native-reanimated/src/ReanimatedModule/reanimatedModuleProxy.ts index cff70e423a7..5d334fb9897 100644 --- a/packages/react-native-reanimated/src/ReanimatedModule/reanimatedModuleProxy.ts +++ b/packages/react-native-reanimated/src/ReanimatedModule/reanimatedModuleProxy.ts @@ -6,6 +6,7 @@ import type { Value3D, ValueRotation, LayoutAnimationBatchItem, + WorkletFunction, } from '../commonTypes'; import type { WorkletRuntime } from '../runtimes'; @@ -53,7 +54,7 @@ export interface ReanimatedModuleProxy { configureProps(uiProps: string[], nativeProps: string[]): void; subscribeForKeyboardEvents( - handler: ShareableRef, + handler: ShareableRef, isStatusBarTranslucent: boolean, isNavigationBarTranslucent: boolean ): number; diff --git a/packages/react-native-reanimated/src/Sensor.ts b/packages/react-native-reanimated/src/Sensor.ts index af0c1d2f552..64dc9dd6e92 100644 --- a/packages/react-native-reanimated/src/Sensor.ts +++ b/packages/react-native-reanimated/src/Sensor.ts @@ -6,6 +6,7 @@ import type { Value3D, ValueRotation, ShareableRef, + WorkletFunction, } from './commonTypes'; import { SensorType } from './commonTypes'; import { makeMutable } from './mutables'; @@ -56,7 +57,7 @@ export default class Sensor { sensorType, config.interval === 'auto' ? -1 : config.interval, config.iosReferenceFrame, - eventHandler + eventHandler as ShareableRef ); return this.sensorId !== -1; } diff --git a/packages/react-native-reanimated/src/commonTypes.ts b/packages/react-native-reanimated/src/commonTypes.ts index f50717436d8..725436df1d8 100644 --- a/packages/react-native-reanimated/src/commonTypes.ts +++ b/packages/react-native-reanimated/src/commonTypes.ts @@ -327,10 +327,22 @@ interface WorkletBaseDev extends WorkletBaseCommon { __stackDetails?: WorkletStackDetails; } +export type WorkletFunctionDev< + Args extends unknown[] = unknown[], + ReturnValue = unknown, +> = ((...args: Args) => ReturnValue) & WorkletBaseDev; + +type WorkletFunctionRelease< + Args extends unknown[] = unknown[], + ReturnValue = unknown, +> = ((...args: Args) => ReturnValue) & WorkletBaseRelease; + export type WorkletFunction< Args extends unknown[] = unknown[], ReturnValue = unknown, -> = ((...args: Args) => ReturnValue) & (WorkletBaseRelease | WorkletBaseDev); +> = + | WorkletFunctionDev + | WorkletFunctionRelease; /** * This function allows you to determine if a given function is a worklet. It diff --git a/packages/react-native-reanimated/src/core.ts b/packages/react-native-reanimated/src/core.ts index dbe92773120..c913d948c45 100644 --- a/packages/react-native-reanimated/src/core.ts +++ b/packages/react-native-reanimated/src/core.ts @@ -9,6 +9,7 @@ import type { SharedValue, Value3D, ValueRotation, + WorkletFunction, } from './commonTypes'; import { makeShareableCloneRecursive } from './shareables'; import { initializeUIRuntime } from './initializers'; @@ -85,7 +86,9 @@ export function registerEventHandler( global.__frameTimestamp = undefined; } return ReanimatedModule.registerEventHandler( - makeShareableCloneRecursive(handleAndFlushAnimationFrame), + makeShareableCloneRecursive( + handleAndFlushAnimationFrame as WorkletFunction + ), eventName, emitterReactTag ); @@ -110,7 +113,9 @@ export function subscribeForKeyboardEvents( global.__frameTimestamp = undefined; } return ReanimatedModule.subscribeForKeyboardEvents( - makeShareableCloneRecursive(handleAndFlushAnimationFrame), + makeShareableCloneRecursive( + handleAndFlushAnimationFrame as WorkletFunction + ), options.isStatusBarTranslucentAndroid ?? false, options.isNavigationBarTranslucentAndroid ?? false ); @@ -132,7 +137,7 @@ export function registerSensor( return sensorContainer.registerSensor( sensorType, config, - makeShareableCloneRecursive(eventHandler) + makeShareableCloneRecursive(eventHandler as WorkletFunction) ); } diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/JSPropsUpdater.ts b/packages/react-native-reanimated/src/createAnimatedComponent/JSPropsUpdater.ts index f0bd87da036..70e345b73df 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/JSPropsUpdater.ts +++ b/packages/react-native-reanimated/src/createAnimatedComponent/JSPropsUpdater.ts @@ -38,7 +38,7 @@ class JSPropsUpdaterPaper implements IJSPropsUpdater { > & IAnimatedComponentInternal ) { - const viewTag = animatedComponent._componentViewTag; + const viewTag = animatedComponent.getComponentViewTag(); JSPropsUpdaterPaper._tagToComponentMapping.set(viewTag, animatedComponent); if (JSPropsUpdaterPaper._tagToComponentMapping.size === 1) { const listener = (data: ListenerData) => { @@ -60,7 +60,7 @@ class JSPropsUpdaterPaper implements IJSPropsUpdater { > & IAnimatedComponentInternal ) { - const viewTag = animatedComponent._componentViewTag; + const viewTag = animatedComponent.getComponentViewTag(); JSPropsUpdaterPaper._tagToComponentMapping.delete(viewTag); if (JSPropsUpdaterPaper._tagToComponentMapping.size === 0) { this._reanimatedEventEmitter.removeAllListeners( @@ -100,7 +100,7 @@ class JSPropsUpdaterFabric implements IJSPropsUpdater { if (!JSPropsUpdaterFabric.isInitialized) { return; } - const viewTag = animatedComponent._componentViewTag; + const viewTag = animatedComponent.getComponentViewTag(); JSPropsUpdaterFabric._tagToComponentMapping.set(viewTag, animatedComponent); } @@ -113,7 +113,7 @@ class JSPropsUpdaterFabric implements IJSPropsUpdater { if (!JSPropsUpdaterFabric.isInitialized) { return; } - const viewTag = animatedComponent._componentViewTag; + const viewTag = animatedComponent.getComponentViewTag(); JSPropsUpdaterFabric._tagToComponentMapping.delete(viewTag); } } diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/NativeEventsManager.ts b/packages/react-native-reanimated/src/createAnimatedComponent/NativeEventsManager.ts index da0bbb03b23..e912ae2eca8 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/NativeEventsManager.ts +++ b/packages/react-native-reanimated/src/createAnimatedComponent/NativeEventsManager.ts @@ -39,7 +39,7 @@ export class NativeEventsManager implements INativeEventsManager { public updateEvents( prevProps: AnimatedComponentProps ) { - const computedEventTag = this.getEventViewTag(); + const computedEventTag = this.getEventViewTag(true); // If the event view tag changes, we need to completely re-mount all events if (this.#eventViewTag !== computedEventTag) { // Remove all bindings from previous props that ran on the old viewTag @@ -77,23 +77,54 @@ export class NativeEventsManager implements INativeEventsManager { }); } - private getEventViewTag() { + private getEventViewTag(componentUpdate: boolean = false) { // Get the tag for registering events - since the event emitting view can be nested inside the main component const componentAnimatedRef = this.#managedComponent - ._component as AnimatedComponentRef; - let newTag: number; + ._componentRef as AnimatedComponentRef & { + // Fabric + __nativeTag?: number; + // Paper + _nativeTag?: number; + }; if (componentAnimatedRef.getScrollableNode) { + /* + In most cases, getScrollableNode() returns a view tag, and findNodeHandle is not required. + However, to cover more exotic list cases, we will continue to use findNodeHandle + for consistency. For numerical values, findNodeHandle should return the value immediately, + as documented here: https://github.com/facebook/react/blob/91061073d57783c061889ac6720ef1ab7f0c2149/packages/react-native-renderer/src/ReactNativePublicCompat.js#L113 + */ const scrollableNode = componentAnimatedRef.getScrollableNode(); - newTag = findNodeHandle(scrollableNode) ?? -1; - } else { - newTag = - findNodeHandle( - this.#componentOptions?.setNativeProps - ? this.#managedComponent - : componentAnimatedRef - ) ?? -1; + if (typeof scrollableNode === 'number') { + return scrollableNode; + } + return findNodeHandle(scrollableNode) ?? -1; + } + if (this.#componentOptions?.setNativeProps) { + // This case ensures backward compatibility with components that + // have their own setNativeProps method passed as an option. + return findNodeHandle(this.#managedComponent) ?? -1; + } + if (!componentUpdate) { + // On the first render of a component, we may already receive a resolved view tag. + return this.#managedComponent.getComponentViewTag(); + } + if (componentAnimatedRef.__nativeTag || componentAnimatedRef._nativeTag) { + /* + Fast path for native refs, + _nativeTag is used by Paper components, + __nativeTag is used by Fabric components. + */ + return ( + componentAnimatedRef.__nativeTag ?? + componentAnimatedRef._nativeTag ?? + -1 + ); } - return newTag; + /* + When a component is updated, a child could potentially change and have a different + view tag. This can occur with a GestureDetector component. + */ + return findNodeHandle(componentAnimatedRef) ?? -1; } } diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts b/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts index edb1c867c23..d48b3d771f4 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts +++ b/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts @@ -100,15 +100,10 @@ export interface AnimatedComponentRef extends Component { export interface IAnimatedComponentInternal { _styles: StyleProps[] | null; _animatedProps?: Partial>; - /** - * Used for Shared Element Transitions, Layout Animations and Animated Styles. - * It is not related to event handling. - */ - _componentViewTag: number; _isFirstRender: boolean; jestInlineStyle: NestedArray | undefined; jestAnimatedStyle: { value: StyleProps }; - _component: AnimatedComponentRef | HTMLElement | null; + _componentRef: AnimatedComponentRef | HTMLElement | null; _sharedElementTransition: SharedTransition | null; _jsPropsUpdater: IJSPropsUpdater; _InlinePropManager: IInlinePropManager; @@ -117,6 +112,11 @@ export interface IAnimatedComponentInternal { _NativeEventsManager?: INativeEventsManager; _viewInfo?: ViewInfo; context: React.ContextType; + /** + * Used for Shared Element Transitions, Layout Animations and Animated Styles. + * It is not related to event handling. + */ + getComponentViewTag: () => number; } export type NestedArray = T | NestedArray[]; diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx b/packages/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx index 9750bb853d6..762e2693bdc 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx +++ b/packages/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx @@ -8,11 +8,10 @@ import type { } from 'react'; import React from 'react'; import { Platform } from 'react-native'; -import { findNodeHandle } from '../platformFunctions/findNodeHandle'; import '../layoutReanimation/animationsManager'; import invariant from 'invariant'; import { adaptViewConfig } from '../ConfigHelper'; -import { RNRenderer } from '../platform-specific/RNRenderer'; +import { findHostInstance } from '../platform-specific/findHostInstance'; import { enableLayoutAnimations } from '../core'; import { SharedTransition } from '../layoutReanimation'; import { LayoutAnimationType } from '../commonTypes'; @@ -125,11 +124,10 @@ export function createAnimatedComponent( { _styles: StyleProps[] | null = null; _animatedProps?: Partial>; - _componentViewTag = -1; _isFirstRender = true; jestInlineStyle: NestedArray | undefined; jestAnimatedStyle: { value: StyleProps } = { value: {} }; - _component: AnimatedComponentRef | HTMLElement | null = null; + _componentRef: AnimatedComponentRef | HTMLElement | null = null; _sharedElementTransition: SharedTransition | null = null; _jsPropsUpdater = new JSPropsUpdater(); _InlinePropManager = new InlinePropManager(); @@ -157,7 +155,6 @@ export function createAnimatedComponent( } componentDidMount() { - this._componentViewTag = this._getComponentViewTag(); if (!IS_WEB) { // It exists only on native platforms. We initialize it here because the ref to the animated component is available only post-mount this._NativeEventsManager = new NativeEventsManager(this, options); @@ -174,7 +171,7 @@ export function createAnimatedComponent( if (IS_WEB) { if (this.props.exiting) { - saveSnapshot(this._component as HTMLElement); + saveSnapshot(this._componentRef as HTMLElement); } if ( @@ -190,11 +187,11 @@ export function createAnimatedComponent( if (!skipEntering) { startWebLayoutAnimation( this.props, - this._component as ReanimatedHTMLElement, + this._componentRef as ReanimatedHTMLElement, LayoutAnimationType.ENTERING ); } else { - (this._component as HTMLElement).style.visibility = 'initial'; + (this._componentRef as HTMLElement).style.visibility = 'initial'; } } @@ -210,7 +207,7 @@ export function createAnimatedComponent( this._configureSharedTransition(true); } this._sharedElementTransition?.unregisterTransition( - this._componentViewTag, + this.getComponentViewTag(), true ); @@ -218,7 +215,7 @@ export function createAnimatedComponent( if ( IS_WEB && - this._component && + this._componentRef && exiting && !getReducedMotionFromConfig(exiting as CustomConfig) ) { @@ -226,7 +223,7 @@ export function createAnimatedComponent( startWebLayoutAnimation( this.props, - this._component as ReanimatedHTMLElement, + this._componentRef as ReanimatedHTMLElement, LayoutAnimationType.EXITING ); } else if (exiting && !IS_WEB && !isFabric()) { @@ -237,7 +234,7 @@ export function createAnimatedComponent( : getReduceMotionFromConfig(); if (!reduceMotionInExiting) { updateLayoutAnimations( - this._componentViewTag, + this.getComponentViewTag(), LayoutAnimationType.EXITING, maybeBuild( exiting, @@ -249,31 +246,33 @@ export function createAnimatedComponent( } } - _getComponentViewTag() { + getComponentViewTag() { return this._getViewInfo().viewTag as number; } _detachStyles() { - if (this._componentViewTag !== -1 && this._styles !== null) { + const viewTag = this.getComponentViewTag(); + if (viewTag !== -1 && this._styles !== null) { for (const style of this._styles) { - style.viewDescriptors.remove(this._componentViewTag); + style.viewDescriptors.remove(viewTag); } if (this.props.animatedProps?.viewDescriptors) { - this.props.animatedProps.viewDescriptors.remove( - this._componentViewTag - ); + this.props.animatedProps.viewDescriptors.remove(viewTag); } if (isFabric()) { - removeFromPropsRegistry(this._componentViewTag); + removeFromPropsRegistry(viewTag); } } } _updateFromNative(props: StyleProps) { if (options?.setNativeProps) { - options.setNativeProps(this._component as AnimatedComponentRef, props); + options.setNativeProps( + this._componentRef as AnimatedComponentRef, + props + ); } else { - (this._component as AnimatedComponentRef)?.setNativeProps?.(props); + (this._componentRef as AnimatedComponentRef)?.setNativeProps?.(props); } } @@ -286,24 +285,22 @@ export function createAnimatedComponent( let viewName: string | null; let shadowNodeWrapper: ShadowNodeWrapper | null = null; let viewConfig; - // Component can specify ref which should be animated when animated version of the component is created. - // Otherwise, we animate the component itself. - const component = (this._component as AnimatedComponentRef) - ?.getAnimatableRef - ? (this._component as AnimatedComponentRef).getAnimatableRef?.() - : this; if (SHOULD_BE_USE_WEB) { // At this point I assume that `_setComponentRef` was already called and `_component` is set. // `this._component` on web represents HTMLElement of our component, that's why we use casting - viewTag = this._component as HTMLElement; + viewTag = this._componentRef as HTMLElement; viewName = null; shadowNodeWrapper = null; viewConfig = null; } else { - // hostInstance can be null for a component that doesn't render anything (render function returns null). Example: svg Stop: https://github.com/react-native-svg/react-native-svg/blob/develop/src/elements/Stop.tsx - const hostInstance = RNRenderer.findHostInstance_DEPRECATED(component); + const hostInstance = findHostInstance(this); if (!hostInstance) { + /* + findHostInstance can return null for a component that doesn't render anything + (render function returns null). Example: + svg Stop: https://github.com/react-native-svg/react-native-svg/blob/develop/src/elements/Stop.tsx + */ throw new ReanimatedError( 'Cannot find host instance for this component. Maybe it renders nothing?' ); @@ -314,7 +311,7 @@ export function createAnimatedComponent( viewName = viewInfo.viewName; viewConfig = viewInfo.viewConfig; shadowNodeWrapper = isFabric() - ? getShadowNodeWrapperFromRef(this) + ? getShadowNodeWrapperFromRef(this, hostInstance) : null; } this._viewInfo = { viewTag, viewName, shadowNodeWrapper, viewConfig }; @@ -341,8 +338,6 @@ export function createAnimatedComponent( adaptViewConfig(viewConfig); } - this._componentViewTag = viewTag as number; - // remove old styles if (prevStyles) { // in most of the cases, views have only a single animated style and it remains unchanged @@ -422,7 +417,7 @@ export function createAnimatedComponent( this._InlinePropManager.attachInlineProps(this, this._getViewInfo()); if (IS_WEB && this.props.exiting) { - saveSnapshot(this._component as HTMLElement); + saveSnapshot(this._componentRef as HTMLElement); } // Snapshot won't be undefined because it comes from getSnapshotBeforeUpdate method @@ -434,7 +429,7 @@ export function createAnimatedComponent( ) { tryActivateLayoutTransition( this.props, - this._component as ReanimatedHTMLElement, + this._componentRef as ReanimatedHTMLElement, snapshot ); } @@ -453,7 +448,7 @@ export function createAnimatedComponent( ) : undefined; updateLayoutAnimations( - this._componentViewTag, + this.getComponentViewTag(), LayoutAnimationType.LAYOUT, layout ); @@ -467,7 +462,7 @@ export function createAnimatedComponent( const { sharedTransitionTag } = this.props; if (!sharedTransitionTag) { this._sharedElementTransition?.unregisterTransition( - this._componentViewTag, + this.getComponentViewTag(), isUnmounting ); this._sharedElementTransition = null; @@ -478,34 +473,42 @@ export function createAnimatedComponent( this._sharedElementTransition ?? new SharedTransition(); sharedElementTransition.registerTransition( - this._componentViewTag, + this.getComponentViewTag(), sharedTransitionTag, isUnmounting ); this._sharedElementTransition = sharedElementTransition; } + _resolveComponentRef = (ref: Component | HTMLElement | null) => { + const componentRef = ref as AnimatedComponentRef; + // Component can specify ref which should be animated when animated version of the component is created. + // Otherwise, we animate the component itself. + if (componentRef && componentRef.getAnimatableRef) { + return componentRef.getAnimatableRef(); + } + return componentRef; + }; + _setComponentRef = setAndForwardRef({ getForwardedRef: () => this.props.forwardedRef as MutableRefObject< Component, Record, unknown> >, setLocalRef: (ref) => { - // TODO update config - - const tag = findNodeHandle(ref as Component); - - // callback refs are executed twice - when the component mounts with ref, - // and with null when it unmounts - if (tag !== null) { - this._componentViewTag = tag; + if (!ref) { + // component has been unmounted + return; + } + if (ref !== this._componentRef) { + this._componentRef = this._resolveComponentRef(ref); + // if ref is changed, reset viewInfo + this._viewInfo = undefined; } + const tag = this.getComponentViewTag(); const { layout, entering, exiting, sharedTransitionTag } = this.props; - if ( - (layout || entering || exiting || sharedTransitionTag) && - tag != null - ) { + if (layout || entering || exiting || sharedTransitionTag) { if (!SHOULD_BE_USE_WEB) { enableLayoutAnimations(true, false); } @@ -545,10 +548,6 @@ export function createAnimatedComponent( ); } } - - if (ref !== this._component) { - this._component = ref; - } }, }); @@ -558,9 +557,9 @@ export function createAnimatedComponent( getSnapshotBeforeUpdate() { if ( IS_WEB && - (this._component as HTMLElement)?.getBoundingClientRect !== undefined + (this._componentRef as HTMLElement)?.getBoundingClientRect !== undefined ) { - return (this._component as HTMLElement).getBoundingClientRect(); + return (this._componentRef as HTMLElement).getBoundingClientRect(); } return null; diff --git a/packages/react-native-reanimated/src/fabricUtils.ts b/packages/react-native-reanimated/src/fabricUtils.ts index a872b9a2167..e6eff46e125 100644 --- a/packages/react-native-reanimated/src/fabricUtils.ts +++ b/packages/react-native-reanimated/src/fabricUtils.ts @@ -2,25 +2,19 @@ /* eslint-disable */ import type { ShadowNodeWrapper } from './commonTypes'; +import { + findHostInstance, + HostInstance, +} from './platform-specific/findHostInstance'; -let findHostInstance_DEPRECATED: (ref: unknown) => void; let getInternalInstanceHandleFromPublicInstance: (ref: unknown) => { stateNode: { node: unknown }; }; export function getShadowNodeWrapperFromRef( - ref: React.Component + ref: React.Component, + hostInstance?: HostInstance ): ShadowNodeWrapper { - // load findHostInstance_DEPRECATED lazily because it may not be available before render - if (findHostInstance_DEPRECATED === undefined) { - try { - findHostInstance_DEPRECATED = - require('react-native/Libraries/Renderer/shims/ReactFabric').findHostInstance_DEPRECATED; - } catch (e) { - findHostInstance_DEPRECATED = (_ref: unknown) => null; - } - } - if (getInternalInstanceHandleFromPublicInstance === undefined) { try { getInternalInstanceHandleFromPublicInstance = @@ -50,9 +44,9 @@ export function getShadowNodeWrapperFromRef( } else if (textInputRef) { resolvedRef = textInputRef; } else { - resolvedRef = getInternalInstanceHandleFromPublicInstance( - findHostInstance_DEPRECATED(ref) - ).stateNode.node; + const instance = hostInstance ?? findHostInstance(ref); + resolvedRef = + getInternalInstanceHandleFromPublicInstance(instance).stateNode.node; } return resolvedRef; diff --git a/packages/react-native-reanimated/src/mock.ts b/packages/react-native-reanimated/src/mock.ts index 3d8e3f8a67c..576df2d34b4 100644 --- a/packages/react-native-reanimated/src/mock.ts +++ b/packages/react-native-reanimated/src/mock.ts @@ -87,7 +87,11 @@ const hook = { useAnimatedReaction: NOOP, useAnimatedRef: () => ({ current: null }), useAnimatedScrollHandler: NOOP_FACTORY, - useDerivedValue: (processor: () => Value) => ({ value: processor() }), + useDerivedValue: (processor: () => Value) => { + const result = processor(); + + return { value: result, get: () => result }; + }, useAnimatedSensor: () => ({ sensor: { value: { diff --git a/packages/react-native-reanimated/src/mutables.ts b/packages/react-native-reanimated/src/mutables.ts index 4a05b56b103..a0aac328df3 100644 --- a/packages/react-native-reanimated/src/mutables.ts +++ b/packages/react-native-reanimated/src/mutables.ts @@ -18,7 +18,7 @@ function shouldWarnAboutAccessDuringRender() { function checkInvalidReadDuringRender() { if (shouldWarnAboutAccessDuringRender()) { logger.warn( - 'Reading from `value` during component render. Please ensure that you do not access the `value` property or use `get` method of a shared value while React is rendering a component.', + "Reading from `value` during component render. Please ensure that you don't access the `value` property nor use `get` method of a shared value while React is rendering a component.", { strict: true } ); } @@ -27,7 +27,7 @@ function checkInvalidReadDuringRender() { function checkInvalidWriteDuringRender() { if (shouldWarnAboutAccessDuringRender()) { logger.warn( - 'Writing to `value` during component render. Please ensure that you do not access the `value` property or use `set` method of a shared value while React is rendering a component.', + "Writing to `value` during component render. Please ensure that you don't access the `value` property nor use `set` method of a shared value while React is rendering a component.", { strict: true } ); } diff --git a/packages/react-native-reanimated/src/platform-specific/RNRenderer.ts b/packages/react-native-reanimated/src/platform-specific/RNRenderer.ts deleted file mode 100644 index cc5c78a5005..00000000000 --- a/packages/react-native-reanimated/src/platform-specific/RNRenderer.ts +++ /dev/null @@ -1,4 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-nocheck -'use strict'; -export { default as RNRenderer } from 'react-native/Libraries/Renderer/shims/ReactNative'; diff --git a/packages/react-native-reanimated/src/platform-specific/RNRenderer.web.ts b/packages/react-native-reanimated/src/platform-specific/RNRenderer.web.ts deleted file mode 100644 index f95b74d4322..00000000000 --- a/packages/react-native-reanimated/src/platform-specific/RNRenderer.web.ts +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; -// RNRender is not used for web. An export is still defined to eliminate warnings from bundlers such as esbuild. -const RNRenderer = {}; -export { RNRenderer }; diff --git a/packages/react-native-reanimated/src/platform-specific/findHostInstance.ts b/packages/react-native-reanimated/src/platform-specific/findHostInstance.ts new file mode 100644 index 00000000000..00d56154e40 --- /dev/null +++ b/packages/react-native-reanimated/src/platform-specific/findHostInstance.ts @@ -0,0 +1,77 @@ +/* eslint-disable camelcase */ +'use strict'; + +import type { IAnimatedComponentInternal } from '../createAnimatedComponent/commonTypes'; +import { ReanimatedError } from '../errors'; +import { isFabric } from '../PlatformChecker'; + +type HostInstanceFabric = { + __internalInstanceHandle?: Record; + __nativeTag?: number; + _viewConfig?: Record; +}; + +type HostInstancePaper = { + _nativeTag?: number; + viewConfig?: Record; +}; + +export type HostInstance = HostInstanceFabric & HostInstancePaper; + +function findHostInstanceFastPath(maybeNativeRef: HostInstance) { + if ( + maybeNativeRef.__internalInstanceHandle && + maybeNativeRef.__nativeTag && + maybeNativeRef._viewConfig + ) { + // This is a native ref to a Fabric component + return maybeNativeRef; + } + if (maybeNativeRef._nativeTag && maybeNativeRef.viewConfig) { + // This is a native ref to a Paper component + return maybeNativeRef; + } + // That means it’s a ref to a non-native component, and it’s necessary + // to call `findHostInstance_DEPRECATED` on them. + return undefined; +} + +function resolveFindHostInstance_DEPRECATED() { + if (findHostInstance_DEPRECATED !== undefined) { + return; + } + if (isFabric()) { + try { + findHostInstance_DEPRECATED = + require('react-native/Libraries/Renderer/shims/ReactFabric').findHostInstance_DEPRECATED; + } catch (e) { + throw new ReanimatedError( + 'Failed to resolve findHostInstance_DEPRECATED' + ); + } + } else { + findHostInstance_DEPRECATED = + require('react-native/Libraries/Renderer/shims/ReactNative').findHostInstance_DEPRECATED; + } +} + +let findHostInstance_DEPRECATED: (ref: unknown) => HostInstance; +export function findHostInstance( + component: IAnimatedComponentInternal | React.Component +): HostInstance { + // Fast path for native refs + const hostInstance = findHostInstanceFastPath( + (component as IAnimatedComponentInternal)._componentRef as HostInstance + ); + if (hostInstance !== undefined) { + return hostInstance; + } + + resolveFindHostInstance_DEPRECATED(); + // Fabric implementation of findHostInstance_DEPRECATED doesn't accept a ref as an argument + return findHostInstance_DEPRECATED( + isFabric() + ? component + : (component as IAnimatedComponentInternal)._componentRef + ); +} diff --git a/packages/react-native-reanimated/src/platform-specific/findHostInstance.web.ts b/packages/react-native-reanimated/src/platform-specific/findHostInstance.web.ts new file mode 100644 index 00000000000..ca342dfd2d4 --- /dev/null +++ b/packages/react-native-reanimated/src/platform-specific/findHostInstance.web.ts @@ -0,0 +1,3 @@ +'use strict'; + +export function findHostInstance(_component: any): void {} diff --git a/packages/react-native-reanimated/src/shareables.ts b/packages/react-native-reanimated/src/shareables.ts index 23070fe7de9..ec4bdea2b33 100644 --- a/packages/react-native-reanimated/src/shareables.ts +++ b/packages/react-native-reanimated/src/shareables.ts @@ -4,6 +4,7 @@ import type { ShareableRef, FlatShareableRef, WorkletFunction, + WorkletFunctionDev, } from './commonTypes'; import { shouldBeUseWeb } from './PlatformChecker'; import { ReanimatedError, registerWorkletStackDetails } from './errors'; @@ -32,10 +33,19 @@ function isHostObject(value: NonNullable) { return MAGIC_KEY in value; } -function isPlainJSObject(object: object) { +function isPlainJSObject(object: object): object is Record { return Object.getPrototypeOf(object) === Object.prototype; } +function getFromCache(value: object) { + const cached = shareableMappingCache.get(value); + if (cached === shareableMappingFlag) { + // This means that `value` was already a clone and we should return it as is. + return value; + } + return cached; +} + // The below object is used as a replacement for objects that cannot be transferred // as shareable values. In makeShareableCloneRecursive we detect if an object is of // a plain Object.prototype and only allow such objects to be transferred. This lets @@ -100,14 +110,72 @@ const DETECT_CYCLIC_OBJECT_DEPTH_THRESHOLD = 30; // We use it to check if later on the function reenters with the same object let processedObjectAtThresholdDepth: unknown; -export function makeShareableCloneRecursive( - value: any, +function makeShareableCloneRecursiveWeb(value: T): ShareableRef { + return value as ShareableRef; +} + +function makeShareableCloneRecursiveNative( + value: T, shouldPersistRemote = false, depth = 0 ): ShareableRef { - if (SHOULD_BE_USE_WEB) { - return value; + detectCyclicObject(value, depth); + + const isObject = typeof value === 'object'; + const isFunction = typeof value === 'function'; + + if ((!isObject && !isFunction) || value === null) { + return clonePrimitive(value, shouldPersistRemote); + } + + const cached = getFromCache(value); + if (cached !== undefined) { + return cached as ShareableRef; + } + + if (Array.isArray(value)) { + return cloneArray(value, shouldPersistRemote, depth); + } + if (isFunction && !isWorkletFunction(value)) { + return cloneRemoteFunction(value, shouldPersistRemote); + } + if (isHostObject(value)) { + return cloneHostObject(value, shouldPersistRemote); } + if (isPlainJSObject(value) && value.__workletContextObjectFactory) { + return cloneContextObject(value); + } + if ((isPlainJSObject(value) || isFunction) && isWorkletFunction(value)) { + return cloneWorklet(value, shouldPersistRemote, depth); + } + if (isPlainJSObject(value) || isFunction) { + return clonePlainJSObject(value, shouldPersistRemote, depth); + } + if (value instanceof RegExp) { + return cloneRegExp(value); + } + if (value instanceof Error) { + return cloneError(value); + } + if (value instanceof ArrayBuffer) { + return cloneArrayBuffer(value, shouldPersistRemote); + } + if (ArrayBuffer.isView(value)) { + // typed array (e.g. Int32Array, Uint8ClampedArray) or DataView + return cloneArrayBufferView(value); + } + return inaccessibleObject(value); +} + +interface MakeShareableClone { + (value: T, shouldPersistRemote?: boolean, depth?: number): ShareableRef; +} + +export const makeShareableCloneRecursive: MakeShareableClone = SHOULD_BE_USE_WEB + ? makeShareableCloneRecursiveWeb + : makeShareableCloneRecursiveNative; + +function detectCyclicObject(value: unknown, depth: number) { if (depth >= DETECT_CYCLIC_OBJECT_DEPTH_THRESHOLD) { // if we reach certain recursion depth we suspect that we are dealing with a cyclic object. // this type of objects are not supported and cannot be transferred as shareable, so we @@ -124,176 +192,259 @@ export function makeShareableCloneRecursive( } else { processedObjectAtThresholdDepth = undefined; } - // This one actually may be worth to be moved to c++, we also need similar logic to run on the UI thread - const type = typeof value; - const isTypeObject = type === 'object'; - const isTypeFunction = type === 'function'; - if ((isTypeObject || isTypeFunction) && value !== null) { - const cached = shareableMappingCache.get(value); - if (cached === shareableMappingFlag) { - return value; - } else if (cached !== undefined) { - return cached as ShareableRef; - } else { - let toAdapt: any; - if (Array.isArray(value)) { - toAdapt = value.map((element) => - makeShareableCloneRecursive(element, shouldPersistRemote, depth + 1) - ); - freezeObjectIfDev(value); - } else if (isTypeFunction && !isWorkletFunction(value)) { - // this is a remote function - toAdapt = value; - freezeObjectIfDev(value); - } else if (isHostObject(value)) { - // for host objects we pass the reference to the object as shareable and - // then recreate new host object wrapping the same instance on the UI thread. - // there is no point of iterating over keys as we do for regular objects. - toAdapt = value; - } else if ( - isPlainJSObject(value) && - value.__workletContextObjectFactory - ) { - const workletContextObjectFactory = value.__workletContextObjectFactory; - const handle = makeShareableCloneRecursive({ - __init: () => { - 'worklet'; - return workletContextObjectFactory(); - }, - }); - shareableMappingCache.set(value, handle); - return handle as ShareableRef; - } else if (isPlainJSObject(value) || isTypeFunction) { - toAdapt = {}; - if (isWorkletFunction(value)) { - if (__DEV__) { - const babelVersion = value.__initData.version; - if (babelVersion !== undefined && babelVersion !== jsVersion) { - throw new ReanimatedError(`Mismatch between JavaScript code version and Reanimated Babel plugin version (${jsVersion} vs. ${babelVersion}). +} + +function clonePrimitive( + value: T, + shouldPersistRemote: boolean +): ShareableRef { + return WorkletsModule.makeShareableClone(value, shouldPersistRemote); +} + +function cloneArray( + value: T, + shouldPersistRemote: boolean, + depth: number +): ShareableRef { + const clonedElements = value.map((element) => + makeShareableCloneRecursive(element, shouldPersistRemote, depth + 1) + ); + const clone = WorkletsModule.makeShareableClone( + clonedElements, + shouldPersistRemote, + value + ) as ShareableRef; + shareableMappingCache.set(value, clone); + shareableMappingCache.set(clone); + + freezeObjectInDev(value); + return clone; +} + +function cloneRemoteFunction( + value: T, + shouldPersistRemote: boolean +): ShareableRef { + const clone = WorkletsModule.makeShareableClone( + value, + shouldPersistRemote, + value + ); + shareableMappingCache.set(value, clone); + shareableMappingCache.set(clone); + + freezeObjectInDev(value); + return clone; +} + +function cloneHostObject( + value: T, + shouldPersistRemote: boolean +): ShareableRef { + // for host objects we pass the reference to the object as shareable and + // then recreate new host object wrapping the same instance on the UI thread. + // there is no point of iterating over keys as we do for regular objects. + const clone = WorkletsModule.makeShareableClone( + value, + shouldPersistRemote, + value + ); + shareableMappingCache.set(value, clone); + shareableMappingCache.set(clone); + + return clone; +} + +function cloneWorklet( + value: T, + shouldPersistRemote: boolean, + depth: number +): ShareableRef { + if (__DEV__) { + const babelVersion = (value as WorkletFunctionDev).__initData.version; + if (babelVersion !== undefined && babelVersion !== jsVersion) { + throw new ReanimatedError(`[Reanimated] Mismatch between JavaScript code version and Reanimated Babel plugin version (${jsVersion} vs. ${babelVersion}). See \`https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#mismatch-between-javascript-code-version-and-reanimated-babel-plugin-version\` for more details. Offending code was: \`${getWorkletCode(value)}\``); - } - registerWorkletStackDetails( - value.__workletHash, - value.__stackDetails! - ); - } - if (value.__stackDetails) { - // `Error` type of value cannot be copied to the UI thread, so we - // remove it after we handled it in dev mode or delete it to ignore it in production mode. - // Not removing this would cause an infinite loop in production mode and it just - // seems more elegant to handle it this way. - delete value.__stackDetails; - } - // to save on transferring static __initData field of worklet structure - // we request shareable value to persist its UI counterpart. This means - // that the __initData field that contains long strings representing the - // worklet code, source map, and location, will always be - // serialized/deserialized once. - toAdapt.__initData = makeShareableCloneRecursive( - value.__initData, - true, - depth + 1 - ); - } + } + registerWorkletStackDetails( + value.__workletHash, + (value as WorkletFunctionDev).__stackDetails! + ); + } + if ((value as WorkletFunctionDev).__stackDetails) { + // `Error` type of value cannot be copied to the UI thread, so we + // remove it after we handled it in dev mode or delete it to ignore it in production mode. + // Not removing this would cause an infinite loop in production mode and it just + // seems more elegant to handle it this way. + delete (value as WorkletFunctionDev).__stackDetails; + } + // to save on transferring static __initData field of worklet structure + // we request shareable value to persist its UI counterpart. This means + // that the __initData field that contains long strings represeting the + // worklet code, source map, and location, will always be + // serialized/deserialized once. + const clonedProps: Record = {}; + clonedProps.__initData = makeShareableCloneRecursive( + value.__initData, + true, + depth + 1 + ); - for (const [key, element] of Object.entries(value)) { - if (key === '__initData' && toAdapt.__initData !== undefined) { - continue; - } - toAdapt[key] = makeShareableCloneRecursive( - element, - shouldPersistRemote, - depth + 1 - ); - } - freezeObjectIfDev(value); - } else if (value instanceof RegExp) { - const pattern = value.source; - const flags = value.flags; - const handle = makeShareableCloneRecursive({ - __init: () => { - 'worklet'; - return new RegExp(pattern, flags); - }, - }); - shareableMappingCache.set(value, handle); - return handle as ShareableRef; - } else if (value instanceof Error) { - const { name, message, stack } = value; - const handle = makeShareableCloneRecursive({ - __init: () => { - 'worklet'; - // eslint-disable-next-line reanimated/use-reanimated-error - const error = new Error(); - error.name = name; - error.message = message; - error.stack = stack; - return error; - }, - }); - shareableMappingCache.set(value, handle); - return handle as ShareableRef; - } else if (value instanceof ArrayBuffer) { - toAdapt = value; - } else if (ArrayBuffer.isView(value)) { - // typed array (e.g. Int32Array, Uint8ClampedArray) or DataView - const buffer = value.buffer; - const typeName = value.constructor.name; - const handle = makeShareableCloneRecursive({ - __init: () => { - 'worklet'; - if (!VALID_ARRAY_VIEWS_NAMES.includes(typeName)) { - throw new ReanimatedError( - `Invalid array view name \`${typeName}\`.` - ); - } - const constructor = global[typeName as keyof typeof global]; - if (constructor === undefined) { - throw new ReanimatedError( - `Constructor for \`${typeName}\` not found.` - ); - } - return new constructor(buffer); - }, - }); - shareableMappingCache.set(value, handle); - return handle as ShareableRef; - } else { - // This is reached for object types that are not of plain Object.prototype. - // We don't support such objects from being transferred as shareables to - // the UI runtime and hence we replace them with "inaccessible object" - // which is implemented as a Proxy object that throws on any attempt - // of accessing its fields. We argue that such objects can sometimes leak - // as attributes of objects being captured by worklets but should never - // be used on the UI runtime regardless. If they are being accessed, the user - // will get an appropriate error message. - const inaccessibleObject = - makeShareableCloneRecursive(INACCESSIBLE_OBJECT); - shareableMappingCache.set(value, inaccessibleObject); - return inaccessibleObject; - } - const adapted = WorkletsModule.makeShareableClone( - toAdapt, - shouldPersistRemote, - value - ); - shareableMappingCache.set(value, adapted); - shareableMappingCache.set(adapted); - return adapted; + for (const [key, element] of Object.entries(value)) { + if (key === '__initData' && clonedProps.__initData !== undefined) { + continue; } + clonedProps[key] = makeShareableCloneRecursive( + element, + shouldPersistRemote, + depth + 1 + ); } - return WorkletsModule.makeShareableClone( + const clone = WorkletsModule.makeShareableClone( + clonedProps, + // retain all worklets + true, + value + ) as ShareableRef; + shareableMappingCache.set(value, clone); + shareableMappingCache.set(clone); + + freezeObjectInDev(value); + return clone; +} + +function cloneContextObject(value: T): ShareableRef { + const workletContextObjectFactory = (value as Record) + .__workletContextObjectFactory as () => T; + const handle = makeShareableCloneRecursive({ + __init: () => { + 'worklet'; + return workletContextObjectFactory(); + }, + }); + shareableMappingCache.set(value, handle); + return handle as ShareableRef; +} + +function clonePlainJSObject( + value: T, + shouldPersistRemote: boolean, + depth: number +): ShareableRef { + const clonedProps: Record = {}; + for (const [key, element] of Object.entries(value)) { + if (key === '__initData' && clonedProps.__initData !== undefined) { + continue; + } + clonedProps[key] = makeShareableCloneRecursive( + element, + shouldPersistRemote, + depth + 1 + ); + } + const clone = WorkletsModule.makeShareableClone( + clonedProps, + shouldPersistRemote, + value + ) as ShareableRef; + shareableMappingCache.set(value, clone); + shareableMappingCache.set(clone); + + freezeObjectInDev(value); + return clone; +} + +function cloneRegExp(value: T): ShareableRef { + const pattern = value.source; + const flags = value.flags; + const handle = makeShareableCloneRecursive({ + __init: () => { + 'worklet'; + return new RegExp(pattern, flags); + }, + }) as unknown as ShareableRef; + shareableMappingCache.set(value, handle); + + return handle; +} + +function cloneError(value: T): ShareableRef { + const { name, message, stack } = value; + const handle = makeShareableCloneRecursive({ + __init: () => { + 'worklet'; + // eslint-disable-next-line reanimated/use-reanimated-error + const error = new Error(); + error.name = name; + error.message = message; + error.stack = stack; + return error; + }, + }); + shareableMappingCache.set(value, handle); + return handle as unknown as ShareableRef; +} + +function cloneArrayBuffer( + value: T, + shouldPersistRemote: boolean +): ShareableRef { + const clone = WorkletsModule.makeShareableClone( value, shouldPersistRemote, - undefined + value ); + shareableMappingCache.set(value, clone); + shareableMappingCache.set(clone); + + return clone; +} + +function cloneArrayBufferView( + value: T +): ShareableRef { + const buffer = value.buffer; + const typeName = value.constructor.name; + const handle = makeShareableCloneRecursive({ + __init: () => { + 'worklet'; + if (!VALID_ARRAY_VIEWS_NAMES.includes(typeName)) { + throw new ReanimatedError( + `[Reanimated] Invalid array view name \`${typeName}\`.` + ); + } + const constructor = global[typeName as keyof typeof global]; + if (constructor === undefined) { + throw new ReanimatedError( + `[Reanimated] Constructor for \`${typeName}\` not found.` + ); + } + return new constructor(buffer); + }, + }) as unknown as ShareableRef; + shareableMappingCache.set(value, handle); + + return handle; +} + +function inaccessibleObject(value: T): ShareableRef { + // This is reached for object types that are not of plain Object.prototype. + // We don't support such objects from being transferred as shareables to + // the UI runtime and hence we replace them with "inaccessible object" + // which is implemented as a Proxy object that throws on any attempt + // of accessing its fields. We argue that such objects can sometimes leak + // as attributes of objects being captured by worklets but should never + // be used on the UI runtime regardless. If they are being accessed, the user + // will get an appropriate error message. + const clone = makeShareableCloneRecursive(INACCESSIBLE_OBJECT as T); + shareableMappingCache.set(value, clone); + return clone; } const WORKLET_CODE_THRESHOLD = 255; function getWorkletCode(value: WorkletFunction) { - // @ts-ignore this is fine const code = value?.__initData?.code; if (!code) { return 'unknown'; @@ -329,7 +480,7 @@ function isRemoteFunction(value: { * the UI thread. If the user really wants some objects to be mutable they * should use shared values instead. */ -function freezeObjectIfDev(value: T) { +function freezeObjectInDev(value: T) { if (!__DEV__) { return; } diff --git a/packages/react-native-worklets/.eslintrc.js b/packages/react-native-worklets/.eslintrc.js new file mode 100644 index 00000000000..17799e75920 --- /dev/null +++ b/packages/react-native-worklets/.eslintrc.js @@ -0,0 +1,4 @@ +/** @type {import('eslint').ESLint.ConfigData} */ +module.exports = { + extends: ['../../.eslintrc.js'], +}; diff --git a/packages/react-native-worklets/.gitattributes b/packages/react-native-worklets/.gitattributes new file mode 100644 index 00000000000..e27f70fa490 --- /dev/null +++ b/packages/react-native-worklets/.gitattributes @@ -0,0 +1,3 @@ +*.pbxproj -text +# specific for windows script files +*.bat text eol=crlf diff --git a/packages/react-native-worklets/.gitignore b/packages/react-native-worklets/.gitignore new file mode 100644 index 00000000000..9a3d039c59d --- /dev/null +++ b/packages/react-native-worklets/.gitignore @@ -0,0 +1,62 @@ +# OSX +.DS_Store + +# VSCode +.vscode/ +jsconfig.json + +# Xcode +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Android/IJ +.classpath +.cxx +.gradle +.idea +.project +.settings +local.properties +android.iml + +# node.js +node_modules/ +npm-debug.log +yarn-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ +android/app/libs +android/keystores/debug.keystore + +# Yarn +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +# generated by bob +lib/ + +# React Native Codegen +apple/generated +android/generated diff --git a/packages/react-native-worklets/.watchmanconfig b/packages/react-native-worklets/.watchmanconfig new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/packages/react-native-worklets/.watchmanconfig @@ -0,0 +1 @@ +{} diff --git a/packages/react-native-worklets/LICENSE b/packages/react-native-worklets/LICENSE new file mode 100644 index 00000000000..6882e4794af --- /dev/null +++ b/packages/react-native-worklets/LICENSE @@ -0,0 +1,20 @@ +MIT License + +Copyright (c) 2024 nobody +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/react-native-worklets/RNWorklets.podspec b/packages/react-native-worklets/RNWorklets.podspec new file mode 100644 index 00000000000..9dcdc62ff15 --- /dev/null +++ b/packages/react-native-worklets/RNWorklets.podspec @@ -0,0 +1,64 @@ +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "package.json"))) +folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' + +Pod::Spec.new do |s| + s.name = "RNWorklets" + s.version = package["version"] + s.summary = package["description"] + s.homepage = "https://github.com/software-mansion/react-native-reanimated" + s.license = package["license"] + s.authors = { "author" => "author@domain.com" } + s.platforms = { :ios => min_ios_version_supported } + s.source = { :git => "https://github.com/software-mansion/react-native-reanimated.git", :tag => "#{s.version}" } + + s.source_files = "apple/*.{h,m,mm,cpp}" + + s.subspec "worklets" do |ss| + ss.source_files = "Common/cpp/worklets/**/*.{cpp,h}" + ss.header_dir = "worklets" + ss.header_mappings_dir = "Common/cpp/worklets" + + ss.subspec "apple" do |sss| + # Please be careful with the snakes. + # 🐍🐍🐍 + # Thank you for your understanding. + sss.source_files = "apple/worklets/**/*.{mm,h,m}" + sss.header_dir = "worklets" + sss.header_mappings_dir = "apple/worklets" + end + end + + # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. + # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79. + if respond_to?(:install_modules_dependencies, true) + install_modules_dependencies(s) + else + s.dependency "React-Core" + + # Don't install the dependencies when we run `pod install` in the old architecture. + if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then + s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" + s.pod_target_xcconfig = { + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", + "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", + "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" + } + s.dependency "React-Codegen" + s.dependency "RCT-Folly" + s.dependency "RCTRequired" + s.dependency "RCTTypeSafety" + s.dependency "ReactCommon/turbomodule/core" + end + end + + s.pod_target_xcconfig = { + "USE_HEADERMAP" => "YES", + "DEFINES_MODULE" => "YES", + "HEADER_SEARCH_PATHS" => '"$(PODS_TARGET_SRCROOT)/ReactCommon" "$(PODS_TARGET_SRCROOT)" "$(PODS_ROOT)/RCT-Folly" "$(PODS_ROOT)/boost" "$(PODS_ROOT)/boost-for-react-native" "$(PODS_ROOT)/DoubleConversion" "$(PODS_ROOT)/Headers/Private/React-Core" "$(PODS_ROOT)/Headers/Private/Yoga"', + "FRAMEWORK_SEARCH_PATHS" => '"${PODS_CONFIGURATION_BUILD_DIR}/React-hermes"', + "CLANG_CXX_LANGUAGE_STANDARD" => "c++17", + } + +end diff --git a/packages/react-native-worklets/android/CMakeLists.txt b/packages/react-native-worklets/android/CMakeLists.txt new file mode 100644 index 00000000000..c2e3256d072 --- /dev/null +++ b/packages/react-native-worklets/android/CMakeLists.txt @@ -0,0 +1,60 @@ +project(Worklets) +cmake_minimum_required(VERSION 3.8) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS + ON + CACHE INTERNAL "") + +set(CMAKE_CXX_STANDARD 20) + +# default CMAKE_CXX_FLAGS: "-g -DANDROID -fdata-sections -ffunction-sections +# -funwind-tables -fstack-protector-strong -no-canonical-prefixes +# -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-protector-all" +include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake") +add_compile_options(${folly_FLAGS}) + +string( + APPEND + CMAKE_CXX_FLAGS + " -DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION} -DREANIMATED_VERSION=${REANIMATED_VERSION} -DHERMES_ENABLE_DEBUGGER=${HERMES_ENABLE_DEBUGGER}" +) + +string( + APPEND + CMAKE_CXX_FLAGS + " -fexceptions -fno-omit-frame-pointer -frtti -fstack-protector-all -std=c++${CMAKE_CXX_STANDARD} -Wall -Werror" +) + +if(${IS_NEW_ARCHITECTURE_ENABLED}) + string(APPEND CMAKE_CXX_FLAGS " -DRCT_NEW_ARCH_ENABLED") +endif() + +if(${IS_REANIMATED_EXAMPLE_APP}) + string(APPEND CMAKE_CXX_FLAGS " -DIS_REANIMATED_EXAMPLE_APP -Wpedantic") +endif() + +if(NOT ${CMAKE_BUILD_TYPE} MATCHES "Debug") + string(APPEND CMAKE_CXX_FLAGS " -DNDEBUG") +endif() + +if(${JS_RUNTIME} STREQUAL "hermes") + string(APPEND CMAKE_CXX_FLAGS " -DJS_RUNTIME_HERMES=1") +elseif(${JS_RUNTIME} STREQUAL "jsc") + string(APPEND CMAKE_CXX_FLAGS " -DJS_RUNTIME_JSC=1") +elseif(${JS_RUNTIME} STREQUAL "v8") + string(APPEND CMAKE_CXX_FLAGS " -DJS_RUNTIME_V8=1") +else() + message(FATAL_ERROR "Unknown JS runtime ${JS_RUNTIME}.") +endif() + +# Resolves "CMake Warning: Manually-specified variables were not used by the +# project" when any of the following variables is not used in some build +# configuration. +set(ignoreMe "${JS_RUNTIME_DIR}") + +set(BUILD_DIR "${CMAKE_SOURCE_DIR}/build") +set(ANDROID_CPP_DIR "${CMAKE_SOURCE_DIR}/src/main/cpp") +set(COMMON_CPP_DIR "${CMAKE_SOURCE_DIR}/../Common/cpp") + +add_subdirectory("${ANDROID_CPP_DIR}/worklets") diff --git a/packages/react-native-worklets/android/build.gradle b/packages/react-native-worklets/android/build.gradle new file mode 100644 index 00000000000..4c532e21201 --- /dev/null +++ b/packages/react-native-worklets/android/build.gradle @@ -0,0 +1,438 @@ +import com.android.Version +import org.apache.tools.ant.filters.ReplaceTokens +import org.apache.tools.ant.taskdefs.condition.Os +import groovy.json.JsonSlurper +import com.android.build.gradle.tasks.ExternalNativeBuildJsonTask + +import javax.inject.Inject +import java.nio.file.Files +import java.nio.file.Paths + +/** + * Finds the path of the installed npm package with the given name using Node's + * module resolution algorithm, which searches "node_modules" directories up to + * the file system root. This handles various cases, including: + * + * - Working in the open-source RN repo: + * Gradle: /path/to/react-native/ReactAndroid + * Node module: /path/to/react-native/node_modules/[package] + * + * - Installing RN as a dependency of an app and searching for hoisted + * dependencies: + * Gradle: /path/to/app/node_modules/react-native/ReactAndroid + * Node module: /path/to/app/node_modules/[package] + * + * - Working in a larger repo (e.g., Facebook) that contains RN: + * Gradle: /path/to/repo/path/to/react-native/ReactAndroid + * Node module: /path/to/repo/node_modules/[package] + * + * The search begins at the given base directory (a File object). The returned + * path is a string. + */ +static def findNodeModulePath(baseDir, packageName) { + def basePath = baseDir.toPath().normalize() + // Node's module resolution algorithm searches up to the root directory, + // after which the base path will be null + while (basePath) { + def candidatePath = Paths.get(basePath.toString(), "node_modules", packageName) + if (candidatePath.toFile().exists()) { + return candidatePath.toString() + } + basePath = basePath.getParent() + } + return null +} + +def safeExtGet(prop, fallback) { + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback +} + +def safeAppExtGet(prop, fallback) { + def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') } + appProject?.ext?.has(prop) ? appProject.ext.get(prop) : fallback +} + +def resolveBuildType() { + Gradle gradle = getGradle() + String tskReqStr = gradle.getStartParameter().getTaskRequests()['args'].toString() + return tskReqStr.contains('Release') ? 'release' : 'debug' +} + +def isReanimatedExampleApp() { + return safeAppExtGet("isReanimatedExampleApp", false) +} + +def isNewArchitectureEnabled() { + // To opt-in for the New Architecture, you can either: + // - Set `newArchEnabled` to true inside the `gradle.properties` file + // - Invoke gradle with `-newArchEnabled=true` + // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true` + return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" +} + +def resolveReactNativeDirectory() { + def reactNativeLocation = safeAppExtGet("REACT_NATIVE_NODE_MODULES_DIR", null) + if (reactNativeLocation != null) { + return file(reactNativeLocation) + } + + // Fallback to node resolver for custom directory structures like monorepos. + def reactNativePackage = file(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()) + if(reactNativePackage.exists()) { + return reactNativePackage.parentFile + } + + throw new GradleException( + "[Reanimated] Unable to resolve react-native location in node_modules. You should project extension property (in `app/build.gradle`) `REACT_NATIVE_NODE_MODULES_DIR` with path to react-native." + ) +} + +def getPlaygroundAppName() { // only for the development + String playgroundAppName = "" + try { + rootProject.getSubprojects().forEach({project -> + if (project.plugins.hasPlugin("com.android.application")) { + var projectCatalogAbsolutePath = project.projectDir.toString().replace("/android/app", "") + var slashPosition = projectCatalogAbsolutePath.lastIndexOf("/") + playgroundAppName = projectCatalogAbsolutePath.substring(slashPosition + 1) + } + }) + } catch (_) { + throw new GradleException("[Reanimated] Couldn't determine playground app name.") + } + return playgroundAppName +} + +def toPlatformFileString(String path) { + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + path = path.replace(File.separatorChar, '/' as char) + } + return path +} + +if (isNewArchitectureEnabled()) { + apply plugin: "com.facebook.react" +} + +def reactNativeRootDir = resolveReactNativeDirectory() + +def reactProperties = new Properties() +file("$reactNativeRootDir/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) } + +def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME") +def REACT_NATIVE_MINOR_VERSION = REACT_NATIVE_VERSION.startsWith("0.0.0-") ? 1000 : REACT_NATIVE_VERSION.split("\\.")[1].toInteger() +def IS_NEW_ARCHITECTURE_ENABLED = isNewArchitectureEnabled() + +// We download various C++ open-source dependencies into downloads. +// We then copy both the downloaded code and our custom makefiles and headers into third-party-ndk. +// After that we build native code from src/main/jni with module path pointing at third-party-ndk. + +def customDownloadsDir = System.getenv("REACT_NATIVE_DOWNLOADS_DIR") +def downloadsDir = customDownloadsDir ? new File(customDownloadsDir) : new File("$buildDir/downloads") +def thirdPartyNdkDir = new File("$buildDir/third-party-ndk") + +def reactNativeThirdParty = new File("$reactNativeRootDir/ReactAndroid/src/main/jni/third-party") +def reactNativeAndroidDownloadDir = new File("$reactNativeRootDir/ReactAndroid/build/downloads") + +def workletsPrefabHeadersDir = project.file("$buildDir/prefab-headers/worklets") + +def JS_RUNTIME = { + // Override JS runtime with environment variable + if (System.getenv("JS_RUNTIME")) { + return System.getenv("JS_RUNTIME") + } + + // Enable V8 runtime if react-native-v8 is installed + def v8Project = rootProject.getSubprojects().find { project -> project.name == "react-native-v8" } + if (v8Project != null) { + return "v8" + } + + // Check if Hermes is enabled in app setup + def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') } + if (appProject?.hermesEnabled?.toBoolean() || appProject?.ext?.react?.enableHermes?.toBoolean()) { + return "hermes" + } + + // Use JavaScriptCore (JSC) by default + return "jsc" +}.call() + +def jsRuntimeDir = { + if (JS_RUNTIME == "hermes") { + return Paths.get(reactNativeRootDir.path, "sdks", "hermes") + } else if (JS_RUNTIME == "v8") { + return findProject(":react-native-v8").getProjectDir().getParent() + } else { + return Paths.get(reactNativeRootDir.path, "ReactCommon", "jsi") + } +}.call() + +def reactNativeArchitectures() { + def value = project.getProperties().get("reactNativeArchitectures") + return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] +} + +buildscript { + // Buildscript is evaluated before everything else so we can't use getExtOrDefault + def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["Worklets_kotlinVersion"] + + // repositories { + // google() + // mavenCentral() + // } + + repositories { + google() + mavenCentral() + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "com.android.tools.build:gradle:8.2.1" + classpath "de.undercouch:gradle-download-task:5.6.0" + classpath "com.diffplug.spotless:spotless-plugin-gradle:6.25.0" + // noinspection DifferentKotlinGradleVersion + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +if (project == rootProject) { + apply from: "spotless.gradle" +} + +apply plugin: "com.android.library" +apply plugin: "maven-publish" +apply plugin: "de.undercouch.download" + + +android { + compileSdkVersion safeExtGet("compileSdkVersion", 34) + + def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION + if (agpVersion.tokenize('.')[0].toInteger() >= 7) { + namespace "com.swmansion.worklets" + } + + if (rootProject.hasProperty("ndkPath")) { + ndkPath rootProject.ext.ndkPath + } + if (rootProject.hasProperty("ndkVersion")) { + ndkVersion rootProject.ext.ndkVersion + } + + buildFeatures { + prefab true + prefabPublishing true + buildConfig true + } + + prefab { + worklets { + headers workletsPrefabHeadersDir.absolutePath + } + } + + defaultConfig { + minSdkVersion safeExtGet("minSdkVersion", 23) + targetSdkVersion safeExtGet("targetSdkVersion", 34) + versionCode 1 + versionName "1.0" + buildConfigField("boolean", "IS_NEW_ARCHITECTURE_ENABLED", IS_NEW_ARCHITECTURE_ENABLED.toString()) + externalNativeBuild { + cmake { + arguments "-DANDROID_STL=c++_shared", + "-DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}", + "-DANDROID_TOOLCHAIN=clang", + "-DREACT_NATIVE_DIR=${toPlatformFileString(reactNativeRootDir.path)}", + "-DJS_RUNTIME=${JS_RUNTIME}", + "-DJS_RUNTIME_DIR=${jsRuntimeDir}", + "-DIS_NEW_ARCHITECTURE_ENABLED=${IS_NEW_ARCHITECTURE_ENABLED}", + "-DIS_REANIMATED_EXAMPLE_APP=${isReanimatedExampleApp()}" + abiFilters (*reactNativeArchitectures()) + } + } + + buildConfigField("boolean", "IS_INTERNAL_BUILD", "false") + buildConfigField("int", "EXOPACKAGE_FLAGS", "0") + buildConfigField("int", "REACT_NATIVE_MINOR_VERSION", REACT_NATIVE_MINOR_VERSION.toString()) + + consumerProguardFiles 'proguard-rules.pro' + } + externalNativeBuild { + cmake { + version = System.getenv("CMAKE_VERSION") ?: "3.22.1" + path "CMakeLists.txt" + } + } + buildTypes { + debug { + externalNativeBuild { + cmake { + if (JS_RUNTIME == "hermes") { + arguments "-DHERMES_ENABLE_DEBUGGER=1" + } else { + arguments "-DHERMES_ENABLE_DEBUGGER=0" + } + } + } + } + release { + externalNativeBuild { + cmake { + arguments "-DHERMES_ENABLE_DEBUGGER=0" + } + } + } + } + lintOptions { + abortOnError false + } + packagingOptions { + doNotStrip resolveBuildType() == 'debug' ? "**/**/*.so" : '' + // For some reason gradle only complains about the duplicated version of librrc_root and libreact_render libraries + // while there are more libraries copied in intermediates folder of the lib build directory, we exclude + // only the ones that make the build fail (ideally we should only include libreanimated but we + // are only allowed to specify exclude patterns) + excludes = [ + "META-INF", + "META-INF/**", + "**/libc++_shared.so", + "**/libfbjni.so", + "**/libjsi.so", + "**/libfolly_json.so", + "**/libfolly_runtime.so", + "**/libglog.so", + "**/libhermes.so", + "**/libhermes-executor-debug.so", + "**/libhermes_executor.so", + "**/libhermestooling.so", + "**/libreactnativejni.so", + "**/libturbomodulejsijni.so", + "**/libreactnative.so", + "**/libreact_nativemodule_core.so", + "**/libreact_render*.so", + "**/librrc_root.so", + "**/libjscexecutor.so", + "**/libv8executor.so", + ] + } + tasks.withType(JavaCompile) { + compileTask -> + compileTask.dependsOn(packageNdkLibs) + } + configurations { + extractHeaders + extractSO + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +def assertMinimalReactNativeVersion = task assertMinimalReactNativeVersionTask { + // If you change the minimal React Native version remember to update Compatibility Table in docs + def minimalReactNativeVersion = 74 + onlyIf { REACT_NATIVE_MINOR_VERSION < minimalReactNativeVersion } + doFirst { + throw new GradleException("[Reanimated] Unsupported React Native version. Please use $minimalReactNativeVersion. or newer.") + } +} + +task prepareWorkletsHeadersForPrefabs(type: Copy) { + from("$projectDir/src/main/cpp") + from("$projectDir/../Common/cpp") + include("worklets/**/*.h") + into(workletsPrefabHeadersDir) +} + +tasks.preBuild { + dependsOn assertMinimalReactNativeVersion +} + +task cleanCmakeCache() { + tasks.getByName("clean").dependsOn(cleanCmakeCache) + doFirst { + delete "${projectDir}/.cxx" + } +} + +task printVersions { + println "Android gradle plugin: ${Version.ANDROID_GRADLE_PLUGIN_VERSION}" + println "Gradle: ${project.gradle.gradleVersion}" +} + +task createNativeDepsDirectories() { + downloadsDir.mkdirs() + thirdPartyNdkDir.mkdirs() + workletsPrefabHeadersDir.mkdirs() +} + +task packageNdkLibs(type: Copy) { + from("$buildDir/worklets-ndk/all") + include("**/libworklets.so") + into("$projectDir/src/main/jniLibs") +} + +repositories { + mavenCentral() + mavenLocal() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$reactNativeRootDir/android" + } + maven { + // Android JSC is installed from npm + url "$reactNativeRootDir/../jsc-android/dist" + } + google() +} + +dependencies { + implementation "com.facebook.yoga:proguard-annotations:1.19.0" + implementation "androidx.transition:transition:1.1.0" + implementation "androidx.core:core:1.6.0" + + implementation "com.facebook.react:react-android" // version substituted by RNGP + if (JS_RUNTIME == "hermes") { + implementation "com.facebook.react:hermes-android" // version substituted by RNGP + } +} + +def nativeBuildDependsOn(dependsOnTask) { + def buildTasks = tasks.findAll({ task -> ( + !task.name.contains("Clean") + && (task.name.contains("externalNative") + || task.name.contains("CMake") + || task.name.contains("generateJsonModel") + ) + ) }) + buildTasks.forEach { task -> task.dependsOn(dependsOnTask) } +} + +afterEvaluate { + preBuild.dependsOn(prepareWorkletsHeadersForPrefabs) + + tasks.forEach({ task -> + if (task.name.contains("JniLibFolders")) { + task.dependsOn(packageNdkLibs) + } + }) + + if (JS_RUNTIME == "hermes") { + // Do nothing + } else if (JS_RUNTIME == "v8") { + def buildTasks = tasks.findAll({ task -> + !task.name.contains("Clean") && (task.name.contains("externalNative") || task.name.contains("CMake") || task.name.startsWith("generateJsonModel")) }) + buildTasks.forEach { task -> + def buildType = task.name.endsWith('Debug') ? 'Debug' : 'Release' + task.dependsOn(":react-native-v8:copy${buildType}JniLibsProjectOnly") + } + } else if (JS_RUNTIME == "jsc") { + // Do nothing + } else { + throw GradleScriptException("[Reanimated] Unknown JS runtime ${JS_RUNTIME}.") + } +} diff --git a/packages/react-native-worklets/android/gradle.properties b/packages/react-native-worklets/android/gradle.properties new file mode 100644 index 00000000000..2cf6e18a6f8 --- /dev/null +++ b/packages/react-native-worklets/android/gradle.properties @@ -0,0 +1,5 @@ +Worklets_kotlinVersion=1.7.0 +Worklets_minSdkVersion=21 +Worklets_targetSdkVersion=31 +Worklets_compileSdkVersion=31 +Worklets_ndkversion=21.4.7075529 diff --git a/packages/react-native-worklets/android/src/main/AndroidManifest.xml b/packages/react-native-worklets/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..a2f47b6057d --- /dev/null +++ b/packages/react-native-worklets/android/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/packages/react-native-worklets/android/src/main/cpp/worklets/CMakeLists.txt b/packages/react-native-worklets/android/src/main/cpp/worklets/CMakeLists.txt new file mode 100644 index 00000000000..63218fb48ae --- /dev/null +++ b/packages/react-native-worklets/android/src/main/cpp/worklets/CMakeLists.txt @@ -0,0 +1,88 @@ +cmake_minimum_required(VERSION 3.8) + +file(GLOB_RECURSE WORKLETS_COMMON_CPP_SOURCES CONFIGURE_DEPENDS + "${COMMON_CPP_DIR}/worklets/*.cpp") +file(GLOB_RECURSE WORKLETS_ANDROID_CPP_SOURCES CONFIGURE_DEPENDS + "${ANDROID_CPP_DIR}/worklets/*.cpp") + +# Consume shared libraries and headers from prefabs +find_package(fbjni REQUIRED CONFIG) +find_package(ReactAndroid REQUIRED CONFIG) + +if(${JS_RUNTIME} STREQUAL "hermes") + find_package(hermes-engine REQUIRED CONFIG) +endif() + +add_library(worklets SHARED ${WORKLETS_COMMON_CPP_SOURCES} + ${WORKLETS_ANDROID_CPP_SOURCES}) + +# includes +target_include_directories(worklets PUBLIC "${COMMON_CPP_DIR}" + "${ANDROID_CPP_DIR}") + +target_include_directories( + worklets + PRIVATE "${REACT_NATIVE_DIR}/ReactCommon" + "${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/react/turbomodule" + "${REACT_NATIVE_DIR}/ReactCommon/react/nativemodule/core/ReactCommon" + "${REACT_NATIVE_DIR}/ReactCommon/callinvoker" + "${REACT_NATIVE_DIR}/ReactCommon/runtimeexecutor") + +if(${IS_NEW_ARCHITECTURE_ENABLED}) + target_include_directories( + worklets + PRIVATE + "${REACT_NATIVE_DIR}/ReactCommon/yoga" + "${REACT_NATIVE_DIR}/ReactCommon/react/renderer/graphics/platform/cxx") + + if(ReactAndroid_VERSION_MINOR LESS 76) + target_link_libraries( + worklets ReactAndroid::fabricjni ReactAndroid::react_debug + ReactAndroid::react_render_core + ReactAndroid::react_render_componentregistry ReactAndroid::rrc_view) + endif() +endif() + +# build shared lib +set_target_properties(worklets PROPERTIES LINKER_LANGUAGE CXX) + +target_link_libraries(worklets log ReactAndroid::jsi fbjni::fbjni) + +if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76) + target_link_libraries(worklets ReactAndroid::reactnative) +else() + target_link_libraries( + worklets ReactAndroid::react_nativemodule_core ReactAndroid::folly_runtime + ReactAndroid::glog ReactAndroid::reactnativejni) +endif() + +if(${JS_RUNTIME} STREQUAL "hermes") + target_link_libraries(worklets hermes-engine::libhermes) + + if(${HERMES_ENABLE_DEBUGGER}) + if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76) + target_link_libraries(worklets ReactAndroid::hermestooling) + else() + target_link_libraries(worklets ReactAndroid::hermes_executor) + endif() + endif() +elseif(${JS_RUNTIME} STREQUAL "jsc") + if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76) + target_link_libraries(worklets ReactAndroid::jsctooling) + else() + target_link_libraries(worklets ReactAndroid::jscexecutor) + endif() +elseif(${JS_RUNTIME} STREQUAL "v8") # TODO: Refactor this when adding support + # for newest V8 + target_include_directories(worklets PRIVATE "${JS_RUNTIME_DIR}/src") + file( + GLOB + V8_SO_DIR + "${JS_RUNTIME_DIR}/android/build/intermediates/library_jni/*/jni/${ANDROID_ABI}" + ) + find_library( + V8EXECUTOR_LIB v8executor + PATHS ${V8_SO_DIR} + NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + target_link_libraries(worklets ${V8EXECUTOR_LIB}) +endif() diff --git a/packages/react-native-worklets/android/src/main/java/com/swmansion/worklets/DummyWorkletsModule.java b/packages/react-native-worklets/android/src/main/java/com/swmansion/worklets/DummyWorkletsModule.java new file mode 100644 index 00000000000..c5cfd25e155 --- /dev/null +++ b/packages/react-native-worklets/android/src/main/java/com/swmansion/worklets/DummyWorkletsModule.java @@ -0,0 +1,18 @@ +package com.swmansion.worklets; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.module.annotations.ReactModule; + +@ReactModule(name = DummyWorkletsModule.NAME) +public class DummyWorkletsModule extends + NativeSulphateWorkletsSpec { + + public DummyWorkletsModule(ReactApplicationContext reactContext) { + super(reactContext); + } + + @Override + public boolean installTurboModule() { + return true; + } +} diff --git a/packages/react-native-worklets/android/src/main/java/com/swmansion/worklets/WorkletsPackage.java b/packages/react-native-worklets/android/src/main/java/com/swmansion/worklets/WorkletsPackage.java new file mode 100644 index 00000000000..23066283cbe --- /dev/null +++ b/packages/react-native-worklets/android/src/main/java/com/swmansion/worklets/WorkletsPackage.java @@ -0,0 +1,56 @@ +package com.swmansion.worklets; + +import androidx.annotation.NonNull; + +import com.facebook.react.BaseReactPackage; +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.module.annotations.ReactModuleList; +import com.facebook.react.module.model.ReactModuleInfo; +import com.facebook.react.module.model.ReactModuleInfoProvider; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@ReactModuleList( + nativeModules = { + DummyWorkletsModule.class + }) +public class WorkletsPackage extends BaseReactPackage implements ReactPackage { + @Override + public NativeModule getModule( + @NonNull String name, @NonNull ReactApplicationContext reactContext) { + return switch (name) { + case DummyWorkletsModule.NAME -> new DummyWorkletsModule(reactContext); + default -> null; + }; + } + + @Override + public ReactModuleInfoProvider getReactModuleInfoProvider() { + Class[] moduleList = + new Class[] { + DummyWorkletsModule.class + }; + + final Map reactModuleInfoMap = new HashMap<>(); + for (Class moduleClass : moduleList) { + ReactModule reactModule = + Objects.requireNonNull(moduleClass.getAnnotation(ReactModule.class)); + + reactModuleInfoMap.put( + reactModule.name(), + new ReactModuleInfo( + reactModule.name(), + moduleClass.getName(), + true, + reactModule.needsEagerInit(), + reactModule.isCxxModule(), + BuildConfig.IS_NEW_ARCHITECTURE_ENABLED)); + } + + return () -> reactModuleInfoMap; + } +} diff --git a/packages/react-native-worklets/apple/worklets/apple/DummyWorkletsModule.h b/packages/react-native-worklets/apple/worklets/apple/DummyWorkletsModule.h new file mode 100644 index 00000000000..c2a6b079ec7 --- /dev/null +++ b/packages/react-native-worklets/apple/worklets/apple/DummyWorkletsModule.h @@ -0,0 +1,5 @@ +#import + +@interface DummyWorkletsModule : NSObject + +@end diff --git a/packages/react-native-worklets/apple/worklets/apple/DummyWorkletsModule.mm b/packages/react-native-worklets/apple/worklets/apple/DummyWorkletsModule.mm new file mode 100644 index 00000000000..2ba55e07e77 --- /dev/null +++ b/packages/react-native-worklets/apple/worklets/apple/DummyWorkletsModule.mm @@ -0,0 +1,19 @@ +#import + +@implementation DummyWorkletsModule{ + std::string valueUnpackerCode_; +} +RCT_EXPORT_MODULE() + +- (std::shared_ptr)getTurboModule : + (const facebook::react::ObjCTurboModule::InitParams &)params +{ + return std::make_shared(params); +} + +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(installTurboModule) +{ + return @YES; +} + +@end diff --git a/packages/react-native-worklets/babel.config.js b/packages/react-native-worklets/babel.config.js new file mode 100644 index 00000000000..5d51f258afc --- /dev/null +++ b/packages/react-native-worklets/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['module:react-native-builder-bob/babel-preset'], +}; diff --git a/packages/react-native-worklets/package.json b/packages/react-native-worklets/package.json new file mode 100644 index 00000000000..fb53bca32bd --- /dev/null +++ b/packages/react-native-worklets/package.json @@ -0,0 +1,82 @@ +{ + "name": "react-native-worklets", + "version": "0.0.1", + "description": "🚀", + "scripts": { + "build": "bob build" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/software-mansion/react-native-reanimated.git", + "directory": "packages/react-native-worklets" + }, + "license": "MIT", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/software-mansion/react-native-reanimated/issues" + }, + "homepage": "https://docs.swmansion.com/react-native-reanimated", + "peerDependencies": { + "@babel/core": "^7.0.0-0", + "react": "*", + "react-native": "*" + }, + "devDependencies": { + "@react-native-community/cli": "15.0.1", + "@react-native/eslint-config": "^0.73.1", + "@types/jest": "^29.5.5", + "@types/react": "^18.2.44", + "eslint": "^8.57.0", + "prettier": "^3.3.3", + "react": "18.3.1", + "react-native": "^0.76.1", + "react-native-builder-bob": "0.33.1", + "typescript": "~5.3.0" + }, + "main": "./lib/module/index", + "module": "./lib/module/index", + "react-native": "./src/index", + "source": "./src/index", + "types": "lib/typescript/index.d.ts", + "files": [ + "src", + "lib", + "android", + "apple", + "Common", + "*.podspec", + "react-native.config.js", + "!apple/build", + "!android/build", + "!android/gradle", + "!android/gradlew", + "!android/gradlew.bat", + "!android/local.properties", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__", + "!**/.*" + ], + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + [ + "module", + { + "esm": true, + "jsxRuntime": "classic" + } + ], + "typescript" + ] + }, + "codegenConfig": { + "name": "rnworklets", + "type": "modules", + "jsSrcsDir": "src/specs", + "android": { + "javaPackageName": "com.swmansion.worklets" + } + } +} diff --git a/packages/react-native-worklets/src/index.ts b/packages/react-native-worklets/src/index.ts new file mode 100644 index 00000000000..ba2a736031d --- /dev/null +++ b/packages/react-native-worklets/src/index.ts @@ -0,0 +1,3 @@ +'use strict'; + +export { DummyWorkletsTurboModule } from './specs'; diff --git a/packages/react-native-worklets/src/specs/NativeDummyWorklets.ts b/packages/react-native-worklets/src/specs/NativeDummyWorklets.ts new file mode 100644 index 00000000000..f59202c88c0 --- /dev/null +++ b/packages/react-native-worklets/src/specs/NativeDummyWorklets.ts @@ -0,0 +1,9 @@ +'use strict'; +import type { TurboModule } from 'react-native'; +import { TurboModuleRegistry } from 'react-native'; + +interface Spec extends TurboModule { + installTurboModule: () => boolean; +} + +export default TurboModuleRegistry.get('DummyWorklets'); diff --git a/packages/react-native-worklets/src/specs/index.ts b/packages/react-native-worklets/src/specs/index.ts new file mode 100644 index 00000000000..5b294afa510 --- /dev/null +++ b/packages/react-native-worklets/src/specs/index.ts @@ -0,0 +1,5 @@ +'use strict'; + +import DummyWorkletsTurboModule from './NativeDummyWorklets'; + +export { DummyWorkletsTurboModule }; diff --git a/packages/react-native-worklets/tsconfig.json b/packages/react-native-worklets/tsconfig.json new file mode 100644 index 00000000000..fd4ac617a0c --- /dev/null +++ b/packages/react-native-worklets/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "paths": { + "react-native-worklets": ["./src"] + } + }, + "include": ["src"] +} diff --git a/yarn.lock b/yarn.lock index 7736167a103..8fcb6ae7d27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3009,6 +3009,13 @@ __metadata: languageName: node linkType: hard +"@eslint-community/regexpp@npm:^4.4.0": + version: 4.12.1 + resolution: "@eslint-community/regexpp@npm:4.12.1" + checksum: 10/c08f1dd7dd18fbb60bdd0d85820656d1374dd898af9be7f82cb00451313402a22d5e30569c150315b4385907cdbca78c22389b2a72ab78883b3173be317620cc + languageName: node + linkType: hard + "@eslint/eslintrc@npm:^2.1.4": version: 2.1.4 resolution: "@eslint/eslintrc@npm:2.1.4" @@ -4460,6 +4467,30 @@ __metadata: languageName: node linkType: hard +"@react-native-community/cli-clean@npm:15.0.1": + version: 15.0.1 + resolution: "@react-native-community/cli-clean@npm:15.0.1" + dependencies: + "@react-native-community/cli-tools": "npm:15.0.1" + chalk: "npm:^4.1.2" + execa: "npm:^5.0.0" + fast-glob: "npm:^3.3.2" + checksum: 10/a1ff1824a4c1290271aaef48af0bc30ed50503ac062341fb051bb07895890414f4e208eb949b05bce79f90d1a51dd2fb133f9fbcf6c019bb7fec8c03ec4e4419 + languageName: node + linkType: hard + +"@react-native-community/cli-config-apple@npm:15.0.1": + version: 15.0.1 + resolution: "@react-native-community/cli-config-apple@npm:15.0.1" + dependencies: + "@react-native-community/cli-tools": "npm:15.0.1" + chalk: "npm:^4.1.2" + execa: "npm:^5.0.0" + fast-glob: "npm:^3.3.2" + checksum: 10/3766bb155962d3ad4908b77db6d68e6edb6c8b4130e78c30e8cea3b1e6535e9730dc0bdc7b995b86d145621f4e992e4a2dcaf2be34b045de13d0c68692fd78ed + languageName: node + linkType: hard + "@react-native-community/cli-config@npm:14.0.0": version: 14.0.0 resolution: "@react-native-community/cli-config@npm:14.0.0" @@ -4502,6 +4533,20 @@ __metadata: languageName: node linkType: hard +"@react-native-community/cli-config@npm:15.0.1": + version: 15.0.1 + resolution: "@react-native-community/cli-config@npm:15.0.1" + dependencies: + "@react-native-community/cli-tools": "npm:15.0.1" + chalk: "npm:^4.1.2" + cosmiconfig: "npm:^9.0.0" + deepmerge: "npm:^4.3.0" + fast-glob: "npm:^3.3.2" + joi: "npm:^17.2.1" + checksum: 10/7fc4d4f3554ddc6d76534f3063baf8f3631c333a2445038a617c1c5630718bc775ca35fe59a7ceafd40bc1f316d1c4fae00693ea22feb367fdaf91104ead3efe + languageName: node + linkType: hard + "@react-native-community/cli-debugger-ui@npm:14.0.0": version: 14.0.0 resolution: "@react-native-community/cli-debugger-ui@npm:14.0.0" @@ -4538,6 +4583,15 @@ __metadata: languageName: node linkType: hard +"@react-native-community/cli-debugger-ui@npm:15.0.1": + version: 15.0.1 + resolution: "@react-native-community/cli-debugger-ui@npm:15.0.1" + dependencies: + serve-static: "npm:^1.13.1" + checksum: 10/366b87f67c72455a61de4beb05d3b24c5e8f6a4ea4d0d11a7bb660e738f43a26daa9721527a36f8e9596586240875c1a35d5b87a8cdddc2d84b38b6b56a83902 + languageName: node + linkType: hard + "@react-native-community/cli-doctor@npm:14.0.0": version: 14.0.0 resolution: "@react-native-community/cli-doctor@npm:14.0.0" @@ -4610,6 +4664,30 @@ __metadata: languageName: node linkType: hard +"@react-native-community/cli-doctor@npm:15.0.1": + version: 15.0.1 + resolution: "@react-native-community/cli-doctor@npm:15.0.1" + dependencies: + "@react-native-community/cli-config": "npm:15.0.1" + "@react-native-community/cli-platform-android": "npm:15.0.1" + "@react-native-community/cli-platform-apple": "npm:15.0.1" + "@react-native-community/cli-platform-ios": "npm:15.0.1" + "@react-native-community/cli-tools": "npm:15.0.1" + chalk: "npm:^4.1.2" + command-exists: "npm:^1.2.8" + deepmerge: "npm:^4.3.0" + envinfo: "npm:^7.13.0" + execa: "npm:^5.0.0" + node-stream-zip: "npm:^1.9.1" + ora: "npm:^5.4.1" + semver: "npm:^7.5.2" + strip-ansi: "npm:^5.2.0" + wcwidth: "npm:^1.0.1" + yaml: "npm:^2.2.1" + checksum: 10/89cb3d2c6610c2316e388adf5efe7f85600d56e2fdc68b3f9fa8842ced44bf7ae89a7d4b733e21bb99f5d31c86112208a12f9a6668f6c99385be4ab415f730ca + languageName: node + linkType: hard + "@react-native-community/cli-platform-android@npm:14.0.0": version: 14.0.0 resolution: "@react-native-community/cli-platform-android@npm:14.0.0" @@ -4652,6 +4730,20 @@ __metadata: languageName: node linkType: hard +"@react-native-community/cli-platform-android@npm:15.0.1": + version: 15.0.1 + resolution: "@react-native-community/cli-platform-android@npm:15.0.1" + dependencies: + "@react-native-community/cli-tools": "npm:15.0.1" + chalk: "npm:^4.1.2" + execa: "npm:^5.0.0" + fast-glob: "npm:^3.3.2" + fast-xml-parser: "npm:^4.4.1" + logkitty: "npm:^0.7.1" + checksum: 10/f537ac01ccce117b29020094165e123e84e5baea1bb4d88c1c1db7a5723060871dafab3b7d12f51aa14f94d13d30b59fff052244b14b8a4927b7e16f7e6f0308 + languageName: node + linkType: hard + "@react-native-community/cli-platform-apple@npm:14.0.0": version: 14.0.0 resolution: "@react-native-community/cli-platform-apple@npm:14.0.0" @@ -4694,6 +4786,19 @@ __metadata: languageName: node linkType: hard +"@react-native-community/cli-platform-apple@npm:15.0.1": + version: 15.0.1 + resolution: "@react-native-community/cli-platform-apple@npm:15.0.1" + dependencies: + "@react-native-community/cli-config-apple": "npm:15.0.1" + "@react-native-community/cli-tools": "npm:15.0.1" + chalk: "npm:^4.1.2" + execa: "npm:^5.0.0" + fast-xml-parser: "npm:^4.4.1" + checksum: 10/85a6d39eba5cd474a063d59a12e893ffe77656b522a7adcac9c092f35565fddcf16c2b50c09b63a8d5c7d71f5fbad33069f1a61f23ca3b8f5dd5e4cb284d9bf9 + languageName: node + linkType: hard + "@react-native-community/cli-platform-ios@npm:14.0.0": version: 14.0.0 resolution: "@react-native-community/cli-platform-ios@npm:14.0.0" @@ -4721,6 +4826,15 @@ __metadata: languageName: node linkType: hard +"@react-native-community/cli-platform-ios@npm:15.0.1": + version: 15.0.1 + resolution: "@react-native-community/cli-platform-ios@npm:15.0.1" + dependencies: + "@react-native-community/cli-platform-apple": "npm:15.0.1" + checksum: 10/17844caec8ed5e4101e35fa42fa12028a99c545f0d86a028b5ed12c19072cd8eeeacf11598a271fef5eea028072dd0521545376f3c50522ac6ca606d841f359d + languageName: node + linkType: hard + "@react-native-community/cli-server-api@npm:14.0.0": version: 14.0.0 resolution: "@react-native-community/cli-server-api@npm:14.0.0" @@ -4789,6 +4903,23 @@ __metadata: languageName: node linkType: hard +"@react-native-community/cli-server-api@npm:15.0.1": + version: 15.0.1 + resolution: "@react-native-community/cli-server-api@npm:15.0.1" + dependencies: + "@react-native-community/cli-debugger-ui": "npm:15.0.1" + "@react-native-community/cli-tools": "npm:15.0.1" + compression: "npm:^1.7.1" + connect: "npm:^3.6.5" + errorhandler: "npm:^1.5.1" + nocache: "npm:^3.0.1" + pretty-format: "npm:^26.6.2" + serve-static: "npm:^1.13.1" + ws: "npm:^6.2.3" + checksum: 10/22341610387537e5603cb7b6f1d8b761b5439174bbac650081cf5b40377c0108262320e282329f977bef826e6c4569fbaa3e85f2a697631e755a020216a5515a + languageName: node + linkType: hard + "@react-native-community/cli-tools@npm:14.0.0": version: 14.0.0 resolution: "@react-native-community/cli-tools@npm:14.0.0" @@ -4862,6 +4993,25 @@ __metadata: languageName: node linkType: hard +"@react-native-community/cli-tools@npm:15.0.1": + version: 15.0.1 + resolution: "@react-native-community/cli-tools@npm:15.0.1" + dependencies: + appdirsjs: "npm:^1.2.4" + chalk: "npm:^4.1.2" + execa: "npm:^5.0.0" + find-up: "npm:^5.0.0" + mime: "npm:^2.4.1" + open: "npm:^6.2.0" + ora: "npm:^5.4.1" + prompts: "npm:^2.4.2" + semver: "npm:^7.5.2" + shell-quote: "npm:^1.7.3" + sudo-prompt: "npm:^9.0.0" + checksum: 10/3447257d1650104466b7d59846ddcd45d8432b18d18df71c0606ecfed7892014fa959b917ab435c822b305a9a890bd51e762e941137e29f7824e215beacb42a5 + languageName: node + linkType: hard + "@react-native-community/cli-types@npm:14.0.0": version: 14.0.0 resolution: "@react-native-community/cli-types@npm:14.0.0" @@ -4889,6 +5039,15 @@ __metadata: languageName: node linkType: hard +"@react-native-community/cli-types@npm:15.0.1": + version: 15.0.1 + resolution: "@react-native-community/cli-types@npm:15.0.1" + dependencies: + joi: "npm:^17.2.1" + checksum: 10/77452486158afcf1f03a3596135b6dba16dba5dd10209dacd5a6a4b176df36d37b8e49af61590d5a64df4907cf0575b6f37e0a3893335f961a9380edaee32152 + languageName: node + linkType: hard + "@react-native-community/cli@npm:14.0.0": version: 14.0.0 resolution: "@react-native-community/cli@npm:14.0.0" @@ -4967,6 +5126,32 @@ __metadata: languageName: node linkType: hard +"@react-native-community/cli@npm:15.0.1": + version: 15.0.1 + resolution: "@react-native-community/cli@npm:15.0.1" + dependencies: + "@react-native-community/cli-clean": "npm:15.0.1" + "@react-native-community/cli-config": "npm:15.0.1" + "@react-native-community/cli-debugger-ui": "npm:15.0.1" + "@react-native-community/cli-doctor": "npm:15.0.1" + "@react-native-community/cli-server-api": "npm:15.0.1" + "@react-native-community/cli-tools": "npm:15.0.1" + "@react-native-community/cli-types": "npm:15.0.1" + chalk: "npm:^4.1.2" + commander: "npm:^9.4.1" + deepmerge: "npm:^4.3.0" + execa: "npm:^5.0.0" + find-up: "npm:^5.0.0" + fs-extra: "npm:^8.1.0" + graceful-fs: "npm:^4.1.3" + prompts: "npm:^2.4.2" + semver: "npm:^7.5.2" + bin: + rnc-cli: build/bin.js + checksum: 10/7673d01bded6e9a368b238031ce237cebcfba230d860804a8f19aa6b4d5adcf4432e0a3b71ea285650c69b6427310f7db152cee6c2152d3303adb8dee6f60923 + languageName: node + linkType: hard + "@react-native-community/slider@npm:^4.5.5": version: 4.5.5 resolution: "@react-native-community/slider@npm:4.5.5" @@ -5646,6 +5831,37 @@ __metadata: languageName: node linkType: hard +"@react-native/eslint-config@npm:^0.73.1": + version: 0.73.2 + resolution: "@react-native/eslint-config@npm:0.73.2" + dependencies: + "@babel/core": "npm:^7.20.0" + "@babel/eslint-parser": "npm:^7.20.0" + "@react-native/eslint-plugin": "npm:0.73.1" + "@typescript-eslint/eslint-plugin": "npm:^5.57.1" + "@typescript-eslint/parser": "npm:^5.57.1" + eslint-config-prettier: "npm:^8.5.0" + eslint-plugin-eslint-comments: "npm:^3.2.0" + eslint-plugin-ft-flow: "npm:^2.0.1" + eslint-plugin-jest: "npm:^26.5.3" + eslint-plugin-prettier: "npm:^4.2.1" + eslint-plugin-react: "npm:^7.30.1" + eslint-plugin-react-hooks: "npm:^4.6.0" + eslint-plugin-react-native: "npm:^4.0.0" + peerDependencies: + eslint: ">=8" + prettier: ">=2" + checksum: 10/01026d001a13df218f9958308a530a02cb57b483202e47ac348d89a2dc1030beb7c1fd6fb01ad10905f8c559fbc64f4c518086fcc18177bbe781c57e491e6f9e + languageName: node + linkType: hard + +"@react-native/eslint-plugin@npm:0.73.1": + version: 0.73.1 + resolution: "@react-native/eslint-plugin@npm:0.73.1" + checksum: 10/59ee5a5a7e17f5d07053d05bdf464d77e3455dcaf7fc65a5784bb085057b098b5dfde9f736464c78b053148ef82feedb01036525680b9dd1049e44ced69b9b59 + languageName: node + linkType: hard + "@react-native/eslint-plugin@npm:0.75.4": version: 0.75.4 resolution: "@react-native/eslint-plugin@npm:0.75.4" @@ -6374,6 +6590,16 @@ __metadata: languageName: node linkType: hard +"@types/jest@npm:^29.5.5": + version: 29.5.14 + resolution: "@types/jest@npm:29.5.14" + dependencies: + expect: "npm:^29.0.0" + pretty-format: "npm:^29.0.0" + checksum: 10/59ec7a9c4688aae8ee529316c43853468b6034f453d08a2e1064b281af9c81234cec986be796288f1bbb29efe943bc950e70c8fa8faae1e460d50e3cf9760f9b + languageName: node + linkType: hard + "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.9": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" @@ -6493,6 +6719,16 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:^18.2.44": + version: 18.3.13 + resolution: "@types/react@npm:18.3.13" + dependencies: + "@types/prop-types": "npm:*" + csstype: "npm:^3.0.2" + checksum: 10/4f2b851cabdb68430b50998ad18f206edaea5b3c651d92e3573d86b7ac740224a91b7ca63176fa70e1defc78459faf518e5d2f58e1ba2839b1f12365dab70de2 + languageName: node + linkType: hard + "@types/scheduler@npm:^0.16": version: 0.16.8 resolution: "@types/scheduler@npm:0.16.8" @@ -6578,6 +6814,30 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/eslint-plugin@npm:^5.57.1": + version: 5.62.0 + resolution: "@typescript-eslint/eslint-plugin@npm:5.62.0" + dependencies: + "@eslint-community/regexpp": "npm:^4.4.0" + "@typescript-eslint/scope-manager": "npm:5.62.0" + "@typescript-eslint/type-utils": "npm:5.62.0" + "@typescript-eslint/utils": "npm:5.62.0" + debug: "npm:^4.3.4" + graphemer: "npm:^1.4.0" + ignore: "npm:^5.2.0" + natural-compare-lite: "npm:^1.4.0" + semver: "npm:^7.3.7" + tsutils: "npm:^3.21.0" + peerDependencies: + "@typescript-eslint/parser": ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/9cc8319c6fd8a21938f5b69476974a7e778c283a55ef9fad183c850995b9adcb0087d57cea7b2ac6b9449570eee983aad39491d14cdd2e52d6b4b0485e7b2482 + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:^6.19.0": version: 6.21.0 resolution: "@typescript-eslint/eslint-plugin@npm:6.21.0" @@ -6626,6 +6886,23 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/parser@npm:^5.57.1": + version: 5.62.0 + resolution: "@typescript-eslint/parser@npm:5.62.0" + dependencies: + "@typescript-eslint/scope-manager": "npm:5.62.0" + "@typescript-eslint/types": "npm:5.62.0" + "@typescript-eslint/typescript-estree": "npm:5.62.0" + debug: "npm:^4.3.4" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/b6ca629d8f4e6283ff124501731cc886703eb4ce2c7d38b3e4110322ea21452b9d9392faf25be6bd72f54b89de7ffc72a40d9b159083ac54345a3d04b4fa5394 + languageName: node + linkType: hard + "@typescript-eslint/parser@npm:^6.19.0, @typescript-eslint/parser@npm:^6.19.1": version: 6.21.0 resolution: "@typescript-eslint/parser@npm:6.21.0" @@ -6708,6 +6985,23 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/type-utils@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/type-utils@npm:5.62.0" + dependencies: + "@typescript-eslint/typescript-estree": "npm:5.62.0" + "@typescript-eslint/utils": "npm:5.62.0" + debug: "npm:^4.3.4" + tsutils: "npm:^3.21.0" + peerDependencies: + eslint: "*" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/f9a4398d6d2aae09e3e765eff04cf4ab364376a87868031ac5c6a64c9bbb555cb1a7f99b07b3d1017e7422725b5f0bbee537f13b82ab2d930f161c987b3dece0 + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:6.21.0": version: 6.21.0 resolution: "@typescript-eslint/type-utils@npm:6.21.0" @@ -6844,6 +7138,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:5.62.0, @typescript-eslint/utils@npm:^5.10.0": + version: 5.62.0 + resolution: "@typescript-eslint/utils@npm:5.62.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.2.0" + "@types/json-schema": "npm:^7.0.9" + "@types/semver": "npm:^7.3.12" + "@typescript-eslint/scope-manager": "npm:5.62.0" + "@typescript-eslint/types": "npm:5.62.0" + "@typescript-eslint/typescript-estree": "npm:5.62.0" + eslint-scope: "npm:^5.1.1" + semver: "npm:^7.3.7" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 10/15ef13e43998a082b15f85db979f8d3ceb1f9ce4467b8016c267b1738d5e7cdb12aa90faf4b4e6dd6486c236cf9d33c463200465cf25ff997dbc0f12358550a1 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:6.21.0, @typescript-eslint/utils@npm:^6.19.1": version: 6.21.0 resolution: "@typescript-eslint/utils@npm:6.21.0" @@ -6875,24 +7187,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:^5.10.0": - version: 5.62.0 - resolution: "@typescript-eslint/utils@npm:5.62.0" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.2.0" - "@types/json-schema": "npm:^7.0.9" - "@types/semver": "npm:^7.3.12" - "@typescript-eslint/scope-manager": "npm:5.62.0" - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/typescript-estree": "npm:5.62.0" - eslint-scope: "npm:^5.1.1" - semver: "npm:^7.3.7" - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 10/15ef13e43998a082b15f85db979f8d3ceb1f9ce4467b8016c267b1738d5e7cdb12aa90faf4b4e6dd6486c236cf9d33c463200465cf25ff997dbc0f12358550a1 - languageName: node - linkType: hard - "@typescript-eslint/visitor-keys@npm:4.33.0": version: 4.33.0 resolution: "@typescript-eslint/visitor-keys@npm:4.33.0" @@ -10582,6 +10876,23 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-jest@npm:^26.5.3": + version: 26.9.0 + resolution: "eslint-plugin-jest@npm:26.9.0" + dependencies: + "@typescript-eslint/utils": "npm:^5.10.0" + peerDependencies: + "@typescript-eslint/eslint-plugin": ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + "@typescript-eslint/eslint-plugin": + optional: true + jest: + optional: true + checksum: 10/6ef994dc4c336e04d6ae3dbc83e526b4b889286c996ebb9bc3c71bb3d40957693fcda94198ae0dc516afc3799a97b5769f2f10ab1b4b9e0b7e735262ea22a5ff + languageName: node + linkType: hard + "eslint-plugin-jest@npm:^27.2.1, eslint-plugin-jest@npm:^27.9.0": version: 27.9.0 resolution: "eslint-plugin-jest@npm:27.9.0" @@ -10651,6 +10962,21 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-prettier@npm:^4.2.1": + version: 4.2.1 + resolution: "eslint-plugin-prettier@npm:4.2.1" + dependencies: + prettier-linter-helpers: "npm:^1.0.0" + peerDependencies: + eslint: ">=7.28.0" + prettier: ">=2.0.0" + peerDependenciesMeta: + eslint-config-prettier: + optional: true + checksum: 10/d387f85dd1bfcb6bc6b794845fee6afb9ebb2375653de6bcde6e615892fb97f85121a7c012a4651b181fc09953bdf54c9bc70cab7ad297019d89ae87dd007e28 + languageName: node + linkType: hard + "eslint-plugin-promise@npm:^6.0.0": version: 6.6.0 resolution: "eslint-plugin-promise@npm:6.6.0" @@ -11434,6 +11760,13 @@ __metadata: languageName: node linkType: hard +"fast-diff@npm:^1.1.2": + version: 1.3.0 + resolution: "fast-diff@npm:1.3.0" + checksum: 10/9e57415bc69cd6efcc720b3b8fe9fdaf42dcfc06f86f0f45378b1fa512598a8aac48aa3928c8751d58e2f01bb4ba4f07e4f3d9bc0d57586d45f1bd1e872c6cde + languageName: node + linkType: hard + "fast-equals@npm:^5.0.1": version: 5.0.1 resolution: "fast-equals@npm:5.0.1" @@ -16177,6 +16510,13 @@ __metadata: languageName: node linkType: hard +"natural-compare-lite@npm:^1.4.0": + version: 1.4.0 + resolution: "natural-compare-lite@npm:1.4.0" + checksum: 10/5222ac3986a2b78dd6069ac62cbb52a7bf8ffc90d972ab76dfe7b01892485d229530ed20d0c62e79a6b363a663b273db3bde195a1358ce9e5f779d4453887225 + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -17310,6 +17650,15 @@ __metadata: languageName: node linkType: hard +"prettier-linter-helpers@npm:^1.0.0": + version: 1.0.0 + resolution: "prettier-linter-helpers@npm:1.0.0" + dependencies: + fast-diff: "npm:^1.1.2" + checksum: 10/00ce8011cf6430158d27f9c92cfea0a7699405633f7f1d4a45f07e21bf78e99895911cbcdc3853db3a824201a7c745bd49bfea8abd5fb9883e765a90f74f8392 + languageName: node + linkType: hard + "prettier-plugin-jsdoc@npm:^1.3.0": version: 1.3.0 resolution: "prettier-plugin-jsdoc@npm:1.3.0" @@ -17688,9 +18037,9 @@ __metadata: languageName: node linkType: hard -"react-native-builder-bob@npm:0.30.2": - version: 0.30.2 - resolution: "react-native-builder-bob@npm:0.30.2" +"react-native-builder-bob@npm:0.33.1": + version: 0.33.1 + resolution: "react-native-builder-bob@npm:0.33.1" dependencies: "@babel/core": "npm:^7.25.2" "@babel/plugin-transform-strict-mode": "npm:^7.24.7" @@ -17716,7 +18065,7 @@ __metadata: yargs: "npm:^17.5.1" bin: bob: bin/bob - checksum: 10/0a6f2321ce7ba4aa08a40e5d599c94fe4705afc7d67ac9d4e2400603dd91cfa6646afdc02697ff683dca3b2654b1701bab4114c3548f4dfa0c9327a74a8a8a48 + checksum: 10/c4aaefdc1f82a88ce66c5b448ed851213ca614c777a775cb0772ab80e75852761b915d715ed6f58f99670b1b2fba6435e1d9c03445b9ba8eb44befeb77f5b532 languageName: node linkType: hard @@ -17877,7 +18226,7 @@ __metadata: prettier: "npm:^3.3.3" react: "npm:18.3.1" react-native: "npm:^0.76.1" - react-native-builder-bob: "npm:0.30.2" + react-native-builder-bob: "npm:0.33.1" react-native-gesture-handler: "npm:2.20.2" react-native-web: "npm:0.19.11" react-test-renderer: "npm:18.2.0" @@ -17947,6 +18296,27 @@ __metadata: languageName: node linkType: hard +"react-native-worklets@workspace:packages/react-native-worklets": + version: 0.0.0-use.local + resolution: "react-native-worklets@workspace:packages/react-native-worklets" + dependencies: + "@react-native-community/cli": "npm:15.0.1" + "@react-native/eslint-config": "npm:^0.73.1" + "@types/jest": "npm:^29.5.5" + "@types/react": "npm:^18.2.44" + eslint: "npm:^8.57.0" + prettier: "npm:^3.3.3" + react: "npm:18.3.1" + react-native: "npm:^0.76.1" + react-native-builder-bob: "npm:0.33.1" + typescript: "npm:~5.3.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + react: "*" + react-native: "*" + languageName: unknown + linkType: soft + "react-native@npm:0.75.4": version: 0.75.4 resolution: "react-native@npm:0.75.4"