Skip to content
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 "Maximum call stack size exceeded" error #4367

Merged
merged 6 commits into from
Apr 17, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions src/reanimated2/shareables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export function registerShareableMapping(
_shareableCache.set(shareable, shareableRef || _shareableFlag);
}

function isPlainJSObject(object: object) {
return Object.getPrototypeOf(object) === Object.prototype;
}

// 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
Expand Down Expand Up @@ -75,8 +79,10 @@ const INACCESSIBLE_OBJECT = {
},
};

const ANALYZE_CYCLIC_OBJECT_AT_DEPTH = 30;
let CYCLIC_OBJECT_TEST: any;
const DETECT_CYCLIC_OBJECT_DEPTH_THRESHOLD = 30;
// Below variable stores object that we process in makeShareableCloneRecursive at the specified depth.
// We use it to check if later on the function reenters with the same object
let procesedObjectAtThresholdDepth: any;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let procesedObjectAtThresholdDepth: any;
let processedObjectAtThresholdDepth: any;


export function makeShareableCloneRecursive<T>(
value: any,
Expand All @@ -86,19 +92,21 @@ export function makeShareableCloneRecursive<T>(
if (USE_STUB_IMPLEMENTATION) {
return value;
}
if (depth >= ANALYZE_CYCLIC_OBJECT_AT_DEPTH) {
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 is not supported and cannot be trasferred as shareable so we
// this type of objects are not supported and cannot be trasferred as shareable, so we
// implement a simple detection mechanism that remembers the value at a given depth and
// tests whether we try re-enter this method later on with the same value. If that happens
// tests whether we try reenter this method later on with the same value. If that happens
// we throw an appropriate error.
if (depth === ANALYZE_CYCLIC_OBJECT_AT_DEPTH) {
CYCLIC_OBJECT_TEST = value;
} else if (value === CYCLIC_OBJECT_TEST) {
if (depth === DETECT_CYCLIC_OBJECT_DEPTH_THRESHOLD) {
procesedObjectAtThresholdDepth = value;
} else if (value === procesedObjectAtThresholdDepth) {
throw new Error(
'Trying to convert a cyclic object to a shareable. This is not supported.'
);
}
} else {
procesedObjectAtThresholdDepth = 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;
Expand All @@ -124,10 +132,7 @@ export function makeShareableCloneRecursive<T>(
// 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 (
Object.getPrototypeOf(value) === Object.prototype ||
isTypeFunction
) {
} else if (isPlainJSObject(value) || isTypeFunction) {
toAdapt = {};
if (value.__workletHash !== undefined) {
// we are converting a worklet
Expand All @@ -142,7 +147,8 @@ export function makeShareableCloneRecursive<T>(
// 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.
// serialized/deserialized once. We don't increase depth when calling
// this method as these objects have one level anyways.
toAdapt.__initData = makeShareableCloneRecursive(
value.__initData,
true
Expand Down