Skip to content

Commit 6519571

Browse files
tyao1facebook-github-bot
authored andcommitted
Shallow freeze resolver value
Reviewed By: alunyov Differential Revision: D51049743 fbshipit-source-id: 5c8926e84e3eb355d4eb5972dbcb556d8a6fa022
1 parent 7f54255 commit 6519571

File tree

5 files changed

+42
-4
lines changed

5 files changed

+42
-4
lines changed

packages/react-relay/__tests__/RelayResolverModel-test.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,15 @@ function logFn(event: LogEvent): void {
5858
beforeEach(() => {
5959
RelayFeatureFlags.ENABLE_RELAY_RESOLVERS = true;
6060
RelayFeatureFlags.ENABLE_CLIENT_EDGES = true;
61+
RelayFeatureFlags.ENABLE_SHALLOW_FREEZE_RESOLVER_VALUES = true;
6162
logEvents = [];
6263
resetStore(logFn);
6364
});
6465

6566
afterEach(() => {
6667
RelayFeatureFlags.ENABLE_RELAY_RESOLVERS = false;
6768
RelayFeatureFlags.ENABLE_CLIENT_EDGES = false;
69+
RelayFeatureFlags.ENABLE_SHALLOW_FREEZE_RESOLVER_VALUES = false;
6870
});
6971

7072
function createEnvironment() {
@@ -539,8 +541,7 @@ describe.each([
539541
setIsHuman(true);
540542
jest.runAllImmediates();
541543
});
542-
// TODO: Should be 0. Relay should not mutate the value here.
543-
expect(renderer.toJSON()).toEqual('human:100');
544+
expect(renderer.toJSON()).toEqual('human:0');
544545

545546
TestRenderer.act(() => {
546547
renderer.unmount();
@@ -568,10 +569,9 @@ describe.each([
568569
);
569570
expect(renderer.toJSON()).toEqual('robot:0');
570571

571-
// TODO: should not throw on mutating the inner of the resolver value
572572
expect(() => {
573573
chargeBattery();
574-
}).toThrow();
574+
}).not.toThrow();
575575

576576
TestRenderer.act(() => {
577577
renderer.unmount();

packages/relay-runtime/store/ResolverCache.js

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import type {
2626

2727
const recycleNodesInto = require('../util/recycleNodesInto');
2828
const {RELAY_LIVE_RESOLVER} = require('../util/RelayConcreteNode');
29+
const RelayFeatureFlags = require('../util/RelayFeatureFlags');
30+
const shallowFreeze = require('../util/shallowFreeze');
2931
const {generateClientID} = require('./ClientID');
3032
const RelayModernRecord = require('./RelayModernRecord');
3133
const {
@@ -172,6 +174,9 @@ class RecordResolverCache implements ResolverCache {
172174
linkedRecord = RelayModernRecord.create(linkedID, '__RELAY_RESOLVER__');
173175

174176
const evaluationResult = evaluate();
177+
if (RelayFeatureFlags.ENABLE_SHALLOW_FREEZE_RESOLVER_VALUES) {
178+
shallowFreeze(evaluationResult.resolverResult);
179+
}
175180
RelayModernRecord.setValue(
176181
linkedRecord,
177182
RELAY_RESOLVER_VALUE_KEY,

packages/relay-runtime/store/experimental-live-resolvers/LiveResolverCache.js

+8
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import type {LiveState} from 'relay-runtime';
3636

3737
const recycleNodesInto = require('../../util/recycleNodesInto');
3838
const {RELAY_LIVE_RESOLVER} = require('../../util/RelayConcreteNode');
39+
const RelayFeatureFlags = require('../../util/RelayFeatureFlags');
40+
const shallowFreeze = require('../../util/shallowFreeze');
3941
const {generateClientID, generateClientObjectClientID} = require('../ClientID');
4042
const RelayModernRecord = require('../RelayModernRecord');
4143
const {createNormalizationSelector} = require('../RelayModernSelector');
@@ -620,12 +622,18 @@ class LiveResolverCache implements ResolverCache {
620622
nextOutputTypeRecordIDs,
621623
);
622624

625+
if (RelayFeatureFlags.ENABLE_SHALLOW_FREEZE_RESOLVER_VALUES) {
626+
shallowFreeze(resolverValue);
627+
}
623628
RelayModernRecord.setValue(
624629
resolverRecord,
625630
RELAY_RESOLVER_VALUE_KEY,
626631
resolverValue,
627632
);
628633
} else {
634+
if (RelayFeatureFlags.ENABLE_SHALLOW_FREEZE_RESOLVER_VALUES) {
635+
shallowFreeze(value);
636+
}
629637
// For "classic" resolvers (or if the value is nullish), we are just setting their
630638
// value as is.
631639
RelayModernRecord.setValue(

packages/relay-runtime/util/RelayFeatureFlags.js

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export type FeatureFlags = {
2828
STRING_INTERN_LEVEL: number,
2929
LOG_MISSING_RECORDS_IN_PROD: boolean,
3030
ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE: boolean,
31+
ENABLE_SHALLOW_FREEZE_RESOLVER_VALUES: boolean,
3132

3233
// Configure RelayStoreSubscriptions to mark a subscription as affected by an
3334
// update if there are any overlapping IDs other than ROOT_ID or VIWER_ID,
@@ -63,6 +64,7 @@ const RelayFeatureFlags: FeatureFlags = {
6364
ENABLE_OPERATION_TRACKER_OPTIMISTIC_UPDATES: false,
6465
ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE: false,
6566
ENABLE_FIELD_ERROR_HANDLING: false,
67+
ENABLE_SHALLOW_FREEZE_RESOLVER_VALUES: false,
6668
};
6769

6870
module.exports = RelayFeatureFlags;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
* @format
9+
* @oncall relay
10+
*/
11+
12+
'use strict';
13+
14+
// Shallow freeze to prevent Relay from mutating the value in recycleNodesInto or deepFreezing the value
15+
module.exports = function shallowFreeze(value: mixed) {
16+
if (
17+
typeof value === 'object' &&
18+
value != null &&
19+
(Array.isArray(value) || value.constructor === Object)
20+
) {
21+
Object.freeze(value);
22+
}
23+
};

0 commit comments

Comments
 (0)