Fix update assignment on computed memberexpression

This commit is contained in:
Joe Savona
2023-02-10 08:59:15 -08:00
parent 5746d5b07f
commit 6877ac6500
5 changed files with 226 additions and 203 deletions
+21 -31
View File
@@ -1147,25 +1147,11 @@ function lowerExpression(
case "MemberExpression": {
// a.b.c += <right>
const leftExpr = left as NodePath<t.MemberExpression>;
// Lower everything up to the final property to a temporary, eg `a.b`
const object = lowerExpressionToPlace(
const { object, property, value } = lowerMemberExpression(
builder,
leftExpr.get("object")
leftExpr
);
// Extract the final property to be read from and re-assigned, eg 'c'
const property = leftExpr.get("property");
if (!property.isIdentifier()) {
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${property.type} properties in MemberExpression`,
severity: ErrorSeverity.Todo,
nodePath: property,
});
return {
kind: "UnsupportedNode",
node: leftExpr.node,
loc: leftExpr.node.loc ?? GeneratedSource,
};
}
// Store the previous value to a temporary
const previousValuePlace: Place = buildTemporaryPlace(
builder,
@@ -1177,13 +1163,7 @@ function lowerExpression(
place: { ...previousValuePlace },
kind: InstructionKind.Const,
},
value: {
kind: "PropertyLoad",
object: { ...object },
property: property.node.name,
loc: leftExpr.node.loc ?? GeneratedSource,
optional: false, // LVal cannot be optional
},
value,
loc: leftExpr.node.loc ?? GeneratedSource,
});
// Store the new value to a temporary
@@ -1205,13 +1185,23 @@ function lowerExpression(
});
// Save the result back to the property
return {
kind: "PropertyStore",
object: { ...object },
property: property.node.name,
value: { ...newValuePlace },
loc: leftExpr.node.loc ?? GeneratedSource,
};
if (typeof property === "string") {
return {
kind: "PropertyStore",
object: { ...object },
property,
value: { ...newValuePlace },
loc: leftExpr.node.loc ?? GeneratedSource,
};
} else {
return {
kind: "ComputedStore",
object: { ...object },
property: { ...property },
value: { ...newValuePlace },
loc: leftExpr.node.loc ?? GeneratedSource,
};
}
}
default: {
builder.errors.push({
@@ -0,0 +1,37 @@
## Input
```javascript
function Component(props) {
const x = [props.x];
const index = 0;
x[index] *= 2;
x["0"] += 3;
return x;
}
```
## Code
```javascript
function Component(props) {
const $ = React.unstable_useMemoCache();
const c_0 = $[0] !== props.x;
let x;
if (c_0) {
x = [props.x];
const index = 0;
x[index] = x[index] * 2;
const t0 = "0";
x[t0] = x[t0] + 3;
$[0] = props.x;
$[1] = x;
} else {
x = $[1];
}
return x;
}
```
@@ -0,0 +1,7 @@
function Component(props) {
const x = [props.x];
const index = 0;
x[index] *= 2;
x["0"] += 3;
return x;
}
@@ -17,7 +17,6 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
const h = [...b];
new c(...args);
c(...args);
g["e"] += 1;
const [y, ...yy] = useState(0);
const { z, aa = "aa", ...zz } = useCustom();
@@ -148,7 +147,7 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
| ^^^^
14 | new c(...args);
15 | c(...args);
16 | g["e"] += 1;
16 | const [y, ...yy] = useState(0);
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle SpreadElement arguments in NewExpression
12 | const g = { ...a, b() {}, c: () => {} };
@@ -156,260 +155,251 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
> 14 | new c(...args);
| ^^^^^^^
15 | c(...args);
16 | g["e"] += 1;
17 | const [y, ...yy] = useState(0);
16 | const [y, ...yy] = useState(0);
17 | const { z, aa = "aa", ...zz } = useCustom();
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle SpreadElement arguments in CallExpression
13 | const h = [...b];
14 | new c(...args);
> 15 | c(...args);
| ^^^^^^^
16 | g["e"] += 1;
17 | const [y, ...yy] = useState(0);
18 | const { z, aa = "aa", ...zz } = useCustom();
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle StringLiteral properties in MemberExpression
14 | new c(...args);
15 | c(...args);
> 16 | g["e"] += 1;
| ^^^
17 | const [y, ...yy] = useState(0);
18 | const { z, aa = "aa", ...zz } = useCustom();
19 |
16 | const [y, ...yy] = useState(0);
17 | const { z, aa = "aa", ...zz } = useCustom();
18 |
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle RestElement in ArrayPattern
14 | new c(...args);
15 | c(...args);
16 | g["e"] += 1;
> 17 | const [y, ...yy] = useState(0);
> 16 | const [y, ...yy] = useState(0);
| ^^^^^
18 | const { z, aa = "aa", ...zz } = useCustom();
19 |
20 | <Button {...args}></Button>;
17 | const { z, aa = "aa", ...zz } = useCustom();
18 |
19 | <Button {...args}></Button>;
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle AssignmentPattern assignments
16 | g["e"] += 1;
17 | const [y, ...yy] = useState(0);
> 18 | const { z, aa = "aa", ...zz } = useCustom();
15 | c(...args);
16 | const [y, ...yy] = useState(0);
> 17 | const { z, aa = "aa", ...zz } = useCustom();
| ^^^^^^^^^
19 |
20 | <Button {...args}></Button>;
21 | <Button xlink:href="localhost:3000"></Button>;
18 |
19 | <Button {...args}></Button>;
20 | <Button xlink:href="localhost:3000"></Button>;
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle RestElement properties in ObjectPattern
16 | g["e"] += 1;
17 | const [y, ...yy] = useState(0);
> 18 | const { z, aa = "aa", ...zz } = useCustom();
15 | c(...args);
16 | const [y, ...yy] = useState(0);
> 17 | const { z, aa = "aa", ...zz } = useCustom();
| ^^^^^
19 |
20 | <Button {...args}></Button>;
21 | <Button xlink:href="localhost:3000"></Button>;
18 |
19 | <Button {...args}></Button>;
20 | <Button xlink:href="localhost:3000"></Button>;
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle JSXNamespacedName attribute names in JSXElement
19 |
20 | <Button {...args}></Button>;
> 21 | <Button xlink:href="localhost:3000"></Button>;
18 |
19 | <Button {...args}></Button>;
> 20 | <Button xlink:href="localhost:3000"></Button>;
| ^^^^^^^^^^
22 | <Button haha={1}></Button>;
23 | <Button>{/** empty */}</Button>;
24 | <DesignSystem.Button />;
21 | <Button haha={1}></Button>;
22 | <Button>{/** empty */}</Button>;
23 | <DesignSystem.Button />;
[ReactForget] TodoError: (BuildHIR::lowerJsxElement) Handle JSXEmptyExpression expressions
21 | <Button xlink:href="localhost:3000"></Button>;
22 | <Button haha={1}></Button>;
> 23 | <Button>{/** empty */}</Button>;
20 | <Button xlink:href="localhost:3000"></Button>;
21 | <Button haha={1}></Button>;
> 22 | <Button>{/** empty */}</Button>;
| ^^^^^^^^^^^^
24 | <DesignSystem.Button />;
25 |
26 | const j = function bar([quz, qux], ...args) {};
23 | <DesignSystem.Button />;
24 |
25 | const j = function bar([quz, qux], ...args) {};
[ReactForget] TodoError: (BuildHIR::lowerJsxElementName) Handle JSXMemberExpression tags
22 | <Button haha={1}></Button>;
23 | <Button>{/** empty */}</Button>;
> 24 | <DesignSystem.Button />;
21 | <Button haha={1}></Button>;
22 | <Button>{/** empty */}</Button>;
> 23 | <DesignSystem.Button />;
| ^^^^^^^^^^^^^^^^^^^
25 |
26 | const j = function bar([quz, qux], ...args) {};
27 |
24 |
25 | const j = function bar([quz, qux], ...args) {};
26 |
[ReactForget] TodoError: (BuildHIR::lower) Handle ArrayPattern params
24 | <DesignSystem.Button />;
25 |
> 26 | const j = function bar([quz, qux], ...args) {};
23 | <DesignSystem.Button />;
24 |
> 25 | const j = function bar([quz, qux], ...args) {};
| ^^^^^^^^^^
27 |
28 | for (; i < 3; i += 1) {
29 | x.push(i);
26 |
27 | for (; i < 3; i += 1) {
28 | x.push(i);
[ReactForget] TodoError: (BuildHIR::lower) Handle RestElement params
24 | <DesignSystem.Button />;
25 |
> 26 | const j = function bar([quz, qux], ...args) {};
23 | <DesignSystem.Button />;
24 |
> 25 | const j = function bar([quz, qux], ...args) {};
| ^^^^^^^
27 |
28 | for (; i < 3; i += 1) {
29 | x.push(i);
26 |
27 | for (; i < 3; i += 1) {
28 | x.push(i);
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
26 | const j = function bar([quz, qux], ...args) {};
27 |
> 28 | for (; i < 3; i += 1) {
25 | const j = function bar([quz, qux], ...args) {};
26 |
> 27 | for (; i < 3; i += 1) {
| ^
29 | x.push(i);
30 | }
31 | for (; i < 3; ) {
28 | x.push(i);
29 | }
30 | for (; i < 3; ) {
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
29 | x.push(i);
30 | }
> 31 | for (; i < 3; ) {
28 | x.push(i);
29 | }
> 30 | for (; i < 3; ) {
| ^
32 | break;
33 | }
34 | for (;;) {
31 | break;
32 | }
33 | for (;;) {
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty update in ForStatement
29 | x.push(i);
30 | }
> 31 | for (; i < 3; ) {
28 | x.push(i);
29 | }
> 30 | for (; i < 3; ) {
| ^
32 | break;
33 | }
34 | for (;;) {
31 | break;
32 | }
33 | for (;;) {
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
32 | break;
33 | }
> 34 | for (;;) {
31 | break;
32 | }
> 33 | for (;;) {
| ^
35 | break;
36 | }
37 |
34 | break;
35 | }
36 |
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty update in ForStatement
32 | break;
33 | }
> 34 | for (;;) {
31 | break;
32 | }
> 33 | for (;;) {
| ^
35 | break;
36 | }
37 |
34 | break;
35 | }
36 |
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty test in ForStatement
32 | break;
33 | }
> 34 | for (;;) {
31 | break;
32 | }
> 33 | for (;;) {
| ^
35 | break;
36 | }
37 |
34 | break;
35 | }
36 |
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle tagged template with interpolations
36 | }
37 |
> 38 | graphql`
35 | }
36 |
> 37 | graphql`
| ^
39 | ${g}
40 | `;
41 |
38 | ${g}
39 | `;
40 |
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value
40 | `;
41 |
> 42 | graphql`\\t\n`;
39 | `;
40 |
> 41 | graphql`\\t\n`;
| ^^^^^^^^^^^^^^
43 |
44 | for (const c of [1, 2]) {
45 | }
42 |
43 | for (const c of [1, 2]) {
44 | }
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle ForOfStatement statements
42 | graphql`\\t\n`;
43 |
> 44 | for (const c of [1, 2]) {
41 | graphql`\\t\n`;
42 |
> 43 | for (const c of [1, 2]) {
| ^
45 | }
46 |
47 | for (let x in { a: 1 }) {
44 | }
45 |
46 | for (let x in { a: 1 }) {
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle ForInStatement statements
45 | }
46 |
> 47 | for (let x in { a: 1 }) {
44 | }
45 |
> 46 | for (let x in { a: 1 }) {
| ^
48 | }
49 |
50 | do {} while (i < 3);
47 | }
48 |
49 | do {} while (i < 3);
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle DoWhileStatement statements
48 | }
49 |
> 50 | do {} while (i < 3);
47 | }
48 |
> 49 | do {} while (i < 3);
| ^^^^^^^^^^^^^^^^^^^^
51 |
52 | let updateIdentifier = 0;
53 | --updateIdentifier;
50 |
51 | let updateIdentifier = 0;
52 | --updateIdentifier;
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression
51 |
52 | let updateIdentifier = 0;
> 53 | --updateIdentifier;
50 |
51 | let updateIdentifier = 0;
> 52 | --updateIdentifier;
| ^^^^^^^^^^^^^^^^^^
54 | ++updateIdentifier;
55 | updateIdentifier.y++;
56 | updateIdentifier.y--;
53 | ++updateIdentifier;
54 | updateIdentifier.y++;
55 | updateIdentifier.y--;
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression
52 | let updateIdentifier = 0;
53 | --updateIdentifier;
> 54 | ++updateIdentifier;
51 | let updateIdentifier = 0;
52 | --updateIdentifier;
> 53 | ++updateIdentifier;
| ^^^^^^^^^^^^^^^^^^
55 | updateIdentifier.y++;
56 | updateIdentifier.y--;
57 |
54 | updateIdentifier.y++;
55 | updateIdentifier.y--;
56 |
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument
53 | --updateIdentifier;
54 | ++updateIdentifier;
> 55 | updateIdentifier.y++;
52 | --updateIdentifier;
53 | ++updateIdentifier;
> 54 | updateIdentifier.y++;
| ^^^^^^^^^^^^^^^^^^^^
56 | updateIdentifier.y--;
57 |
58 | switch (i) {
55 | updateIdentifier.y--;
56 |
57 | switch (i) {
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument
54 | ++updateIdentifier;
55 | updateIdentifier.y++;
> 56 | updateIdentifier.y--;
53 | ++updateIdentifier;
54 | updateIdentifier.y++;
> 55 | updateIdentifier.y--;
| ^^^^^^^^^^^^^^^^^^^^
57 |
58 | switch (i) {
59 | case 1 + 1: {
56 |
57 | switch (i) {
58 | case 1 + 1: {
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
61 | case foo(): {
62 | }
> 63 | case x.y: {
60 | case foo(): {
61 | }
> 62 | case x.y: {
| ^^^
64 | }
65 | default: {
66 | }
63 | }
64 | default: {
65 | }
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
59 | case 1 + 1: {
60 | }
> 61 | case foo(): {
58 | case 1 + 1: {
59 | }
> 60 | case foo(): {
| ^^^^^
62 | }
63 | case x.y: {
64 | }
61 | }
62 | case x.y: {
63 | }
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
57 |
58 | switch (i) {
> 59 | case 1 + 1: {
56 |
57 | switch (i) {
> 58 | case 1 + 1: {
| ^^^^^
60 | }
61 | case foo(): {
62 | }
59 | }
60 | case foo(): {
61 | }
```
@@ -13,7 +13,6 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
const h = [...b];
new c(...args);
c(...args);
g["e"] += 1;
const [y, ...yy] = useState(0);
const { z, aa = "aa", ...zz } = useCustom();