From de929fcc40a8a975e11d839746d7b55b0e36b1b1 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Thu, 2 Feb 2023 07:53:24 -0800 Subject: [PATCH] Partial support for update expressions Adds limited support for UpdateExpressions (`x++`). We now support the postfix form (`x++` ok, `++x` is a todo) and only when the argument is an identifier. We can relax these restrictions with more work, but this PR should be sufficient for the examples we've seen so far. --- compiler/forget/src/HIR/BuildHIR.ts | 49 +++++++++++++++++++ .../hir/error.todo-kitchensink.expect.md | 46 ++++++++++++++++- .../fixtures/hir/error.todo-kitchensink.js | 6 +++ .../__tests__/fixtures/hir/ssa-for.expect.md | 2 +- .../src/__tests__/fixtures/hir/ssa-for.js | 2 +- .../fixtures/hir/update-expression.expect.md | 35 +++++++++++++ .../fixtures/hir/update-expression.js | 6 +++ 7 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 compiler/forget/src/__tests__/fixtures/hir/update-expression.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/update-expression.js diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index 39246a7bec..8038197514 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -1405,6 +1405,55 @@ function lowerExpression( loc: exprLoc, }; } + case "UpdateExpression": { + let expr = exprPath as NodePath; + const argument = expr.get("argument"); + if (!argument.isIdentifier()) { + builder.errors.push({ + reason: `(BuildHIR::lowerExpression) Handle UpdateExpression with ${argument.type} argument`, + severity: ErrorSeverity.Todo, + nodePath: exprPath, + }); + return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc }; + } + if (expr.node.prefix) { + builder.errors.push({ + reason: `(BuildHIR::lowerExpression) Handle prefix UpdateExpression`, + severity: ErrorSeverity.Todo, + nodePath: exprPath, + }); + return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc }; + } + const temp = buildTemporaryPlace( + builder, + expr.node.loc ?? GeneratedSource + ); + builder.push({ + id: makeInstructionId(0), + lvalue: { place: { ...temp }, kind: InstructionKind.Const }, + value: { + kind: "Primitive", + value: 1, + loc: expr.node.loc ?? GeneratedSource, + }, + loc: expr.node.loc ?? GeneratedSource, + }); + const identifier = argument as NodePath; + const place = lowerExpressionToPlace(builder, identifier); + builder.push({ + id: makeInstructionId(0), + lvalue: { place: { ...place }, kind: InstructionKind.Reassign }, + value: { + kind: "BinaryExpression", + operator: expr.node.operator === "++" ? "+" : "-", + left: { ...place }, + right: { ...temp }, + loc: exprLoc, + }, + loc: exprLoc, + }); + return place; + } default: { builder.errors.push({ reason: `(BuildHIR::lowerExpression) Handle ${exprPath.type} expressions`, 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 59bfe5cc2e..d75803c9c8 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 @@ -52,6 +52,12 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) { } do {} while (i < 3); + + let updateIdentifier = 0; + --updateIdentifier; + ++updateIdentifier; + updateIdentifier.y++; + updateIdentifier.y--; } ``` @@ -327,8 +333,44 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) { 49 | > 50 | do {} while (i < 3); | ^^^^^^^^^^^^^^^^^^^^ - 51 | } - 52 | + 51 | + 52 | let updateIdentifier = 0; + 53 | --updateIdentifier; + +[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression + 51 | + 52 | let updateIdentifier = 0; +> 53 | --updateIdentifier; + | ^^^^^^^^^^^^^^^^^^ + 54 | ++updateIdentifier; + 55 | updateIdentifier.y++; + 56 | updateIdentifier.y--; + +[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression + 52 | let updateIdentifier = 0; + 53 | --updateIdentifier; +> 54 | ++updateIdentifier; + | ^^^^^^^^^^^^^^^^^^ + 55 | updateIdentifier.y++; + 56 | updateIdentifier.y--; + 57 | } + +[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument + 53 | --updateIdentifier; + 54 | ++updateIdentifier; +> 55 | updateIdentifier.y++; + | ^^^^^^^^^^^^^^^^^^^^ + 56 | updateIdentifier.y--; + 57 | } + 58 | + +[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument + 54 | ++updateIdentifier; + 55 | updateIdentifier.y++; +> 56 | updateIdentifier.y--; + | ^^^^^^^^^^^^^^^^^^^^ + 57 | } + 58 | ``` \ 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 c4675acc10..b503b6b110 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.js +++ b/compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.js @@ -48,4 +48,10 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) { } do {} while (i < 3); + + let updateIdentifier = 0; + --updateIdentifier; + ++updateIdentifier; + updateIdentifier.y++; + updateIdentifier.y--; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md index 61ba326665..734d16130d 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md @@ -4,7 +4,7 @@ ```javascript function foo() { let x = 1; - for (let i = 0; i < 10; i += 1) { + for (let i = 0; i < 10; i++) { x += 1; } return x; diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-for.js b/compiler/forget/src/__tests__/fixtures/hir/ssa-for.js index 455f279915..14978ef645 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-for.js +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-for.js @@ -1,6 +1,6 @@ function foo() { let x = 1; - for (let i = 0; i < 10; i += 1) { + for (let i = 0; i < 10; i++) { x += 1; } return x; diff --git a/compiler/forget/src/__tests__/fixtures/hir/update-expression.expect.md b/compiler/forget/src/__tests__/fixtures/hir/update-expression.expect.md new file mode 100644 index 0000000000..fdbff2d6eb --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/update-expression.expect.md @@ -0,0 +1,35 @@ + +## Input + +```javascript +function foo(props) { + let x = props.x; + let y = x++; + let z = x--; + return { x, y, z }; +} + +``` + +## Code + +```javascript +function foo(props) { + const $ = React.useMemoCache(); + const x = props.x; + const x$0 = x + 1; + const y = x$0; + const x$1 = x$0 - 1; + const z = x$1; + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = { x: x$1, y: y, z: z }; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/update-expression.js b/compiler/forget/src/__tests__/fixtures/hir/update-expression.js new file mode 100644 index 0000000000..7ff8299b98 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/update-expression.js @@ -0,0 +1,6 @@ +function foo(props) { + let x = props.x; + let y = x++; + let z = x--; + return { x, y, z }; +}