diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index 1e56ee0c4d..a6bb8e2167 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -1147,25 +1147,11 @@ function lowerExpression( case "MemberExpression": { // a.b.c += const leftExpr = left as NodePath; - // 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({ diff --git a/compiler/forget/src/__tests__/fixtures/hir/assignment-expression-computed.expect.md b/compiler/forget/src/__tests__/fixtures/hir/assignment-expression-computed.expect.md new file mode 100644 index 0000000000..80f8a47fe9 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/assignment-expression-computed.expect.md @@ -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; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/assignment-expression-computed.js b/compiler/forget/src/__tests__/fixtures/hir/assignment-expression-computed.js new file mode 100644 index 0000000000..da5881a6c8 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/assignment-expression-computed.js @@ -0,0 +1,7 @@ +function Component(props) { + const x = [props.x]; + const index = 0; + x[index] *= 2; + x["0"] += 3; + return x; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.expect.md b/compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.expect.md index 5897310ee9..b92f21f70a 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.expect.md @@ -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 | ; + 17 | const { z, aa = "aa", ...zz } = useCustom(); + 18 | + 19 | ; [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 | ; - 21 | ; + 18 | + 19 | ; + 20 | ; [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 | ; - 21 | ; + 18 | + 19 | ; + 20 | ; [ReactForget] TodoError: (BuildHIR::lowerExpression) Handle JSXNamespacedName attribute names in JSXElement - 19 | - 20 | ; -> 21 | ; + 18 | + 19 | ; +> 20 | ; | ^^^^^^^^^^ - 22 | ; - 23 | ; - 24 | ; + 21 | ; + 22 | ; + 23 | ; [ReactForget] TodoError: (BuildHIR::lowerJsxElement) Handle JSXEmptyExpression expressions - 21 | ; - 22 | ; -> 23 | ; + 20 | ; + 21 | ; +> 22 | ; | ^^^^^^^^^^^^ - 24 | ; - 25 | - 26 | const j = function bar([quz, qux], ...args) {}; + 23 | ; + 24 | + 25 | const j = function bar([quz, qux], ...args) {}; [ReactForget] TodoError: (BuildHIR::lowerJsxElementName) Handle JSXMemberExpression tags - 22 | ; - 23 | ; -> 24 | ; + 21 | ; + 22 | ; +> 23 | ; | ^^^^^^^^^^^^^^^^^^^ - 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 | ; - 25 | -> 26 | const j = function bar([quz, qux], ...args) {}; + 23 | ; + 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 | ; - 25 | -> 26 | const j = function bar([quz, qux], ...args) {}; + 23 | ; + 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 | } ``` \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.js b/compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.js index efd2ea31d2..fd71c0cb2d 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.js +++ b/compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.js @@ -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();