@samitouri / QOS-React-1 / commits / fe91bcefd2

Fix promotion of catch bindings w/in function expressions

One of our visitors wasn't visiting TryTerminal's handlerBinding, which meant that we missed renaming those identifiers in RenameVariables. I also updated the printers to print this binding.

Joe Savona committed Mar 7, 2024 at 09:21 UTC fe91bcefd2bbc3da9f1998befb466acc8b4afdca
13 files changed +330 -29
compiler/packages/babel-plugin-react-forget/src/HIR/PrintHIR.ts
+4 -2
@@ -266,8 +266,10 @@ export function printTerminal(terminal: Terminal): Array<string> | string {
266 break;
267 }
268 case "try": {
269 - value = `Try block=bb${terminal.block} catch=bb${
270 - terminal.handler
269 + value = `Try block=bb${terminal.block} handler=bb${terminal.handler}${
270 + terminal.handlerBinding !== null
271 + ? ` handlerBinding=(${printPlace(terminal.handlerBinding)})`
272 + : ""
273 } fallthrough=${
274 terminal.fallthrough != null ? `bb${terminal.fallthrough}` : ""
275 }`;
compiler/packages/babel-plugin-react-forget/src/HIR/visitors.ts
+6 -1
@@ -1126,7 +1126,12 @@ export function* eachTerminalOperand(terminal: Terminal): Iterable<Place> {
1126 yield terminal.value;
1127 break;
1128 }
1129 - case "try":
1129 + case "try": {
1130 + if (terminal.handlerBinding !== null) {
1131 + yield terminal.handlerBinding;
1132 + }
1133 + break;
1134 + }
1135 case "maybe-throw":
1136 case "sequence":
1137 case "label":
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PrintReactiveFunction.ts
+6 -1
@@ -354,7 +354,12 @@ function writeTerminal(writer: Writer, terminal: ReactiveTerminal): void {
354 case "try": {
355 writer.writeLine(`[${terminal.id}] try {`);
356 writeReactiveInstructions(writer, terminal.block);
357 - writer.writeLine(`} catch {`);
357 + writer.write(`} catch `);
358 + if (terminal.handlerBinding !== null) {
359 + writer.writeLine(`(${printPlace(terminal.handlerBinding)}) {`);
360 + } else {
361 + writer.writeLine(`{`);
362 + }
363 writeReactiveInstructions(writer, terminal.handler);
364 writer.writeLine("}");
365 break;
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/visitors.ts
+3
@@ -545,6 +545,9 @@ export class ReactiveFunctionTransform<
545 }
546 case "try": {
547 this.visitBlock(terminal.block, state);
548 + if (terminal.handlerBinding !== null) {
549 + this.visitPlace(terminal.id, terminal.handlerBinding, state);
550 + }
551 this.visitBlock(terminal.handler, state);
552 break;
553 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-try-catch-within-function-expression.expect.md deleted
-25
@@ -1,25 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -function Component(props) {
6 - const callback = () => {
7 - try {
8 - return [];
9 - } catch (e) {
10 - return;
11 - }
12 - };
13 - return callback();
14 -}
15 -
16 -```
17 -
18 -
19 -## Error
20 -
21 -```
22 -[ReactForget] Invariant: Expected temporaries to be promoted to named identifiers in an earlier pass. identifier 16 is unnamed
23 -```
24 -
25 -
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-within-function-expression-returns-caught-value.expect.md new
+68
@@ -0,0 +1,68 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { throwInput } from "shared-runtime";
6 +
7 +function Component(props) {
8 + const callback = () => {
9 + try {
10 + throwInput([props.value]);
11 + } catch (e) {
12 + return e;
13 + }
14 + };
15 + return callback();
16 +}
17 +
18 +export const FIXTURE_ENTRYPOINT = {
19 + fn: Component,
20 + params: [{ value: 42 }],
21 +};
22 +
23 +```
24 +
25 +## Code
26 +
27 +```javascript
28 +import { unstable_useMemoCache as useMemoCache } from "react";
29 +import { throwInput } from "shared-runtime";
30 +
31 +function Component(props) {
32 + const $ = useMemoCache(4);
33 + let t0;
34 + if ($[0] !== props.value) {
35 + t0 = () => {
36 + try {
37 + throwInput([props.value]);
38 + } catch (t1) {
39 + const e = t1;
40 + return e;
41 + }
42 + };
43 + $[0] = props.value;
44 + $[1] = t0;
45 + } else {
46 + t0 = $[1];
47 + }
48 + const callback = t0;
49 + let t1;
50 + if ($[2] !== callback) {
51 + t1 = callback();
52 + $[2] = callback;
53 + $[3] = t1;
54 + } else {
55 + t1 = $[3];
56 + }
57 + return t1;
58 +}
59 +
60 +export const FIXTURE_ENTRYPOINT = {
61 + fn: Component,
62 + params: [{ value: 42 }],
63 +};
64 +
65 +```
66 +
67 +### Eval output
68 +(kind: ok) [42]
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-within-function-expression-returns-caught-value.js new
+17
@@ -0,0 +1,17 @@
1 +import { throwInput } from "shared-runtime";
2 +
3 +function Component(props) {
4 + const callback = () => {
5 + try {
6 + throwInput([props.value]);
7 + } catch (e) {
8 + return e;
9 + }
10 + };
11 + return callback();
12 +}
13 +
14 +export const FIXTURE_ENTRYPOINT = {
15 + fn: Component,
16 + params: [{ value: 42 }],
17 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-within-function-expression.expect.md new
+61
@@ -0,0 +1,61 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const callback = () => {
7 + try {
8 + return [];
9 + } catch (e) {
10 + return;
11 + }
12 + };
13 + return callback();
14 +}
15 +
16 +export const FIXTURE_ENTRYPOINT = {
17 + fn: Component,
18 + params: [{}],
19 +};
20 +
21 +```
22 +
23 +## Code
24 +
25 +```javascript
26 +import { unstable_useMemoCache as useMemoCache } from "react";
27 +function Component(props) {
28 + const $ = useMemoCache(2);
29 + let t0;
30 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
31 + t0 = () => {
32 + try {
33 + return [];
34 + } catch (t1) {
35 + return;
36 + }
37 + };
38 + $[0] = t0;
39 + } else {
40 + t0 = $[0];
41 + }
42 + const callback = t0;
43 + let t1;
44 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
45 + t1 = callback();
46 + $[1] = t1;
47 + } else {
48 + t1 = $[1];
49 + }
50 + return t1;
51 +}
52 +
53 +export const FIXTURE_ENTRYPOINT = {
54 + fn: Component,
55 + params: [{}],
56 +};
57 +
58 +```
59 +
60 +### Eval output
61 +(kind: ok) []
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-within-function-expression.js renamed
+5
@@ -8,3 +8,8 @@ function Component(props) {
8 };
9 return callback();
10 }
11 +
12 +export const FIXTURE_ENTRYPOINT = {
13 + fn: Component,
14 + params: [{}],
15 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-within-object-method-returns-caught-value.expect.md new
+65
@@ -0,0 +1,65 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { throwInput } from "shared-runtime";
6 +
7 +function Component(props) {
8 + const object = {
9 + foo() {
10 + try {
11 + throwInput([props.value]);
12 + } catch (e) {
13 + return e;
14 + }
15 + },
16 + };
17 + return object.foo();
18 +}
19 +
20 +export const FIXTURE_ENTRYPOINT = {
21 + fn: Component,
22 + params: [{ value: 42 }],
23 +};
24 +
25 +```
26 +
27 +## Code
28 +
29 +```javascript
30 +import { unstable_useMemoCache as useMemoCache } from "react";
31 +import { throwInput } from "shared-runtime";
32 +
33 +function Component(props) {
34 + const $ = useMemoCache(2);
35 + let t0;
36 + if ($[0] !== props.value) {
37 + const object = {
38 + foo() {
39 + try {
40 + throwInput([props.value]);
41 + } catch (t1) {
42 + const e = t1;
43 + return e;
44 + }
45 + },
46 + };
47 +
48 + t0 = object.foo();
49 + $[0] = props.value;
50 + $[1] = t0;
51 + } else {
52 + t0 = $[1];
53 + }
54 + return t0;
55 +}
56 +
57 +export const FIXTURE_ENTRYPOINT = {
58 + fn: Component,
59 + params: [{ value: 42 }],
60 +};
61 +
62 +```
63 +
64 +### Eval output
65 +(kind: ok) [42]
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-within-object-method-returns-caught-value.js new
+19
@@ -0,0 +1,19 @@
1 +import { throwInput } from "shared-runtime";
2 +
3 +function Component(props) {
4 + const object = {
5 + foo() {
6 + try {
7 + throwInput([props.value]);
8 + } catch (e) {
9 + return e;
10 + }
11 + },
12 + };
13 + return object.foo();
14 +}
15 +
16 +export const FIXTURE_ENTRYPOINT = {
17 + fn: Component,
18 + params: [{ value: 42 }],
19 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-within-object-method.expect.md new
+59
@@ -0,0 +1,59 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const object = {
7 + foo() {
8 + try {
9 + return [];
10 + } catch (e) {
11 + return;
12 + }
13 + },
14 + };
15 + return object.foo();
16 +}
17 +
18 +export const FIXTURE_ENTRYPOINT = {
19 + fn: Component,
20 + params: [{}],
21 +};
22 +
23 +```
24 +
25 +## Code
26 +
27 +```javascript
28 +import { unstable_useMemoCache as useMemoCache } from "react";
29 +function Component(props) {
30 + const $ = useMemoCache(1);
31 + let t0;
32 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
33 + const object = {
34 + foo() {
35 + try {
36 + return [];
37 + } catch (t1) {
38 + return;
39 + }
40 + },
41 + };
42 +
43 + t0 = object.foo();
44 + $[0] = t0;
45 + } else {
46 + t0 = $[0];
47 + }
48 + return t0;
49 +}
50 +
51 +export const FIXTURE_ENTRYPOINT = {
52 + fn: Component,
53 + params: [{}],
54 +};
55 +
56 +```
57 +
58 +### Eval output
59 +(kind: ok) []
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/try-catch-within-object-method.js new
+17
@@ -0,0 +1,17 @@
1 +function Component(props) {
2 + const object = {
3 + foo() {
4 + try {
5 + return [];
6 + } catch (e) {
7 + return;
8 + }
9 + },
10 + };
11 + return object.foo();
12 +}
13 +
14 +export const FIXTURE_ENTRYPOINT = {
15 + fn: Component,
16 + params: [{}],
17 +};