Fix LeaveSSA missing param declarations
Nice find from @mofeiz, we weren't adding declarations for function params in LeaveSSA. This caused us to hit an invariant for update expressions that updated params which assumed that all named variables had a declaration. This is why it's helpful to add invariants, it helps you find places where you violate them :-)
Joe Savona committed
Aug 14, 2023 at 15:48 UTC
45d884bb6b443bc8c34633e32916791236d34e7b
4 files changed
+97
-2
compiler/forget/packages/babel-plugin-react-forget/src/ReactiveScopes/PruneNonEscapingScopes.ts
+14
-2
@@ -430,8 +430,6 @@ function computeMemoizationInputs(
430
rvalues: value.children,
431
};
432
}
433
- case "PrefixUpdate":
434
- case "PostfixUpdate":
433
case "Debugger":
434
case "ComputedDelete":
435
case "PropertyDelete":
@@ -507,6 +505,20 @@ function computeMemoizationInputs(
505
rvalues: [],
506
};
507
}
508
+ case "PrefixUpdate":
509
+ case "PostfixUpdate": {
510
+ const lvalues = [
511
+ { place: value.lvalue, level: MemoizationLevel.Conditional },
512
+ ];
513
+ if (lvalue !== null) {
514
+ lvalues.push({ place: lvalue, level: MemoizationLevel.Conditional });
515
+ }
516
+ return {
517
+ // Indirection for the inner value, memoized if the value is
518
+ lvalues,
519
+ rvalues: [value.value],
520
+ };
521
+ }
522
case "StoreLocal": {
523
const lvalues = [
524
{ place: value.lvalue.place, level: MemoizationLevel.Conditional },
compiler/forget/packages/babel-plugin-react-forget/src/SSA/LeaveSSA.ts
+12
@@ -99,6 +99,18 @@ export function leaveSSA(fn: HIRFunction): void {
99
{ lvalue: LValue | LValuePattern; place: Place }
100
> = new Map();
101
102
+ for (const param of fn.params) {
103
+ if (param.identifier.name !== null) {
104
+ declarations.set(param.identifier.name, {
105
+ lvalue: {
106
+ kind: InstructionKind.Let,
107
+ place: param,
108
+ },
109
+ place: param,
110
+ });
111
+ }
112
+ }
113
+
114
// For non-memoizable phis, this maps original identifiers to the identifier they should be
115
// *rewritten* to. The keys are the original identifiers, and the value will be _either_ the
116
// phi id or, more typically, the operand that was defined prior to the phi.
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/update-expression-on-function-parameter.expect.md
new
+61
@@ -0,0 +1,61 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @debug
6
+function Component(a, [b], { c }) {
7
+ let d = a++;
8
+ let e = ++a;
9
+ let f = b--;
10
+ let g = --b;
11
+ let h = c++;
12
+ let i = --c;
13
+ return [a, b, c, d, e, f, g, h, i];
14
+}
15
+
16
+```
17
+
18
+## Code
19
+
20
+```javascript
21
+import { unstable_useMemoCache as useMemoCache } from "react"; // @debug
22
+function Component(a, t37, t38) {
23
+ const $ = useMemoCache(10);
24
+ let [b] = t37;
25
+ let { c } = t38;
26
+ const d = a++;
27
+ const e = ++a;
28
+ const f = b--;
29
+ const g = --b;
30
+ const h = c++;
31
+ const i = --c;
32
+ const c_0 = $[0] !== a;
33
+ const c_1 = $[1] !== b;
34
+ const c_2 = $[2] !== c;
35
+ const c_3 = $[3] !== d;
36
+ const c_4 = $[4] !== e;
37
+ const c_5 = $[5] !== f;
38
+ const c_6 = $[6] !== g;
39
+ const c_7 = $[7] !== h;
40
+ const c_8 = $[8] !== i;
41
+ let t0;
42
+ if (c_0 || c_1 || c_2 || c_3 || c_4 || c_5 || c_6 || c_7 || c_8) {
43
+ t0 = [a, b, c, d, e, f, g, h, i];
44
+ $[0] = a;
45
+ $[1] = b;
46
+ $[2] = c;
47
+ $[3] = d;
48
+ $[4] = e;
49
+ $[5] = f;
50
+ $[6] = g;
51
+ $[7] = h;
52
+ $[8] = i;
53
+ $[9] = t0;
54
+ } else {
55
+ t0 = $[9];
56
+ }
57
+ return t0;
58
+}
59
+
60
+```
61
+
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/update-expression-on-function-parameter.js
new
+10
@@ -0,0 +1,10 @@
1
+// @debug
2
+function Component(a, [b], { c }) {
3
+ let d = a++;
4
+ let e = ++a;
5
+ let f = b--;
6
+ let g = --b;
7
+ let h = c++;
8
+ let i = --c;
9
+ return [a, b, c, d, e, f, g, h, i];
10
+}