Files
react-native/packages/react-native-fantom/runtime/patchWeakRef.js
T
Rubén Norte b12ab7a3d4 Enforce that WeakRef.deref() is always called within the event loop (#50257)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50257

Changelog: [internal]

## Context

We have some tests that make sure certain objects are deallocated/released at the right times, but those are generally hard to get right. The main reason is that WeakRefs semantics are tied to the tasks and microtasks in JS, but we handle them both inside and outside the Event Loop in Fantom tests.

This leads to some surprising behavior where things we expect to have been deallocated weren't because of some innocent looking code.

## Changes

This introduces a safety mechanism in Fantom to enforce that WeakRefs are always dereferenced inside the Event Loop, by patching the method in `WeakRef` and checking if we're in the Event Loop using Fantom APIs.

It also updates the existing test using WeakRefs to fix the new errors thrown by this patch, and to serve as a "good example" on how to use WeakRefs to do memory testing.

Reviewed By: yungsters

Differential Revision: D71815397

fbshipit-source-id: 8faab1898d9112ec365b41867179abb8b251e337
2025-03-25 17:09:18 -07:00

56 lines
1.5 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import * as Fantom from '@react-native/fantom';
let initialized = false;
/**
* This method modifies the built-in `WeakRef.prototype.deref` method to make
* sure it is always called within the Event Loop in Fantom tests.
*
* Calling it outside the Event Loop can lead to inconsistent and confusing
* behavior as WeakRefs semantics are tied to the task + microtasks lifecycle.
*/
export default function patchWeakRef(): void {
if (initialized) {
return;
}
initialized = true;
// $FlowExpectedError[method-unbinding]
const originalDeref = WeakRef.prototype.deref;
// $FlowExpectedError[cannot-write]
WeakRef.prototype.deref = function patchedDeref<T>(this: WeakRef<T>): T {
if (!Fantom.isInWorkLoop()) {
throw new Error(
'Unexpected call to `WeakRef.deref()` outside of the Event Loop. Please use this method within `Fantom.runTask()`.',
);
}
return originalDeref.call(this);
};
const OriginalWeakRef = WeakRef;
global.WeakRef = function WeakRef(...args) {
if (!Fantom.isInWorkLoop()) {
throw new Error(
'Unexpected instantiation of `WeakRef` outside of the Event Loop. Please create the instance within `Fantom.runTask()`.',
);
}
return new OriginalWeakRef(...args);
};
}