@samitouri / QOS-React-1 / commits / 75c7fdcbd0

MutableIfOperandsAreMutable flag handles mutation via capturing

There was one missing piece to the optimization from the previous PR: Array#map can return an alias to the receiver in its output, which means that mutations of the result have to be treated as mutations of the receiver. This means we need to use a Capture effect on the receiver. If that doesn't get downgraded to a Read bc the value was immutable, we then also need to make the lvalue effect a Store (so that InferMutableRanges actually looks at it for aliasing).

Joe Savona committed Nov 29, 2023 at 10:46 UTC 75c7fdcbd0e73a846899e69b13062c82f0fac468
3 files changed +70 -3
compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts
+6 -3
@@ -851,16 +851,19 @@ function inferBlock(
851 ) {
852 /*
853 * None of the args are mutable or mutate their params, we can downgrade to
854 - * treating as all reads
854 + * treating as all reads (except that the receiver may be captured)
855 */
856 for (const arg of instrValue.args) {
857 const place = arg.kind === "Identifier" ? arg : arg.place;
858 state.reference(place, Effect.Read);
859 }
860 - state.reference(instrValue.receiver, Effect.Read);
860 + state.reference(instrValue.receiver, Effect.Capture);
861 state.initialize(instrValue, signature.returnValueKind);
862 state.define(instr.lvalue, instrValue);
863 - instr.lvalue.effect = Effect.ConditionallyMutate;
863 + instr.lvalue.effect =
864 + instrValue.receiver.effect === Effect.Capture
865 + ? Effect.Store
866 + : Effect.ConditionallyMutate;
867 continue;
868 }
869
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/array-map-mutable-array-non-mutating-lambda-mutated-result.expect.md new
+50
@@ -0,0 +1,50 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const x = [{}];
7 + const y = x.map((item) => {
8 + return item;
9 + });
10 + y[0].flag = true;
11 + return [x, y];
12 +}
13 +
14 +export const FIXTURE_ENTRYPOINT = {
15 + fn: Component,
16 + params: [{}],
17 + isComponent: false,
18 +};
19 +
20 +```
21 +
22 +## Code
23 +
24 +```javascript
25 +import { unstable_useMemoCache as useMemoCache } from "react";
26 +function Component(props) {
27 + const $ = useMemoCache(1);
28 + let t0;
29 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30 + const x = [{}];
31 + const y = x.map((item) => item);
32 + y[0].flag = true;
33 + t0 = [x, y];
34 + $[0] = t0;
35 + } else {
36 + t0 = $[0];
37 + }
38 + return t0;
39 +}
40 +
41 +export const FIXTURE_ENTRYPOINT = {
42 + fn: Component,
43 + params: [{}],
44 + isComponent: false,
45 +};
46 +
47 +```
48 +
49 +### Eval output
50 +(kind: ok) [[{"flag":true}],["[[ cyclic ref *2 ]]"]]
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/array-map-mutable-array-non-mutating-lambda-mutated-result.js new
+14
@@ -0,0 +1,14 @@
1 +function Component(props) {
2 + const x = [{}];
3 + const y = x.map((item) => {
4 + return item;
5 + });
6 + y[0].flag = true;
7 + return [x, y];
8 +}
9 +
10 +export const FIXTURE_ENTRYPOINT = {
11 + fn: Component,
12 + params: [{}],
13 + isComponent: false,
14 +};