UNPKG

react-native-reanimated

Version:

More powerful alternative to Animated library for React Native.

111 lines (106 loc) 4.19 kB
/* eslint-disable reanimated/use-reanimated-error */ 'use strict'; import type { WorkletFunction } from '../commonTypes'; import { isWorkletFunction } from '../commonTypes'; import { shouldBeUseWeb } from '../PlatformChecker'; function valueUnpacker( objectToUnpack: any, category?: string, remoteFunctionName?: string ): any { 'worklet'; let workletsCache = global.__workletsCache; let handleCache = global.__handleCache; if (workletsCache === undefined) { // init workletsCache = global.__workletsCache = new Map(); handleCache = global.__handleCache = new WeakMap(); } const workletHash = objectToUnpack.__workletHash; if (workletHash !== undefined) { let workletFun = workletsCache.get(workletHash); if (workletFun === undefined) { const initData = objectToUnpack.__initData; if (global.evalWithSourceMap) { // if the runtime (hermes only for now) supports loading source maps // we want to use the proper filename for the location as it guarantees // that debugger understands and loads the source code of the file where // the worklet is defined. workletFun = global.evalWithSourceMap( '(' + initData.code + '\n)', initData.location, initData.sourceMap ) as (...args: any[]) => any; } else if (global.evalWithSourceUrl) { // if the runtime doesn't support loading source maps, in dev mode we // can pass source url when evaluating the worklet. Now, instead of using // the actual file location we use worklet hash, as it the allows us to // properly symbolicate traces (see errors.ts for details) workletFun = global.evalWithSourceUrl( '(' + initData.code + '\n)', `worklet_${workletHash}` ) as (...args: any[]) => any; } else { // in release we use the regular eval to save on JSI calls // eslint-disable-next-line no-eval workletFun = eval('(' + initData.code + '\n)') as ( ...args: any[] ) => any; } workletsCache.set(workletHash, workletFun); } const functionInstance = workletFun.bind(objectToUnpack); objectToUnpack._recur = functionInstance; return functionInstance; } else if (objectToUnpack.__init !== undefined) { let value = handleCache.get(objectToUnpack); if (value === undefined) { value = objectToUnpack.__init(); handleCache.set(objectToUnpack, value); } return value; } else if (category === 'RemoteFunction') { const fun = () => { const label = remoteFunctionName ? `function \`${remoteFunctionName}\`` : 'anonymous function'; throw new Error(`[Reanimated] Tried to synchronously call a non-worklet ${label} on the UI thread. See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#tried-to-synchronously-call-a-non-worklet-function-on-the-ui-thread for more details.`); }; fun.__remoteFunction = objectToUnpack; return fun; } else { throw new Error( `[Reanimated] Data type in category "${category}" not recognized by value unpacker: "${_toString( objectToUnpack )}".` ); } } type ValueUnpacker = WorkletFunction< [objectToUnpack: any, category?: string], any >; if (__DEV__ && !shouldBeUseWeb()) { const testWorklet = (() => { 'worklet'; }) as WorkletFunction<[], void>; if (!isWorkletFunction(testWorklet)) { throw new Error( `[Reanimated] Failed to create a worklet. See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#failed-to-create-a-worklet for more details.` ); } if (!isWorkletFunction(valueUnpacker)) { throw new Error('[Reanimated] `valueUnpacker` is not a worklet'); } const closure = (valueUnpacker as ValueUnpacker).__closure; if (closure === undefined) { throw new Error('[Reanimated] `valueUnpacker` closure is undefined'); } if (Object.keys(closure).length !== 0) { throw new Error('[Reanimated] `valueUnpacker` must have empty closure'); } } export function getValueUnpackerCode() { return (valueUnpacker as ValueUnpacker).__initData.code; }