Support AssignmentPattern (default values in destructuring)
"Supports" default values in destructuring (AssignmentPattern) by lowering to a ternary, even in the output. Examples: ```javascript // Input: const [x = 'default'] = y; // Output: const [t0] = y; const x = t0 === undefined ? 'default' : t0; ``` ```javascript // Input 2 const [{x} = makeObject()] = y; // Output 2 const [t0] = y; const {x} = t0 === undefined ? makeObject() : t0; ``` Note that this is how Babel lowers AssignmentPattern, so it isn't too bad. This should help avoid the need to update product code, even if the output isn't perfectly ideal.
Joe Savona committed
Mar 27, 2023 at 10:34 UTC
80bdab74470d237883d5c88fba3baafb3b0d447a
8 files changed
+223
-16
compiler/forget/src/HIR/BuildHIR.ts
+100
@@ -2446,6 +2446,106 @@ function lowerAssignment(
2446
}
2447
return { kind: "LoadLocal", place: temporary, loc: value.loc };
2448
}
2449
+ case "AssignmentPattern": {
2450
+ const lvalue = lvaluePath as NodePath<t.AssignmentPattern>;
2451
+ const loc = lvalue.node.loc ?? GeneratedSource;
2452
+ const temp = buildTemporaryPlace(builder, loc);
2453
+
2454
+ const testBlock = builder.reserve("value");
2455
+ const continuationBlock = builder.reserve(builder.currentBlockKind());
2456
+
2457
+ const consequent = builder.enter("value", () => {
2458
+ const defaultValue = lowerExpressionToTemporary(
2459
+ builder,
2460
+ lvalue.get("right")
2461
+ );
2462
+ builder.push({
2463
+ id: makeInstructionId(0),
2464
+ lvalue: buildTemporaryPlace(builder, loc),
2465
+ value: {
2466
+ kind: "StoreLocal",
2467
+ lvalue: { kind: InstructionKind.Const, place: { ...temp } },
2468
+ value: { ...defaultValue },
2469
+ loc,
2470
+ },
2471
+ loc,
2472
+ });
2473
+ return {
2474
+ kind: "goto",
2475
+ variant: GotoVariant.Break,
2476
+ block: continuationBlock.id,
2477
+ id: makeInstructionId(0),
2478
+ loc,
2479
+ };
2480
+ });
2481
+
2482
+ const alternate = builder.enter("value", () => {
2483
+ builder.push({
2484
+ id: makeInstructionId(0),
2485
+ lvalue: buildTemporaryPlace(builder, loc),
2486
+ value: {
2487
+ kind: "StoreLocal",
2488
+ lvalue: { kind: InstructionKind.Const, place: { ...temp } },
2489
+ value: { ...value },
2490
+ loc,
2491
+ },
2492
+ loc,
2493
+ });
2494
+ return {
2495
+ kind: "goto",
2496
+ variant: GotoVariant.Break,
2497
+ block: continuationBlock.id,
2498
+ id: makeInstructionId(0),
2499
+ loc,
2500
+ };
2501
+ });
2502
+ builder.terminateWithContinuation(
2503
+ {
2504
+ kind: "ternary",
2505
+ test: testBlock.id,
2506
+ fallthrough: continuationBlock.id,
2507
+ id: makeInstructionId(0),
2508
+ loc,
2509
+ },
2510
+ testBlock
2511
+ );
2512
+ const undef = buildTemporaryPlace(builder, loc);
2513
+ builder.push({
2514
+ id: makeInstructionId(0),
2515
+ lvalue: { ...undef },
2516
+ value: {
2517
+ kind: "Primitive",
2518
+ value: undefined,
2519
+ loc,
2520
+ },
2521
+ loc,
2522
+ });
2523
+ const test = buildTemporaryPlace(builder, loc);
2524
+ builder.push({
2525
+ id: makeInstructionId(0),
2526
+ lvalue: { ...test },
2527
+ value: {
2528
+ kind: "BinaryExpression",
2529
+ left: { ...value },
2530
+ operator: "===",
2531
+ right: { ...undef },
2532
+ loc,
2533
+ },
2534
+ loc,
2535
+ });
2536
+ builder.terminateWithContinuation(
2537
+ {
2538
+ kind: "branch",
2539
+ test: { ...test },
2540
+ consequent,
2541
+ alternate,
2542
+ id: makeInstructionId(0),
2543
+ },
2544
+ continuationBlock
2545
+ );
2546
+
2547
+ return lowerAssignment(builder, loc, kind, lvalue.get("left"), temp);
2548
+ }
2549
default: {
2550
builder.errors.push({
2551
reason: `(BuildHIR::lowerAssignment) Handle ${lvaluePath.type} assignments`,
compiler/forget/src/__tests__/fixtures/compiler/destructuring-array-default.expect.md
new
+32
@@ -0,0 +1,32 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ const [[x] = [foo()]] = props.y;
7
+ return x;
8
+}
9
+
10
+```
11
+
12
+## Code
13
+
14
+```javascript
15
+function Component(props) {
16
+ const $ = React.unstable_useMemoCache(2);
17
+ const [t0] = props.y;
18
+ const c_0 = $[0] !== t0;
19
+ let t1;
20
+ if (c_0) {
21
+ t1 = t0 === undefined ? [foo()] : t0;
22
+ $[0] = t0;
23
+ $[1] = t1;
24
+ } else {
25
+ t1 = $[1];
26
+ }
27
+ const [x] = t1;
28
+ return x;
29
+}
30
+
31
+```
32
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/destructuring-array-default.js
new
+4
@@ -0,0 +1,4 @@
1
+function Component(props) {
2
+ const [[x] = [foo()]] = props.y;
3
+ return x;
4
+}
compiler/forget/src/__tests__/fixtures/compiler/destructuring-assignment-array-default.expect.md
new
+42
@@ -0,0 +1,42 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ let x;
7
+ if (props.cond) {
8
+ [[x] = [foo()]] = props.y;
9
+ } else {
10
+ x = props.fallback;
11
+ }
12
+ return x;
13
+}
14
+
15
+```
16
+
17
+## Code
18
+
19
+```javascript
20
+function Component(props) {
21
+ const $ = React.unstable_useMemoCache(2);
22
+ let x = undefined;
23
+ if (props.cond) {
24
+ const [t0] = props.y;
25
+ const c_0 = $[0] !== t0;
26
+ let t1;
27
+ if (c_0) {
28
+ t1 = t0 === undefined ? [foo()] : t0;
29
+ $[0] = t0;
30
+ $[1] = t1;
31
+ } else {
32
+ t1 = $[1];
33
+ }
34
+ [x] = t1;
35
+ } else {
36
+ x = props.fallback;
37
+ }
38
+ return x;
39
+}
40
+
41
+```
42
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/destructuring-assignment-array-default.js
new
+9
@@ -0,0 +1,9 @@
1
+function Component(props) {
2
+ let x;
3
+ if (props.cond) {
4
+ [[x] = [foo()]] = props.y;
5
+ } else {
6
+ x = props.fallback;
7
+ }
8
+ return x;
9
+}
compiler/forget/src/__tests__/fixtures/compiler/destructuring-object-default.expect.md
new
+32
@@ -0,0 +1,32 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ const { x: { y } = { y: "default" } } = props.y;
7
+ return y;
8
+}
9
+
10
+```
11
+
12
+## Code
13
+
14
+```javascript
15
+function Component(props) {
16
+ const $ = React.unstable_useMemoCache(2);
17
+ const { x: t0 } = props.y;
18
+ const c_0 = $[0] !== t0;
19
+ let t1;
20
+ if (c_0) {
21
+ t1 = t0 === undefined ? { y: "default" } : t0;
22
+ $[0] = t0;
23
+ $[1] = t1;
24
+ } else {
25
+ t1 = $[1];
26
+ }
27
+ const { y } = t1;
28
+ return y;
29
+}
30
+
31
+```
32
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/destructuring-object-default.js
new
+4
@@ -0,0 +1,4 @@
1
+function Component(props) {
2
+ const { x: { y } = { y: "default" } } = props.y;
3
+ return y;
4
+}
compiler/forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md
-16
@@ -79,13 +79,6 @@ let moduleLocal = false;
79
## Error
80
81
```
82
-[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle AssignmentPattern assignments
83
-> 1 | function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
84
- | ^^^^^^^
85
- 2 | let i = 0;
86
- 3 | var x = [];
87
- 4 |
88
-
82
[ReactForget] TodoError: (BuildHIR::lower) Handle AssignmentPattern params
83
> 1 | function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
84
| ^^^^^^^
@@ -127,15 +120,6 @@ let moduleLocal = false;
120
14 |
121
15 | <Button haha={1}></Button>;
122
130
-[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle AssignmentPattern assignments
131
- 11 |
132
- 12 | const g = { b() {}, c: () => {} };
133
-> 13 | const { z, aa = "aa" } = useCustom();
134
- | ^^^^^^^^^
135
- 14 |
136
- 15 | <Button haha={1}></Button>;
137
- 16 | <Button>{/** empty */}</Button>;
138
-
123
[ReactForget] TodoError: (BuildHIR::lowerJsxElement) Handle JSXEmptyExpression expressions
124
14 |
125
15 | <Button haha={1}></Button>;