Fix dropped constant declaration bug
Joe Savona committed
Jun 5, 2023 at 14:08 UTC
66ca1c75cb6d205b88f7761d432a84710f0b1683
7 files changed
+111
compiler/forget/packages/babel-plugin-react-forget/src/Optimization/ConstantPropagation.ts
+21
@@ -81,6 +81,23 @@ export function constantPropagation(fn: HIRFunction): void {
81
}
82
83
function applyConstantPropagation(fn: HIRFunction): boolean {
84
+ // Track the set of identifiers which are used as dependencies for function expressions
85
+ // in order to avoid propagating these constants. This is necessary because the function
86
+ // itself will still reference the original value. If the dependency is propagated but the
87
+ // function still refers to the original value, this can create a situation where DCE
88
+ // will think the original expression is unused. Until we are able to propagate constants
89
+ // into function expression bodies, we disable propagation of function deps.
90
+ const functionDependencies = new Set<IdentifierId>();
91
+ for (const [, block] of fn.body.blocks) {
92
+ for (const instr of block.instructions) {
93
+ if (instr.value.kind === "FunctionExpression") {
94
+ for (const operand of instr.value.dependencies) {
95
+ functionDependencies.add(operand.identifier.id);
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
let hasChanges = false;
102
103
const constants: Constants = new Map();
@@ -120,6 +137,10 @@ function applyConstantPropagation(fn: HIRFunction): boolean {
137
continue;
138
}
139
const instr = block.instructions[i]!;
140
+ // Don't propagate constants used as function expression dependencies
141
+ if (functionDependencies.has(instr.lvalue.identifier.id)) {
142
+ continue;
143
+ }
144
const value = evaluateInstruction(constants, instr);
145
if (value !== null) {
146
constants.set(instr.lvalue.identifier.id, value);
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/const-propagation-into-function-expression-global.expect.md
new
+45
@@ -0,0 +1,45 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ const isX = GLOBAL_IS_X;
7
+ const getJSX = () => {
8
+ <Child x={isX}></Child>;
9
+ };
10
+ const result = getJSX();
11
+ return result;
12
+}
13
+
14
+```
15
+
16
+## Code
17
+
18
+```javascript
19
+import { unstable_useMemoCache as useMemoCache } from "react";
20
+function foo() {
21
+ const $ = useMemoCache(2);
22
+ const isX = GLOBAL_IS_X;
23
+ let t0;
24
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
25
+ t0 = () => {
26
+ <Child x={isX}></Child>;
27
+ };
28
+ $[0] = t0;
29
+ } else {
30
+ t0 = $[0];
31
+ }
32
+ const getJSX = t0;
33
+ let t1;
34
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
35
+ t1 = getJSX();
36
+ $[1] = t1;
37
+ } else {
38
+ t1 = $[1];
39
+ }
40
+ const result = t1;
41
+ return result;
42
+}
43
+
44
+```
45
+
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/const-propagation-into-function-expression-global.js
new
+8
@@ -0,0 +1,8 @@
1
+function foo() {
2
+ const isX = GLOBAL_IS_X;
3
+ const getJSX = () => {
4
+ <Child x={isX}></Child>;
5
+ };
6
+ const result = getJSX();
7
+ return result;
8
+}
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/const-propagation-into-function-expression-primitive.expect.md
new
+29
@@ -0,0 +1,29 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ const x = 42;
7
+ const f = () => {
8
+ console.log(x);
9
+ };
10
+ f();
11
+ return x;
12
+}
13
+
14
+```
15
+
16
+## Code
17
+
18
+```javascript
19
+function foo() {
20
+ const x = 42;
21
+ const f = () => {
22
+ console.log(x);
23
+ };
24
+ f();
25
+ return 42;
26
+}
27
+
28
+```
29
+
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/const-propagation-into-function-expression-primitive.js
new
+8
@@ -0,0 +1,8 @@
1
+function foo() {
2
+ const x = 42;
3
+ const f = () => {
4
+ console.log(x);
5
+ };
6
+ f();
7
+ return x;
8
+}