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.
This commit is contained in:
Joe Savona
2023-03-27 10:34:11 -07:00
parent 61e97dc278
commit 80bdab7447
8 changed files with 223 additions and 16 deletions
+100
View File
@@ -2446,6 +2446,106 @@ function lowerAssignment(
}
return { kind: "LoadLocal", place: temporary, loc: value.loc };
}
case "AssignmentPattern": {
const lvalue = lvaluePath as NodePath<t.AssignmentPattern>;
const loc = lvalue.node.loc ?? GeneratedSource;
const temp = buildTemporaryPlace(builder, loc);
const testBlock = builder.reserve("value");
const continuationBlock = builder.reserve(builder.currentBlockKind());
const consequent = builder.enter("value", () => {
const defaultValue = lowerExpressionToTemporary(
builder,
lvalue.get("right")
);
builder.push({
id: makeInstructionId(0),
lvalue: buildTemporaryPlace(builder, loc),
value: {
kind: "StoreLocal",
lvalue: { kind: InstructionKind.Const, place: { ...temp } },
value: { ...defaultValue },
loc,
},
loc,
});
return {
kind: "goto",
variant: GotoVariant.Break,
block: continuationBlock.id,
id: makeInstructionId(0),
loc,
};
});
const alternate = builder.enter("value", () => {
builder.push({
id: makeInstructionId(0),
lvalue: buildTemporaryPlace(builder, loc),
value: {
kind: "StoreLocal",
lvalue: { kind: InstructionKind.Const, place: { ...temp } },
value: { ...value },
loc,
},
loc,
});
return {
kind: "goto",
variant: GotoVariant.Break,
block: continuationBlock.id,
id: makeInstructionId(0),
loc,
};
});
builder.terminateWithContinuation(
{
kind: "ternary",
test: testBlock.id,
fallthrough: continuationBlock.id,
id: makeInstructionId(0),
loc,
},
testBlock
);
const undef = buildTemporaryPlace(builder, loc);
builder.push({
id: makeInstructionId(0),
lvalue: { ...undef },
value: {
kind: "Primitive",
value: undefined,
loc,
},
loc,
});
const test = buildTemporaryPlace(builder, loc);
builder.push({
id: makeInstructionId(0),
lvalue: { ...test },
value: {
kind: "BinaryExpression",
left: { ...value },
operator: "===",
right: { ...undef },
loc,
},
loc,
});
builder.terminateWithContinuation(
{
kind: "branch",
test: { ...test },
consequent,
alternate,
id: makeInstructionId(0),
},
continuationBlock
);
return lowerAssignment(builder, loc, kind, lvalue.get("left"), temp);
}
default: {
builder.errors.push({
reason: `(BuildHIR::lowerAssignment) Handle ${lvaluePath.type} assignments`,
@@ -0,0 +1,32 @@
## Input
```javascript
function Component(props) {
const [[x] = [foo()]] = props.y;
return x;
}
```
## Code
```javascript
function Component(props) {
const $ = React.unstable_useMemoCache(2);
const [t0] = props.y;
const c_0 = $[0] !== t0;
let t1;
if (c_0) {
t1 = t0 === undefined ? [foo()] : t0;
$[0] = t0;
$[1] = t1;
} else {
t1 = $[1];
}
const [x] = t1;
return x;
}
```
@@ -0,0 +1,4 @@
function Component(props) {
const [[x] = [foo()]] = props.y;
return x;
}
@@ -0,0 +1,42 @@
## Input
```javascript
function Component(props) {
let x;
if (props.cond) {
[[x] = [foo()]] = props.y;
} else {
x = props.fallback;
}
return x;
}
```
## Code
```javascript
function Component(props) {
const $ = React.unstable_useMemoCache(2);
let x = undefined;
if (props.cond) {
const [t0] = props.y;
const c_0 = $[0] !== t0;
let t1;
if (c_0) {
t1 = t0 === undefined ? [foo()] : t0;
$[0] = t0;
$[1] = t1;
} else {
t1 = $[1];
}
[x] = t1;
} else {
x = props.fallback;
}
return x;
}
```
@@ -0,0 +1,9 @@
function Component(props) {
let x;
if (props.cond) {
[[x] = [foo()]] = props.y;
} else {
x = props.fallback;
}
return x;
}
@@ -0,0 +1,32 @@
## Input
```javascript
function Component(props) {
const { x: { y } = { y: "default" } } = props.y;
return y;
}
```
## Code
```javascript
function Component(props) {
const $ = React.unstable_useMemoCache(2);
const { x: t0 } = props.y;
const c_0 = $[0] !== t0;
let t1;
if (c_0) {
t1 = t0 === undefined ? { y: "default" } : t0;
$[0] = t0;
$[1] = t1;
} else {
t1 = $[1];
}
const { y } = t1;
return y;
}
```
@@ -0,0 +1,4 @@
function Component(props) {
const { x: { y } = { y: "default" } } = props.y;
return y;
}
@@ -79,13 +79,6 @@ let moduleLocal = false;
## Error
```
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle AssignmentPattern assignments
> 1 | function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
| ^^^^^^^
2 | let i = 0;
3 | var x = [];
4 |
[ReactForget] TodoError: (BuildHIR::lower) Handle AssignmentPattern params
> 1 | function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
| ^^^^^^^
@@ -127,15 +120,6 @@ let moduleLocal = false;
14 |
15 | <Button haha={1}></Button>;
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle AssignmentPattern assignments
11 |
12 | const g = { b() {}, c: () => {} };
> 13 | const { z, aa = "aa" } = useCustom();
| ^^^^^^^^^
14 |
15 | <Button haha={1}></Button>;
16 | <Button>{/** empty */}</Button>;
[ReactForget] TodoError: (BuildHIR::lowerJsxElement) Handle JSXEmptyExpression expressions
14 |
15 | <Button haha={1}></Button>;