[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
+};