@samitouri / QOS-React / commits / dd1a021bad

[compiler][ez] Patch hoistability for ObjectMethods (#31197)

Extends #31066 to ObjectMethods (somehow missed this before). ' --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31197). * #31204 * #31202 * #31203 * #31201 * #31200 * #31346 * #31199 * #31431 * #31345 * __->__ #31197

mofeiZ committed Nov 5, 2024 at 15:25 UTC dd1a021bad0908d3ad7ba4314ca9a39173d7758f
3 files changed +114 -2
compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts
+6 -2
@@ -348,7 +348,8 @@ function collectNonNullsInBlocks(
348 assumedNonNullObjects.add(maybeNonNull);
349 }
350 if (
351 - instr.value.kind === 'FunctionExpression' &&
351 + (instr.value.kind === 'FunctionExpression' ||
352 + instr.value.kind === 'ObjectMethod') &&
353 !fn.env.config.enableTreatFunctionDepsAsConditional
354 ) {
355 const innerFn = instr.value.loweredFunc;
@@ -591,7 +592,10 @@ function collectFunctionExpressionFakeLoads(
592
593 for (const [_, block] of fn.body.blocks) {
594 for (const {lvalue, value} of block.instructions) {
594 - if (value.kind === 'FunctionExpression') {
595 + if (
596 + value.kind === 'FunctionExpression' ||
597 + value.kind === 'ObjectMethod'
598 + ) {
599 for (const reference of value.loweredFunc.dependencies) {
600 let curr: IdentifierId | undefined = reference.identifier.id;
601 while (curr != null) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-objectmethod-cond-access.expect.md new
+82
@@ -0,0 +1,82 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @enablePropagateDepsInHIR
6 +import {Stringify} from 'shared-runtime';
7 +
8 +function Foo({a, shouldReadA}) {
9 + return (
10 + <Stringify
11 + objectMethod={{
12 + method() {
13 + if (shouldReadA) return a.b.c;
14 + return null;
15 + },
16 + }}
17 + shouldInvokeFns={true}
18 + />
19 + );
20 +}
21 +
22 +export const FIXTURE_ENTRYPOINT = {
23 + fn: Foo,
24 + params: [{a: null, shouldReadA: true}],
25 + sequentialRenders: [
26 + {a: null, shouldReadA: true},
27 + {a: null, shouldReadA: false},
28 + {a: {b: {c: 4}}, shouldReadA: true},
29 + ],
30 +};
31 +
32 +```
33 +
34 +## Code
35 +
36 +```javascript
37 +import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
38 +import { Stringify } from "shared-runtime";
39 +
40 +function Foo(t0) {
41 + const $ = _c(3);
42 + const { a, shouldReadA } = t0;
43 + let t1;
44 + if ($[0] !== shouldReadA || $[1] !== a) {
45 + t1 = (
46 + <Stringify
47 + objectMethod={{
48 + method() {
49 + if (shouldReadA) {
50 + return a.b.c;
51 + }
52 + return null;
53 + },
54 + }}
55 + shouldInvokeFns={true}
56 + />
57 + );
58 + $[0] = shouldReadA;
59 + $[1] = a;
60 + $[2] = t1;
61 + } else {
62 + t1 = $[2];
63 + }
64 + return t1;
65 +}
66 +
67 +export const FIXTURE_ENTRYPOINT = {
68 + fn: Foo,
69 + params: [{ a: null, shouldReadA: true }],
70 + sequentialRenders: [
71 + { a: null, shouldReadA: true },
72 + { a: null, shouldReadA: false },
73 + { a: { b: { c: 4 } }, shouldReadA: true },
74 + ],
75 +};
76 +
77 +```
78 +
79 +### Eval output
80 +(kind: ok) [[ (exception in render) TypeError: Cannot read properties of null (reading 'b') ]]
81 +<div>{"objectMethod":{"method":{"kind":"Function","result":null}},"shouldInvokeFns":true}</div>
82 +<div>{"objectMethod":{"method":{"kind":"Function","result":4}},"shouldInvokeFns":true}</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/infer-objectmethod-cond-access.js new
+26
@@ -0,0 +1,26 @@
1 +// @enablePropagateDepsInHIR
2 +import {Stringify} from 'shared-runtime';
3 +
4 +function Foo({a, shouldReadA}) {
5 + return (
6 + <Stringify
7 + objectMethod={{
8 + method() {
9 + if (shouldReadA) return a.b.c;
10 + return null;
11 + },
12 + }}
13 + shouldInvokeFns={true}
14 + />
15 + );
16 +}
17 +
18 +export const FIXTURE_ENTRYPOINT = {
19 + fn: Foo,
20 + params: [{a: null, shouldReadA: true}],
21 + sequentialRenders: [
22 + {a: null, shouldReadA: true},
23 + {a: null, shouldReadA: false},
24 + {a: {b: {c: 4}}, shouldReadA: true},
25 + ],
26 +};