diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp index ca10e7a13d7..b159b070d6c 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp @@ -193,56 +193,6 @@ ReanimatedModuleProxy::~ReanimatedModuleProxy() { #endif // RCT_NEW_ARCH_ENABLED } -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."); - 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 - // temporary JSI objects and hence it allows for such objects to be - // garbage collected much sooner. Apparently the scope API is only - // supported on Hermes at the moment. - const auto scope = jsi::Scope( - workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime()); -#endif - workletsModuleProxy_->getUIWorkletRuntime()->runGuarded(shareableWorklet); - }); -} - -jsi::Value ReanimatedModuleProxy::executeOnUIRuntimeSync( - jsi::Runtime &rt, - const jsi::Value &worklet) { - return workletsModuleProxy_->getUIWorkletRuntime()->executeSync(rt, worklet); -} - -jsi::Value ReanimatedModuleProxy::createWorkletRuntime( - jsi::Runtime &rt, - const jsi::Value &name, - const jsi::Value &initializer) { - auto workletRuntime = std::make_shared( - rt, - 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); - return jsi::Object::createFromHostObject(rt, workletRuntime); -} - -jsi::Value ReanimatedModuleProxy::scheduleOnRuntime( - jsi::Runtime &rt, - const jsi::Value &workletRuntimeValue, - const jsi::Value &shareableWorkletValue) { - reanimated::scheduleOnRuntime(rt, workletRuntimeValue, shareableWorkletValue); - return jsi::Value::undefined(); -} - jsi::Value ReanimatedModuleProxy::registerEventHandler( jsi::Runtime &rt, const jsi::Value &worklet, diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h index 65b3a5f3c65..dd3b6661999 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h @@ -42,19 +42,6 @@ class ReanimatedModuleProxy : public ReanimatedModuleProxySpec { ~ReanimatedModuleProxy(); - void scheduleOnUI(jsi::Runtime &rt, const jsi::Value &worklet) override; - jsi::Value executeOnUIRuntimeSync(jsi::Runtime &rt, const jsi::Value &worklet) - override; - - jsi::Value createWorkletRuntime( - jsi::Runtime &rt, - const jsi::Value &name, - const jsi::Value &initializer) override; - jsi::Value scheduleOnRuntime( - jsi::Runtime &rt, - const jsi::Value &workletRuntimeValue, - const jsi::Value &shareableWorkletValue) override; - jsi::Value registerEventHandler( jsi::Runtime &rt, const jsi::Value &worklet, diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.cpp index d797483bcf1..47ad998e6e4 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.cpp @@ -7,45 +7,6 @@ namespace reanimated { -// scheduler - -static jsi::Value REANIMATED_SPEC_PREFIX(scheduleOnUI)( - jsi::Runtime &rt, - TurboModule &turboModule, - const jsi::Value *args, - size_t) { - static_cast(&turboModule) - ->scheduleOnUI(rt, std::move(args[0])); - return jsi::Value::undefined(); -} - -static jsi::Value REANIMATED_SPEC_PREFIX(executeOnUIRuntimeSync)( - jsi::Runtime &rt, - TurboModule &turboModule, - const jsi::Value *args, - size_t) { - return static_cast(&turboModule) - ->executeOnUIRuntimeSync(rt, std::move(args[0])); -} - -static jsi::Value REANIMATED_SPEC_PREFIX(createWorkletRuntime)( - jsi::Runtime &rt, - TurboModule &turboModule, - const jsi::Value *args, - size_t) { - return static_cast(&turboModule) - ->createWorkletRuntime(rt, std::move(args[0]), std::move(args[1])); -} - -static jsi::Value REANIMATED_SPEC_PREFIX(scheduleOnRuntime)( - jsi::Runtime &rt, - TurboModule &turboModule, - const jsi::Value *args, - size_t) { - return static_cast(&turboModule) - ->scheduleOnRuntime(rt, std::move(args[0]), std::move(args[1])); -} - static jsi::Value REANIMATED_SPEC_PREFIX(registerEventHandler)( jsi::Runtime &rt, TurboModule &turboModule, @@ -163,15 +124,6 @@ static jsi::Value REANIMATED_SPEC_PREFIX(setShouldAnimateExiting)( ReanimatedModuleProxySpec::ReanimatedModuleProxySpec( const std::shared_ptr &jsInvoker) : TurboModule("NativeReanimated", jsInvoker) { - methodMap_["scheduleOnUI"] = - MethodMetadata{1, REANIMATED_SPEC_PREFIX(scheduleOnUI)}; - methodMap_["executeOnUIRuntimeSync"] = - MethodMetadata{1, REANIMATED_SPEC_PREFIX(executeOnUIRuntimeSync)}; - methodMap_["createWorkletRuntime"] = - MethodMetadata{2, REANIMATED_SPEC_PREFIX(createWorkletRuntime)}; - methodMap_["scheduleOnRuntime"] = - MethodMetadata{2, REANIMATED_SPEC_PREFIX(scheduleOnRuntime)}; - methodMap_["registerEventHandler"] = MethodMetadata{3, REANIMATED_SPEC_PREFIX(registerEventHandler)}; methodMap_["unregisterEventHandler"] = diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.h b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.h index 3f82813abff..3321e93d889 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.h @@ -18,22 +18,6 @@ class JSI_EXPORT ReanimatedModuleProxySpec : public TurboModule { const std::shared_ptr &jsInvoker); public: - // Scheduling - virtual void scheduleOnUI(jsi::Runtime &rt, const jsi::Value &worklet) = 0; - virtual jsi::Value executeOnUIRuntimeSync( - jsi::Runtime &rt, - const jsi::Value &worklet) = 0; - - // Worklet runtime - virtual jsi::Value createWorkletRuntime( - jsi::Runtime &rt, - const jsi::Value &name, - const jsi::Value &initializer) = 0; - virtual jsi::Value scheduleOnRuntime( - jsi::Runtime &rt, - const jsi::Value &workletRuntimeValue, - const jsi::Value &shareableWorkletValue) = 0; - // events virtual jsi::Value registerEventHandler( jsi::Runtime &rt, diff --git a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.cpp index ef488e28c4b..f49d86b9ec5 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.cpp @@ -14,6 +14,18 @@ #include +// Standard `__cplusplus` macro reference: +// https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros +#if REACT_NATIVE_MINOR_VERSION >= 75 || __cplusplus >= 202002L +// Implicit copy capture of `this` is deprecated in NDK27, which uses C++20. +#define COPY_CAPTURE_WITH_THIS [ =, this ] // NOLINT (whitespace/braces) +#else +// React Native 0.75 is the last one which allows NDK23. NDK23 uses C++17 and +// explicitly disallows C++20 features, including the syntax above. Therefore we +// fallback to the deprecated syntax here. +#define COPY_CAPTURE_WITH_THIS [=] // NOLINT (whitespace/braces) +#endif // REACT_NATIVE_MINOR_VERSION >= 75 || __cplusplus >= 202002L + using namespace facebook; namespace worklets { @@ -53,4 +65,53 @@ jsi::Value WorkletsModuleProxy::makeShareableClone( rt, value, shouldRetainRemote, nativeStateSource); } +void WorkletsModuleProxy::scheduleOnUI( + jsi::Runtime &rt, + const jsi::Value &worklet) { + auto shareableWorklet = extractShareableOrThrow( + rt, worklet, "[Worklets] Only worklets can be scheduled to run on UI."); + uiScheduler_->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 + // temporary JSI objects and hence it allows for such objects to be + // garbage collected much sooner. Apparently the scope API is only + // supported on Hermes at the moment. + const auto scope = jsi::Scope(uiWorkletRuntime_->getJSIRuntime()); +#endif + uiWorkletRuntime_->runGuarded(shareableWorklet); + }); +} + +jsi::Value WorkletsModuleProxy::executeOnUIRuntimeSync( + jsi::Runtime &rt, + const jsi::Value &worklet) { + return uiWorkletRuntime_->executeSync(rt, worklet); +} + +jsi::Value WorkletsModuleProxy::createWorkletRuntime( + jsi::Runtime &rt, + const jsi::Value &name, + const jsi::Value &initializer) { + auto workletRuntime = std::make_shared( + rt, + jsQueue_, + jsScheduler_, + name.asString(rt).utf8(rt), + false /* supportsLocking */, + valueUnpackerCode_); + auto initializerShareable = extractShareableOrThrow( + rt, initializer, "[Reanimated] Initializer must be a worklet."); + workletRuntime->runGuarded(initializerShareable); + return jsi::Object::createFromHostObject(rt, workletRuntime); +} + +jsi::Value WorkletsModuleProxy::scheduleOnRuntime( + jsi::Runtime &rt, + const jsi::Value &workletRuntimeValue, + const jsi::Value &shareableWorkletValue) { + worklets::scheduleOnRuntime(rt, workletRuntimeValue, shareableWorkletValue); + return jsi::Value::undefined(); +} + } // namespace worklets diff --git a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.h b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.h index 90fc31b4cd4..b2f4203a954 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.h +++ b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.h @@ -29,6 +29,21 @@ class WorkletsModuleProxy : public WorkletsModuleProxySpec { const jsi::Value &shouldRetainRemote, const jsi::Value &nativeStateSource) override; + void scheduleOnUI(jsi::Runtime &rt, const jsi::Value &worklet) override; + + jsi::Value executeOnUIRuntimeSync(jsi::Runtime &rt, const jsi::Value &worklet) + override; + + jsi::Value createWorkletRuntime( + jsi::Runtime &rt, + const jsi::Value &name, + const jsi::Value &initializer) override; + + jsi::Value scheduleOnRuntime( + jsi::Runtime &rt, + const jsi::Value &workletRuntimeValue, + const jsi::Value &shareableWorkletValue) override; + [[nodiscard]] inline std::string getValueUnpackerCode() const { return valueUnpackerCode_; } diff --git a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.cpp b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.cpp index 624c42ee224..973dcf333af 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.cpp +++ b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.cpp @@ -17,11 +17,56 @@ static jsi::Value WORKLETS_SPEC_PREFIX(makeShareableClone)( rt, std::move(args[0]), std::move(args[1]), std::move(args[2])); } +static jsi::Value WORKLETS_SPEC_PREFIX(scheduleOnUI)( + jsi::Runtime &rt, + TurboModule &turboModule, + const jsi::Value *args, + size_t) { + static_cast(&turboModule) + ->scheduleOnUI(rt, std::move(args[0])); + return jsi::Value::undefined(); +} + +static jsi::Value WORKLETS_SPEC_PREFIX(executeOnUIRuntimeSync)( + jsi::Runtime &rt, + TurboModule &turboModule, + const jsi::Value *args, + size_t) { + return static_cast(&turboModule) + ->executeOnUIRuntimeSync(rt, std::move(args[0])); +} + +static jsi::Value WORKLETS_SPEC_PREFIX(createWorkletRuntime)( + jsi::Runtime &rt, + TurboModule &turboModule, + const jsi::Value *args, + size_t) { + return static_cast(&turboModule) + ->createWorkletRuntime(rt, std::move(args[0]), std::move(args[1])); +} + +static jsi::Value WORKLETS_SPEC_PREFIX(scheduleOnRuntime)( + jsi::Runtime &rt, + TurboModule &turboModule, + const jsi::Value *args, + size_t) { + return static_cast(&turboModule) + ->scheduleOnRuntime(rt, std::move(args[0]), std::move(args[1])); +} + WorkletsModuleProxySpec::WorkletsModuleProxySpec( const std::shared_ptr jsInvoker) : TurboModule("NativeWorklets", jsInvoker) { methodMap_["makeShareableClone"] = MethodMetadata{2, WORKLETS_SPEC_PREFIX(makeShareableClone)}; + methodMap_["scheduleOnUI"] = + MethodMetadata{1, WORKLETS_SPEC_PREFIX(scheduleOnUI)}; + methodMap_["executeOnUIRuntimeSync"] = + MethodMetadata{1, WORKLETS_SPEC_PREFIX(executeOnUIRuntimeSync)}; + methodMap_["createWorkletRuntime"] = + MethodMetadata{2, WORKLETS_SPEC_PREFIX(createWorkletRuntime)}; + methodMap_["scheduleOnRuntime"] = + MethodMetadata{2, WORKLETS_SPEC_PREFIX(scheduleOnRuntime)}; } } // namespace worklets diff --git a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.h b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.h index c79a86b908f..5d3573c969a 100644 --- a/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.h +++ b/packages/react-native-reanimated/Common/cpp/worklets/NativeModules/WorkletsModuleProxySpec.h @@ -15,11 +15,30 @@ class JSI_EXPORT WorkletsModuleProxySpec : public TurboModule { const std::shared_ptr jsInvoker); public: + // Shareables virtual jsi::Value makeShareableClone( jsi::Runtime &rt, const jsi::Value &value, const jsi::Value &shouldRetainRemote, const jsi::Value &nativeStateSource) = 0; + + // Scheduling + virtual void scheduleOnUI(jsi::Runtime &rt, const jsi::Value &worklet) = 0; + + virtual jsi::Value executeOnUIRuntimeSync( + jsi::Runtime &rt, + const jsi::Value &worklet) = 0; + + // Worklet runtime + virtual jsi::Value createWorkletRuntime( + jsi::Runtime &rt, + const jsi::Value &name, + const jsi::Value &initializer) = 0; + + virtual jsi::Value scheduleOnRuntime( + jsi::Runtime &rt, + const jsi::Value &workletRuntimeValue, + const jsi::Value &shareableWorkletValue) = 0; }; } // namespace worklets diff --git a/packages/react-native-reanimated/src/ReanimatedModule/NativeReanimated.ts b/packages/react-native-reanimated/src/ReanimatedModule/NativeReanimated.ts index 3ff0ef98727..40fb1b7f33b 100644 --- a/packages/react-native-reanimated/src/ReanimatedModule/NativeReanimated.ts +++ b/packages/react-native-reanimated/src/ReanimatedModule/NativeReanimated.ts @@ -10,7 +10,6 @@ import type { } from '../commonTypes'; import { checkCppVersion } from '../platform-specific/checkCppVersion'; import { jsVersion } from '../platform-specific/jsVersion'; -import type { WorkletRuntime } from '../runtimes'; import { isFabric } from '../PlatformChecker'; import type React from 'react'; import { getShadowNodeWrapperFromRef } from '../fabricUtils'; @@ -65,28 +64,6 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti this.#reanimatedModuleProxy = global.__reanimatedModuleProxy; } - scheduleOnUI(shareable: ShareableRef) { - return this.#reanimatedModuleProxy.scheduleOnUI(shareable); - } - - executeOnUIRuntimeSync(shareable: ShareableRef): R { - return this.#reanimatedModuleProxy.executeOnUIRuntimeSync(shareable); - } - - createWorkletRuntime(name: string, initializer: ShareableRef<() => void>) { - return this.#reanimatedModuleProxy.createWorkletRuntime(name, initializer); - } - - scheduleOnRuntime( - workletRuntime: WorkletRuntime, - shareableWorklet: ShareableRef - ) { - return this.#reanimatedModuleProxy.scheduleOnRuntime( - workletRuntime, - shareableWorklet - ); - } - registerSensor( sensorType: number, interval: number, 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 e4fa3b8a008..8b071cb4d95 100644 --- a/packages/react-native-reanimated/src/ReanimatedModule/js-reanimated/JSReanimated.ts +++ b/packages/react-native-reanimated/src/ReanimatedModule/js-reanimated/JSReanimated.ts @@ -15,8 +15,6 @@ import type { WorkletFunction, } from '../../commonTypes'; import type { WebSensor } from './WebSensor'; -import { mockedRequestAnimationFrame } from '../../mockedRequestAnimationFrame'; -import type { WorkletRuntime } from '../../runtimes'; import { logger } from '../../logger'; import { ReanimatedError } from '../../errors'; import { WorkletsModule } from '../../worklets'; @@ -25,14 +23,6 @@ export function createJSReanimatedModule(): IReanimatedModule { return new JSReanimated(); } -// In Node.js environments (like when static rendering with Expo Router) -// requestAnimationFrame is unavailable, so we use our mock. -// It also has to be mocked for Jest purposes (see `initializeUIRuntime`). -const requestAnimationFrameImpl = - isJest() || !globalThis.requestAnimationFrame - ? mockedRequestAnimationFrame - : globalThis.requestAnimationFrame; - class JSReanimated implements IReanimatedModule { /** * We keep the instance of `WorkletsModule` here to keep correct coupling of @@ -43,26 +33,6 @@ class JSReanimated implements IReanimatedModule { sensors = new Map(); platform?: Platform = undefined; - scheduleOnUI(worklet: ShareableRef) { - // @ts-ignore web implementation has still not been updated after the rewrite, this will be addressed once the web implementation updates are ready - requestAnimationFrameImpl(worklet); - } - - createWorkletRuntime( - _name: string, - _initializer: ShareableRef<() => void> - ): WorkletRuntime { - throw new ReanimatedError( - 'createWorkletRuntime is not available in JSReanimated.' - ); - } - - scheduleOnRuntime() { - throw new ReanimatedError( - 'scheduleOnRuntime is not available in JSReanimated.' - ); - } - registerEventHandler( _eventHandler: ShareableRef, _eventName: string, @@ -293,12 +263,6 @@ class JSReanimated implements IReanimatedModule { 'configureProps is not available in JSReanimated.' ); } - - executeOnUIRuntimeSync(_shareable: ShareableRef): R { - throw new ReanimatedError( - '`executeOnUIRuntimeSync` is not available in JSReanimated.' - ); - } } // Lack of this export breaks TypeScript generation since diff --git a/packages/react-native-reanimated/src/ReanimatedModule/reanimatedModuleProxy.ts b/packages/react-native-reanimated/src/ReanimatedModule/reanimatedModuleProxy.ts index 5d334fb9897..a2380bfee86 100644 --- a/packages/react-native-reanimated/src/ReanimatedModule/reanimatedModuleProxy.ts +++ b/packages/react-native-reanimated/src/ReanimatedModule/reanimatedModuleProxy.ts @@ -8,24 +8,9 @@ import type { LayoutAnimationBatchItem, WorkletFunction, } from '../commonTypes'; -import type { WorkletRuntime } from '../runtimes'; /** Type of `__reanimatedModuleProxy` injected with JSI. */ export interface ReanimatedModuleProxy { - scheduleOnUI(shareable: ShareableRef): void; - - executeOnUIRuntimeSync(shareable: ShareableRef): R; - - createWorkletRuntime( - name: string, - initializer: ShareableRef<() => void> - ): WorkletRuntime; - - scheduleOnRuntime( - workletRuntime: WorkletRuntime, - worklet: ShareableRef - ): void; - registerEventHandler( eventHandler: ShareableRef, eventName: string, diff --git a/packages/react-native-reanimated/src/commonTypes.ts b/packages/react-native-reanimated/src/commonTypes.ts index 725436df1d8..68bdc7888c5 100644 --- a/packages/react-native-reanimated/src/commonTypes.ts +++ b/packages/react-native-reanimated/src/commonTypes.ts @@ -8,10 +8,14 @@ import type { import type { WorkletsModuleProxy } from './worklets'; import type { ReanimatedModuleProxy } from './ReanimatedModule'; -export interface IWorkletsModule extends WorkletsModuleProxy {} +type DisallowKeysOf = { + [TKey in keyof TInterface]?: never; +}; +export interface IWorkletsModule extends WorkletsModuleProxy {} export interface IReanimatedModule - extends Omit { + extends Omit, + DisallowKeysOf { getViewProp( viewTag: number, propName: string, diff --git a/packages/react-native-reanimated/src/runtimes.ts b/packages/react-native-reanimated/src/runtimes.ts index 448d0d2a70b..3237acc0e67 100644 --- a/packages/react-native-reanimated/src/runtimes.ts +++ b/packages/react-native-reanimated/src/runtimes.ts @@ -4,12 +4,12 @@ import type { WorkletFunction } from './commonTypes'; import { ReanimatedError, registerReanimatedError } from './errors'; import { setupCallGuard, setupConsole } from './initializers'; import { registerLoggerConfig } from './logger'; -import { ReanimatedModule } from './ReanimatedModule'; import { shouldBeUseWeb } from './PlatformChecker'; import { makeShareableCloneOnUIRecursive, makeShareableCloneRecursive, } from './shareables'; +import { WorkletsModule } from './worklets'; const SHOULD_BE_USE_WEB = shouldBeUseWeb(); @@ -43,7 +43,7 @@ export function createWorkletRuntime( // Assign to a different variable as __reanimatedLoggerConfig is not a captured // identifier in the Worklet runtime. const config = __reanimatedLoggerConfig; - return ReanimatedModule.createWorkletRuntime( + return WorkletsModule.createWorkletRuntime( name, makeShareableCloneRecursive(() => { 'worklet'; @@ -86,7 +86,7 @@ export function runOnRuntime( ); } return (...args) => - ReanimatedModule.scheduleOnRuntime( + WorkletsModule.scheduleOnRuntime( workletRuntime, makeShareableCloneRecursive(() => { 'worklet'; diff --git a/packages/react-native-reanimated/src/threads.ts b/packages/react-native-reanimated/src/threads.ts index 02b46d687c2..291798b82de 100644 --- a/packages/react-native-reanimated/src/threads.ts +++ b/packages/react-native-reanimated/src/threads.ts @@ -7,7 +7,7 @@ import { } from './shareables'; import { isWorkletFunction } from './commonTypes'; import { ReanimatedError } from './errors'; -import { ReanimatedModule } from './ReanimatedModule'; +import { WorkletsModule } from './worklets'; const IS_JEST = isJest(); const SHOULD_BE_USE_WEB = shouldBeUseWeb(); @@ -101,7 +101,7 @@ export function runOnUI( // that's not possible, and hence in Jest environment instead of using scheduling // mechanism we just schedule the work ommiting the queue. This is ok for the // uses that we currently have but may not be ok for future tests that we write. - ReanimatedModule.scheduleOnUI( + WorkletsModule.scheduleOnUI( makeShareableCloneRecursive(() => { 'worklet'; worklet(...args); @@ -123,7 +123,7 @@ export function runOnUI( queueMicrotask(() => { const queue = _runOnUIQueue; _runOnUIQueue = []; - ReanimatedModule.scheduleOnUI( + WorkletsModule.scheduleOnUI( makeShareableCloneRecursive(() => { 'worklet'; // eslint-disable-next-line @typescript-eslint/no-shadow @@ -147,7 +147,7 @@ export function executeOnUIRuntimeSync( worklet: WorkletFunction ): (...args: Args) => ReturnValue { return (...args) => { - return ReanimatedModule.executeOnUIRuntimeSync( + return WorkletsModule.executeOnUIRuntimeSync( makeShareableCloneRecursive(() => { 'worklet'; const result = worklet(...args); @@ -177,7 +177,7 @@ export function runOnUIImmediately( ); } return (...args) => { - ReanimatedModule.scheduleOnUI( + WorkletsModule.scheduleOnUI( makeShareableCloneRecursive(() => { 'worklet'; worklet(...args); diff --git a/packages/react-native-reanimated/src/worklets/WorkletsModule/JSWorklets.ts b/packages/react-native-reanimated/src/worklets/WorkletsModule/JSWorklets.ts index 2cc7cfa714c..3b535f81286 100644 --- a/packages/react-native-reanimated/src/worklets/WorkletsModule/JSWorklets.ts +++ b/packages/react-native-reanimated/src/worklets/WorkletsModule/JSWorklets.ts @@ -2,15 +2,53 @@ import type { IWorkletsModule, ShareableRef } from '../../commonTypes'; import { ReanimatedError } from '../../errors'; +import { mockedRequestAnimationFrame } from '../../mockedRequestAnimationFrame'; +import { isJest } from '../../PlatformChecker'; +import type { WorkletRuntime } from '../../runtimes'; export function createJSWorkletsModule(): IWorkletsModule { return new JSWorklets(); } +// In Node.js environments (like when static rendering with Expo Router) +// requestAnimationFrame is unavailable, so we use our mock. +// It also has to be mocked for Jest purposes (see `initializeUIRuntime`). +const requestAnimationFrameImpl = + isJest() || !globalThis.requestAnimationFrame + ? mockedRequestAnimationFrame + : globalThis.requestAnimationFrame; + class JSWorklets implements IWorkletsModule { - makeShareableClone(): ShareableRef { + makeShareableClone(): ShareableRef { throw new ReanimatedError( 'makeShareableClone should never be called in JSWorklets.' ); } + + scheduleOnUI(worklet: ShareableRef) { + // @ts-ignore web implementation has still not been updated after the rewrite, + // this will be addressed once the web implementation updates are ready + requestAnimationFrameImpl(worklet); + } + + executeOnUIRuntimeSync(_shareable: ShareableRef): R { + throw new ReanimatedError( + '`executeOnUIRuntimeSync` is not available in JSReanimated.' + ); + } + + createWorkletRuntime( + _name: string, + _initializer: ShareableRef<() => void> + ): WorkletRuntime { + throw new ReanimatedError( + 'createWorkletRuntime is not available in JSReanimated.' + ); + } + + scheduleOnRuntime() { + throw new ReanimatedError( + 'scheduleOnRuntime is not available in JSReanimated.' + ); + } } diff --git a/packages/react-native-reanimated/src/worklets/WorkletsModule/NativeWorklets.ts b/packages/react-native-reanimated/src/worklets/WorkletsModule/NativeWorklets.ts index e305d6f164d..16243cefe2e 100644 --- a/packages/react-native-reanimated/src/worklets/WorkletsModule/NativeWorklets.ts +++ b/packages/react-native-reanimated/src/worklets/WorkletsModule/NativeWorklets.ts @@ -2,8 +2,9 @@ import { getValueUnpackerCode } from '../valueUnpacker'; import { WorkletsTurboModule } from '../../specs'; import { ReanimatedError } from '../../errors'; -import type { IWorkletsModule } from '../../commonTypes'; +import type { IWorkletsModule, ShareableRef } from '../../commonTypes'; import type { WorkletsModuleProxy } from './workletsModuleProxy'; +import type { WorkletRuntime } from '../../runtimes'; export function createNativeWorkletsModule(): IWorkletsModule { return new NativeWorklets(); @@ -26,8 +27,8 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti this.#workletsModuleProxy = global.__workletsModuleProxy; } - makeShareableClone( - value: T, + makeShareableClone( + value: TValue, shouldPersistRemote: boolean, nativeStateSource?: object ) { @@ -37,4 +38,28 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti nativeStateSource ); } + + scheduleOnUI(shareable: ShareableRef) { + return this.#workletsModuleProxy.scheduleOnUI(shareable); + } + + executeOnUIRuntimeSync( + shareable: ShareableRef + ): TReturn { + return this.#workletsModuleProxy.executeOnUIRuntimeSync(shareable); + } + + createWorkletRuntime(name: string, initializer: ShareableRef<() => void>) { + return this.#workletsModuleProxy.createWorkletRuntime(name, initializer); + } + + scheduleOnRuntime( + workletRuntime: WorkletRuntime, + shareableWorklet: ShareableRef + ) { + return this.#workletsModuleProxy.scheduleOnRuntime( + workletRuntime, + shareableWorklet + ); + } } diff --git a/packages/react-native-reanimated/src/worklets/WorkletsModule/workletsModuleProxy.ts b/packages/react-native-reanimated/src/worklets/WorkletsModule/workletsModuleProxy.ts index 537e5556502..df96918633a 100644 --- a/packages/react-native-reanimated/src/worklets/WorkletsModule/workletsModuleProxy.ts +++ b/packages/react-native-reanimated/src/worklets/WorkletsModule/workletsModuleProxy.ts @@ -1,6 +1,7 @@ 'use strict'; import type { ShareableRef } from '../../commonTypes'; +import type { WorkletRuntime } from '../../runtimes'; /** Type of `__workletsModuleProxy` injected with JSI. */ export interface WorkletsModuleProxy { @@ -9,4 +10,20 @@ export interface WorkletsModuleProxy { shouldPersistRemote: boolean, nativeStateSource?: object ): ShareableRef; + + scheduleOnUI(shareable: ShareableRef): void; + + executeOnUIRuntimeSync( + shareable: ShareableRef + ): TReturn; + + createWorkletRuntime( + name: string, + initializer: ShareableRef<() => void> + ): WorkletRuntime; + + scheduleOnRuntime( + workletRuntime: WorkletRuntime, + worklet: ShareableRef + ): void; }