UNPKG

4.14 kBPlain TextView Raw
1'use strict';
2import { shouldBeUseWeb } from './PlatformChecker';
3import { isWorkletFunction } from './commonTypes';
4import type { WorkletFunction } from './commonTypes';
5
6function valueUnpacker(
7 objectToUnpack: any,
8 category?: string,
9 remoteFunctionName?: string
10): any {
11 'worklet';
12 let workletsCache = global.__workletsCache;
13 let handleCache = global.__handleCache;
14 if (workletsCache === undefined) {
15 // init
16 workletsCache = global.__workletsCache = new Map();
17 handleCache = global.__handleCache = new WeakMap();
18 }
19 const workletHash = objectToUnpack.__workletHash;
20 if (workletHash !== undefined) {
21 let workletFun = workletsCache.get(workletHash);
22 if (workletFun === undefined) {
23 const initData = objectToUnpack.__initData;
24 if (global.evalWithSourceMap) {
25 // if the runtime (hermes only for now) supports loading source maps
26 // we want to use the proper filename for the location as it guarantees
27 // that debugger understands and loads the source code of the file where
28 // the worklet is defined.
29 workletFun = global.evalWithSourceMap(
30 '(' + initData.code + '\n)',
31 initData.location,
32 initData.sourceMap
33 ) as (...args: any[]) => any;
34 } else if (global.evalWithSourceUrl) {
35 // if the runtime doesn't support loading source maps, in dev mode we
36 // can pass source url when evaluating the worklet. Now, instead of using
37 // the actual file location we use worklet hash, as it the allows us to
38 // properly symbolicate traces (see errors.ts for details)
39 workletFun = global.evalWithSourceUrl(
40 '(' + initData.code + '\n)',
41 `worklet_${workletHash}`
42 ) as (...args: any[]) => any;
43 } else {
44 // in release we use the regular eval to save on JSI calls
45 // eslint-disable-next-line no-eval
46 workletFun = eval('(' + initData.code + '\n)') as (
47 ...args: any[]
48 ) => any;
49 }
50 workletsCache.set(workletHash, workletFun);
51 }
52 const functionInstance = workletFun.bind(objectToUnpack);
53 objectToUnpack._recur = functionInstance;
54 return functionInstance;
55 } else if (objectToUnpack.__init !== undefined) {
56 let value = handleCache.get(objectToUnpack);
57 if (value === undefined) {
58 value = objectToUnpack.__init();
59 handleCache.set(objectToUnpack, value);
60 }
61 return value;
62 } else if (category === 'RemoteFunction') {
63 const fun = () => {
64 const label = remoteFunctionName
65 ? `function \`${remoteFunctionName}\``
66 : 'anonymous function';
67 throw new Error(`[Reanimated] Tried to synchronously call a non-worklet ${label} on the UI thread.
68See 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.`);
69 };
70 fun.__remoteFunction = objectToUnpack;
71 return fun;
72 } else {
73 throw new Error(
74 `[Reanimated] Data type in category "${category}" not recognized by value unpacker: "${_toString(
75 objectToUnpack
76 )}".`
77 );
78 }
79}
80
81type ValueUnpacker = WorkletFunction<
82 [objectToUnpack: any, category?: string],
83 any
84>;
85
86if (__DEV__ && !shouldBeUseWeb()) {
87 const testWorklet = (() => {
88 'worklet';
89 }) as WorkletFunction<[], void>;
90 if (!isWorkletFunction(testWorklet)) {
91 throw new Error(
92 `[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.`
93 );
94 }
95 if (!isWorkletFunction(valueUnpacker)) {
96 throw new Error('[Reanimated] `valueUnpacker` is not a worklet');
97 }
98 const closure = (valueUnpacker as ValueUnpacker).__closure;
99 if (closure === undefined) {
100 throw new Error('[Reanimated] `valueUnpacker` closure is undefined');
101 }
102 if (Object.keys(closure).length !== 0) {
103 throw new Error('[Reanimated] `valueUnpacker` must have empty closure');
104 }
105}
106
107export function getValueUnpackerCode() {
108 return (valueUnpacker as ValueUnpacker).__initData.code;
109}
110
\No newline at end of file