From 10525b6015a37d26e38f32298888811f223cf7a1 Mon Sep 17 00:00:00 2001 From: Mike Vitousek Date: Wed, 14 Aug 2024 16:56:49 -0400 Subject: [PATCH] [compiler] Support for member expression inc/decrement Test Plan: Builds support for a.x++ and friends. Similar to a.x += y, emits it as an assignment expression. [ghstack-poisoned] --- .../src/HIR/BuildHIR.ts | 51 +++++++++++++++++++ .../compiler/error.todo-kitchensink.expect.md | 4 -- .../fixtures/compiler/member-inc.expect.md | 44 ++++++++++++++++ .../__tests__/fixtures/compiler/member-inc.js | 14 +++++ 4 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/member-inc.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/member-inc.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index 8ed71b5e62a7e..945a265f487c9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -2396,6 +2396,57 @@ function lowerExpression( case 'UpdateExpression': { let expr = exprPath as NodePath; const argument = expr.get('argument'); + if (argument.isMemberExpression()) { + const binaryOperator = expr.node.operator === '++' ? '+' : '-'; + const leftExpr = argument as NodePath; + const {object, property, value} = lowerMemberExpression( + builder, + leftExpr, + ); + + // Store the previous value to a temporary + const previousValuePlace = lowerValueToTemporary(builder, value); + // Store the new value to a temporary + const updatedValue = lowerValueToTemporary(builder, { + kind: 'BinaryExpression', + operator: binaryOperator, + left: {...previousValuePlace}, + right: lowerValueToTemporary(builder, { + kind: 'Primitive', + value: 1, + loc: GeneratedSource, + }), + loc: leftExpr.node.loc ?? GeneratedSource, + }); + + // Save the result back to the property + let newValuePlace; + if (typeof property === 'string') { + newValuePlace = lowerValueToTemporary(builder, { + kind: 'PropertyStore', + object: {...object}, + property, + value: {...updatedValue}, + loc: leftExpr.node.loc ?? GeneratedSource, + }); + } else { + newValuePlace = lowerValueToTemporary(builder, { + kind: 'ComputedStore', + object: {...object}, + property: {...property}, + value: {...updatedValue}, + loc: leftExpr.node.loc ?? GeneratedSource, + }); + } + + return { + kind: 'LoadLocal', + place: expr.node.prefix + ? {...newValuePlace} + : {...previousValuePlace}, + loc: exprLoc, + }; + } if (!argument.isIdentifier()) { builder.errors.push({ reason: `(BuildHIR::lowerExpression) Handle UpdateExpression with ${argument.type} argument`, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md index ba32831cbb884..d8360455bb303 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md @@ -104,10 +104,6 @@ Todo: (BuildHIR::lowerStatement) Handle ArrayPattern inits in ForOfStatement (38 Todo: (BuildHIR::lowerStatement) Handle ObjectPattern inits in ForOfStatement (40:40) -Todo: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument (49:49) - -Todo: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument (50:50) - Todo: (BuildHIR::node.lowerReorderableExpression) Expression type `MemberExpression` cannot be safely reordered (57:57) Todo: (BuildHIR::node.lowerReorderableExpression) Expression type `BinaryExpression` cannot be safely reordered (53:53) diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/member-inc.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/member-inc.expect.md new file mode 100644 index 0000000000000..b8d4de7e84573 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/member-inc.expect.md @@ -0,0 +1,44 @@ + +## Input + +```javascript +//@flow + +component Foo() { + let x = {a: 1}; + x.a++; + x.a--; + console.log(++x.a); + console.log(x.a++); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [], +}; + +``` + +## Code + +```javascript +function Foo() { + const x = { a: 1 }; + x.a = x.a + 1; + x.a = x.a - 1; + console.log((x.a = x.a + 1)); + const t0 = x.a; + x.a = t0 + 1; + console.log(t0); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [], +}; + +``` + +### Eval output +(kind: ok) +logs: [2,2] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/member-inc.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/member-inc.js new file mode 100644 index 0000000000000..c5b649a3d4944 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/member-inc.js @@ -0,0 +1,14 @@ +//@flow + +component Foo() { + let x = {a: 1}; + x.a++; + x.a--; + console.log(++x.a); + console.log(x.a++); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [], +};