-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix passing remote functions as arguments to runOnJS
#4617
Conversation
…s remote function arguments
src/reanimated2/shareables.ts
Outdated
} | ||
return _makeShareableClone(toAdapt); | ||
return _makeShareableClone(toAdapt) as ShareableRef<T>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not a huge fan of this case here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tomekzaw @tjzel I patched these locally in my project and I am running into quite a bit of Error:
This is my patch based on 3.4.2: diff --git a/node_modules/react-native-reanimated/src/reanimated2/commonTypes.ts b/node_modules/react-native-reanimated/src/reanimated2/commonTypes.ts
index 5ab6085..07770a5 100644
--- a/node_modules/react-native-reanimated/src/reanimated2/commonTypes.ts
+++ b/node_modules/react-native-reanimated/src/reanimated2/commonTypes.ts
@@ -55,6 +55,12 @@ export type ShareableRef<T> = {
__hostObjectShareableJSRef: T;
};
+// In case of objects with depth or arrays of objects or arrays of arrays etc.
+// we add this utility type that makes it a SharaebleRef of the outermost type.
+export type FlatShareableRef<T> = T extends ShareableRef<infer U>
+ ? ShareableRef<U>
+ : ShareableRef<T>;
+
export type ShareableSyncDataHolderRef<T> = {
__hostObjectShareableJSRefSyncDataHolder: T;
};
diff --git a/node_modules/react-native-reanimated/src/reanimated2/globals.d.ts b/node_modules/react-native-reanimated/src/reanimated2/globals.d.ts
index f3259e4..343695a 100644
--- a/node_modules/react-native-reanimated/src/reanimated2/globals.d.ts
+++ b/node_modules/react-native-reanimated/src/reanimated2/globals.d.ts
@@ -8,6 +8,7 @@ import type {
ShareableSyncDataHolderRef,
ShadowNodeWrapper,
ComplexWorkletFunction,
+ FlatShareableRef,
} from './commonTypes';
import type { AnimatedStyle } from './helperTypes';
import type { FrameCallbackRegistryUI } from './frameCallback/FrameCallbackRegistryUI';
@@ -35,7 +36,7 @@ declare global {
) => void;
var _notifyAboutEnd: (tag: number, removeView: boolean) => void;
var _setGestureState: (handlerTag: number, newState: number) => void;
- var _makeShareableClone: <T>(value: T) => ShareableRef<T>;
+ var _makeShareableClone: <T>(value: T) => FlatShareableRef<T>;
var _updateDataSynchronously: (
dataHolder: ShareableSyncDataHolderRef<any>,
data: ShareableRef<any>
diff --git a/node_modules/react-native-reanimated/src/reanimated2/shareables.ts b/node_modules/react-native-reanimated/src/reanimated2/shareables.ts
index 03575ab..e03f727 100644
--- a/node_modules/react-native-reanimated/src/reanimated2/shareables.ts
+++ b/node_modules/react-native-reanimated/src/reanimated2/shareables.ts
@@ -1,5 +1,9 @@
import NativeReanimatedModule from './NativeReanimated';
-import type { ShareableRef, WorkletFunction } from './commonTypes';
+import type {
+ FlatShareableRef,
+ ShareableRef,
+ WorkletFunction,
+} from './commonTypes';
import { shouldBeUseWeb } from './PlatformChecker';
import { registerWorkletStackDetails } from './errors';
import { jsVersion } from './platform-specific/jsVersion';
@@ -20,7 +24,7 @@ const _shareableFlag = Symbol('shareable flag');
const MAGIC_KEY = 'REANIMATED_MAGIC_KEY';
-function isHostObject(value: NonNullable<object>): boolean {
+function isHostObject(value: NonNullable<object>) {
// We could use JSI to determine whether an object is a host object, however
// the below workaround works well and is way faster than an additional JSI call.
// We use the fact that host objects have broken implementation of `hasOwnProperty`
@@ -247,28 +251,44 @@ function getWorkletCode(value: WorkletFunction) {
return code;
}
-export function makeShareableCloneOnUIRecursive<T>(value: T): ShareableRef<T> {
+type RemoteFunction<T> = {
+ __remoteFunction: FlatShareableRef<T>;
+};
+
+function isRemoteFunction<T>(value: object): value is RemoteFunction<T> {
+ return '__remoteFunction' in value;
+}
+
+export function makeShareableCloneOnUIRecursive<T>(
+ value: T
+): FlatShareableRef<T> {
'worklet';
if (USE_STUB_IMPLEMENTATION) {
// @ts-ignore web is an interesting place where we don't run a secondary VM on the UI thread
// see more details in the comment where USE_STUB_IMPLEMENTATION is defined.
return value;
}
- function cloneRecursive<T>(value: T): ShareableRef<T> {
- const type = typeof value;
- if ((type === 'object' || type === 'function') && value !== null) {
- let toAdapt: any;
+ function cloneRecursive<T>(value: T): FlatShareableRef<T> {
+ if (
+ (typeof value === 'object' && value !== null) ||
+ typeof value === 'function'
+ ) {
+ if (isRemoteFunction<T>(value)) {
+ return value.__remoteFunction;
+ }
+ if (isHostObject(value)) {
+ return value as FlatShareableRef<T>;
+ }
if (Array.isArray(value)) {
- toAdapt = value.map((element) => cloneRecursive(element));
- } else if (value !== undefined) {
- toAdapt = {};
- for (const [key, element] of Object.entries(
- value as Record<string, unknown>
- )) {
- toAdapt[key] = cloneRecursive(element);
- }
+ return _makeShareableClone(
+ value.map(cloneRecursive)
+ ) as FlatShareableRef<T>;
+ }
+ const toAdapt: Record<string, FlatShareableRef<T>> = {};
+ for (const [key, element] of Object.entries(value)) {
+ toAdapt[key] = cloneRecursive<T>(element);
}
- return _makeShareableClone(toAdapt);
+ return _makeShareableClone(toAdapt) as FlatShareableRef<T>;
}
return _makeShareableClone(value);
} |
@AlexanderEggers I think I know what's the issue, can you please mark @tjzel Would be nice to include function name in the error message, for instance "Tried to synchronously call a non-worklet function `isHostObject` on the UI thread" (if possible, in a separate PR). Also, what do you think about describing this error on the brand new Troubleshooting page? |
@tomekzaw I tried that already but made no difference. |
@AlexanderEggers Have you also tried marking |
@tomekzaw Adding |
🤔 good idea |
@tomekzaw Do you have an ETA when these would be available in a release? |
This PR will make it to 3.5.0 which we plan to release next week. |
@tomekzaw I did a bit more testing using this implementation and unfortunately these changes are crashing in release builds (on Android and iOS) for some reasons.
|
@AlexanderEggers I didn't get any crash in the reproduction you provided here but I still have a clue what might be the offender. Could you check it with the changes that I now pushed to this branch? If this doesn't help, could you provide your test suite so I could replicate the crash? |
@tjzel Thanks, I had a quick test and that seemed to have fixed the crash for me. I am just curious, do you have an idea why my case would crash because of that incorrect order? |
Yes, it's because of how host objects works, or rather the In debug we have this special object wrapper called Therefore we must firstly check if it's a host object and then if it's a RemoteFunction - we overlooked this in the first place. |
oh, that makes sense. Thanks for taking the time to explain that |
Thanks @tjzel for landing this PR. |
…ive` (#5048) <!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please follow the template so that the reviewers can easily understand what the code changes affect. --> ## Summary #4617 Changed the logic of `makeShareableCloneOnUIRecursive`, detecting if the object is a `HostObject` and skipping its serialization, on the assumption that it's already good to go. This caused crashes in `extractShareableOrThrow` due to how `isHostObject<T>` function works - it not only checks if the object is a `HostObject` but also if it's of type `T`. <img width="631" alt="image" src="https://github.com/software-mansion/react-native-reanimated/assets/40713406/64879db2-fdbb-4d8c-89f3-a37d97535d4f"> Therefore, given a `HostObject` from `react-native`, in this case `ShadowNodeWrapper` on Fabric, it wasn't converted to our `Shareable` type and failed the second check in `extractShareableOrThrow`. Big thanks to @piaskowyk who debugged the most of it 🥳 ## Test plan See that without the change in `makeShareableCloneOnUIRecursive` the new error is thrown. ## Note I have added some (seemingly) unnecessary `HostObject` creation elision, please check if it's relevant to the problem or maybe should be deleted or expanded. --------- Co-authored-by: Tomek Zawadzki <tomasz.zawadzki@swmansion.com>
<!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please follow the template so that the reviewers can easily understand what the code changes affect. --> ## Summary This PR fixes a crash described in #4613 (comment). Turns out that the condition in assert is always true in debug mode but there's an optimization for RemoteFunction in release mode which breaks it. ## Test plan See repro in #4617.
Summary
Fixes #4613.
Work in progress, need to fix types.
Test plan
App.tsx