@samitouri / QOS-React-1 / commits / 8d41529600

Support computed object keys

Joe Savona committed Nov 13, 2023 at 09:55 UTC 8d415296009b3a014e4d4678c41a9520d5acd894
28 files changed +722 -60
compiler/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts
+32 -19
@@ -1321,26 +1321,45 @@ function lowerObjectMethod(
1321
1322 function lowerObjectPropertyKey(
1323 builder: HIRBuilder,
1324 - key: t.PrivateName | t.Expression
1324 + property: NodePath<t.ObjectProperty | t.ObjectMethod>
1325 ): ObjectPropertyKey | null {
1326 - if (key.type === "Identifier") {
1326 + const key = property.get("key");
1327 + if (key.isStringLiteral()) {
1328 return {
1328 - name: key.name,
1329 - type: "identifier",
1329 + kind: "string",
1330 + name: key.node.value,
1331 };
1331 - }
1332 -
1333 - if (key.type === "StringLiteral") {
1332 + } else if (property.node.computed && key.isExpression()) {
1333 + if (!key.isIdentifier()) {
1334 + /*
1335 + * NOTE: allowing complex key expressions can trigger a bug where a mutation is made conditional
1336 + * see fixture
1337 + * error.object-expression-computed-key-modified-during-after-construction.js
1338 + */
1339 + builder.errors.push({
1340 + reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,
1341 + severity: ErrorSeverity.Todo,
1342 + loc: key.node.loc ?? null,
1343 + suggestions: null,
1344 + });
1345 + return null;
1346 + }
1347 + const place = lowerExpressionToTemporary(builder, key);
1348 + return {
1349 + kind: "computed",
1350 + name: place,
1351 + };
1352 + } else if (key.isIdentifier()) {
1353 return {
1335 - name: key.value,
1336 - type: "string",
1354 + kind: "identifier",
1355 + name: key.node.name,
1356 };
1357 }
1358
1359 builder.errors.push({
1360 reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,
1361 severity: ErrorSeverity.Todo,
1343 - loc: key.loc ?? null,
1362 + loc: key.node.loc ?? null,
1363 suggestions: null,
1364 });
1365 return null;
@@ -1388,10 +1407,7 @@ function lowerExpression(
1407 const properties: Array<ObjectProperty | SpreadPattern> = [];
1408 for (const propertyPath of propertyPaths) {
1409 if (propertyPath.isObjectProperty()) {
1391 - const loweredKey = lowerObjectPropertyKey(
1392 - builder,
1393 - propertyPath.node.key
1394 - );
1410 + const loweredKey = lowerObjectPropertyKey(builder, propertyPath);
1411 if (!loweredKey) {
1412 continue;
1413 }
@@ -1424,10 +1440,7 @@ function lowerExpression(
1440 } else if (propertyPath.isObjectMethod()) {
1441 const method = lowerObjectMethod(builder, propertyPath);
1442 const place = lowerValueToTemporary(builder, method);
1427 - const loweredKey = lowerObjectPropertyKey(
1428 - builder,
1429 - propertyPath.node.key
1430 - );
1443 + const loweredKey = lowerObjectPropertyKey(builder, propertyPath);
1444 if (!loweredKey) {
1445 continue;
1446 }
@@ -3443,7 +3456,7 @@ function lowerAssignment(
3456 });
3457 continue;
3458 }
3446 - const loweredKey = lowerObjectPropertyKey(builder, property.node.key);
3459 + const loweredKey = lowerObjectPropertyKey(builder, property);
3460 if (!loweredKey) {
3461 continue;
3462 }
compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts
+13 -4
@@ -548,10 +548,19 @@ export type ObjectPattern = {
548 properties: Array<ObjectProperty | SpreadPattern>;
549 };
550
551 -export type ObjectPropertyKey = {
552 - type: "string" | "identifier";
553 - name: string; // TODO: make a Place
554 -};
551 +export type ObjectPropertyKey =
552 + | {
553 + kind: "string";
554 + name: string;
555 + }
556 + | {
557 + kind: "identifier";
558 + name: string;
559 + }
560 + | {
561 + kind: "computed";
562 + name: Place;
563 + };
564
565 export type ObjectProperty = {
566 kind: "ObjectProperty";
compiler/packages/babel-plugin-react-forget/src/HIR/PrintHIR.ts
+5 -2
@@ -264,11 +264,14 @@ function printHole(): string {
264 }
265
266 function printObjectPropertyKey(key: ObjectPropertyKey): string {
267 - switch (key.type) {
267 + switch (key.kind) {
268 case "identifier":
269 return key.name;
270 case "string":
271 - return `'${key.name}'`;
271 + return `"${key.name}"`;
272 + case "computed": {
273 + return `[${printPlace(key.name)}]`;
274 + }
275 }
276 }
277
compiler/packages/babel-plugin-react-forget/src/HIR/visitors.ts
+12
@@ -155,6 +155,12 @@ export function* eachInstructionValueOperand(
155 }
156 case "ObjectExpression": {
157 for (const property of instrValue.properties) {
158 + if (
159 + property.kind === "ObjectProperty" &&
160 + property.key.kind === "computed"
161 + ) {
162 + yield property.key.name;
163 + }
164 yield property.place;
165 }
166 break;
@@ -436,6 +442,12 @@ export function mapInstructionOperands(
442 }
443 case "ObjectExpression": {
444 for (const property of instrValue.properties) {
445 + if (
446 + property.kind === "ObjectProperty" &&
447 + property.key.kind === "computed"
448 + ) {
449 + property.key.name = fn(property.key.name);
450 + }
451 property.place = fn(property.place);
452 }
453 break;
compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts
+29 -4
@@ -690,10 +690,35 @@ function inferBlock(
690 ? ValueKind.Context
691 : ValueKind.Mutable;
692
693 - // Object construction captures but does not modify the key/property values
694 - effectKind = Effect.Capture;
695 - lvalueEffect = Effect.Store;
696 - break;
693 + for (const property of instrValue.properties) {
694 + switch (property.kind) {
695 + case "ObjectProperty": {
696 + if (property.key.kind === "computed") {
697 + // Object keys must be primitives, so we know they're frozen at this point
698 + state.reference(property.key.name, Effect.Freeze);
699 + }
700 + // Object construction captures but does not modify the key/property values
701 + state.reference(property.place, Effect.Capture);
702 + break;
703 + }
704 + case "Spread": {
705 + // Object construction captures but does not modify the key/property values
706 + state.reference(property.place, Effect.Capture);
707 + break;
708 + }
709 + default: {
710 + assertExhaustive(
711 + property,
712 + `Unexpected property kind '${(property as any).kind}'`
713 + );
714 + }
715 + }
716 + }
717 +
718 + state.initialize(instrValue, valueKind);
719 + state.define(instr.lvalue, instrValue);
720 + instr.lvalue.effect = Effect.Store;
721 + continue;
722 }
723 case "UnaryExpression": {
724 valueKind = ValueKind.Immutable;
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+42 -21
@@ -534,11 +534,11 @@ function codegenTerminal(
534 let lval: t.LVal;
535 switch (iterableItem.value.kind) {
536 case "StoreLocal": {
537 - lval = codegenLValue(iterableItem.value.lvalue.place);
537 + lval = codegenLValue(cx, iterableItem.value.lvalue.place);
538 break;
539 }
540 case "Destructure": {
541 - lval = codegenLValue(iterableItem.value.lvalue.pattern);
541 + lval = codegenLValue(cx, iterableItem.value.lvalue.pattern);
542 break;
543 }
544 default:
@@ -748,7 +748,7 @@ function codegenInstructionNullable(
748 suggestions: null,
749 });
750 return createVariableDeclaration(instr.loc, "const", [
751 - t.variableDeclarator(codegenLValue(lvalue), value),
751 + t.variableDeclarator(codegenLValue(cx, lvalue), value),
752 ]);
753 }
754 case InstructionKind.Let: {
@@ -759,7 +759,7 @@ function codegenInstructionNullable(
759 suggestions: null,
760 });
761 return createVariableDeclaration(instr.loc, "let", [
762 - t.variableDeclarator(codegenLValue(lvalue), value),
762 + t.variableDeclarator(codegenLValue(cx, lvalue), value),
763 ]);
764 }
765 case InstructionKind.Reassign: {
@@ -769,7 +769,11 @@ function codegenInstructionNullable(
769 loc: instr.value.loc,
770 suggestions: null,
771 });
772 - const expr = t.assignmentExpression("=", codegenLValue(lvalue), value);
772 + const expr = t.assignmentExpression(
773 + "=",
774 + codegenLValue(cx, lvalue),
775 + value
776 + );
777 if (instr.lvalue !== null) {
778 if (instr.value.kind !== "StoreContext") {
779 cx.temp.set(instr.lvalue.identifier.id, expr);
@@ -1132,7 +1136,7 @@ function codegenInstructionValue(
1136 const properties = [];
1137 for (const property of instrValue.properties) {
1138 if (property.kind === "ObjectProperty") {
1135 - const key = codegenObjectPropertyKey(property.key);
1139 + const key = codegenObjectPropertyKey(cx, property.key);
1140
1141 switch (property.type) {
1142 case "property": {
@@ -1141,9 +1145,10 @@ function codegenInstructionValue(
1145 t.objectProperty(
1146 key,
1147 value,
1144 - false,
1145 - value.type === "Identifier" &&
1146 - value.name === property.key.name
1148 + property.key.kind === "computed",
1149 + key.type === "Identifier" &&
1150 + value.type === "Identifier" &&
1151 + value.name === key.name
1152 )
1153 );
1154 break;
@@ -1645,17 +1650,31 @@ function convertMemberExpressionToJsx(
1650 }
1651
1652 function codegenObjectPropertyKey(
1653 + cx: Context,
1654 key: ObjectPropertyKey
1649 -): t.StringLiteral | t.Identifier {
1650 - switch (key.type) {
1651 - case "identifier":
1652 - return t.identifier(key.name);
1653 - case "string":
1655 +): t.Expression {
1656 + switch (key.kind) {
1657 + case "string": {
1658 return t.stringLiteral(key.name);
1659 + }
1660 + case "identifier": {
1661 + return t.identifier(key.name);
1662 + }
1663 + case "computed": {
1664 + const expr = codegenPlace(cx, key.name);
1665 + CompilerError.invariant(t.isExpression(expr), {
1666 + reason: "Expected object property key to be an expression",
1667 + description: null,
1668 + loc: key.name.loc,
1669 + suggestions: null,
1670 + });
1671 + return expr;
1672 + }
1673 }
1674 }
1675
1676 function codegenLValue(
1677 + cx: Context,
1678 pattern: Pattern | Place | SpreadPattern
1679 ): t.ArrayPattern | t.ObjectPattern | t.RestElement | t.Identifier {
1680 switch (pattern.kind) {
@@ -1665,7 +1684,7 @@ function codegenLValue(
1684 if (item.kind === "Hole") {
1685 return null;
1686 }
1668 - return codegenLValue(item);
1687 + return codegenLValue(cx, item);
1688 })
1689 );
1690 }
@@ -1673,22 +1692,24 @@ function codegenLValue(
1692 return t.objectPattern(
1693 pattern.properties.map((property) => {
1694 if (property.kind === "ObjectProperty") {
1676 - const key = codegenObjectPropertyKey(property.key);
1677 - const value = codegenLValue(property.place);
1695 + const key = codegenObjectPropertyKey(cx, property.key);
1696 + const value = codegenLValue(cx, property.place);
1697 return t.objectProperty(
1698 key,
1699 value,
1681 - false,
1682 - value.type === "Identifier" && value.name === property.key.name
1700 + property.key.kind === "computed",
1701 + key.type === "Identifier" &&
1702 + value.type === "Identifier" &&
1703 + value.name === key.name
1704 );
1705 } else {
1685 - return t.restElement(codegenLValue(property.place));
1706 + return t.restElement(codegenLValue(cx, property.place));
1707 }
1708 })
1709 );
1710 }
1711 case "Spread": {
1691 - return t.restElement(codegenLValue(pattern.place));
1712 + return t.restElement(codegenLValue(cx, pattern.place));
1713 }
1714 case "Identifier": {
1715 return convertIdentifier(pattern.identifier);
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PruneNonEscapingScopes.ts
+3 -4
@@ -5,7 +5,6 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import prettyFormat from "pretty-format";
8 import { CompilerError } from "../CompilerError";
9 import {
10 Environment,
@@ -25,7 +24,6 @@ import {
24 isMutableEffect,
25 } from "../HIR";
26 import { getFunctionCallSignature } from "../Inference/InferReferenceEffects";
28 -import { log } from "../Utils/logger";
27 import { assertExhaustive } from "../Utils/utils";
28 import { getPlaceScope } from "./BuildReactiveBlocks";
29 import {
@@ -131,7 +129,7 @@ export function pruneNonEscapingScopes(
129 state
130 );
131
134 - log(() => prettyFormat(state));
132 + // log(() => prettyFormat(state));
133
134 /*
135 * Then walk outward from the returned values and find all captured operands.
@@ -139,7 +137,8 @@ export function pruneNonEscapingScopes(
137 */
138 const memoized = computeMemoizedIdentifiers(state);
139
142 - log(() => prettyFormat(memoized));
140 + // log(() => prettyFormat(memoized));
141 +
142 // log(() => printReactiveFunction(fn));
143
144 // Prune scopes that do not declare/reassign any escaping values
compiler/packages/babel-plugin-react-forget/src/TypeInference/InferTypes.ts
+20 -5
@@ -185,6 +185,16 @@ function* generateInstructionTypes(
185 }
186
187 case "ObjectExpression": {
188 + for (const property of value.properties) {
189 + if (
190 + property.kind === "ObjectProperty" &&
191 + property.key.kind === "computed"
192 + ) {
193 + yield equation(property.key.name.identifier.type, {
194 + kind: "Primitive",
195 + });
196 + }
197 + }
198 yield equation(left, { kind: "Object", shapeId: BuiltInObjectId });
199 break;
200 }
@@ -235,11 +245,16 @@ function* generateInstructionTypes(
245 } else {
246 for (const property of pattern.properties) {
247 if (property.kind === "ObjectProperty") {
238 - yield equation(property.place.identifier.type, {
239 - kind: "Property",
240 - object: value.value.identifier.type,
241 - propertyName: property.key.name,
242 - });
248 + if (
249 + property.key.kind === "identifier" ||
250 + property.key.kind === "string"
251 + ) {
252 + yield equation(property.place.identifier.type, {
253 + kind: "Property",
254 + object: value.value.identifier.type,
255 + propertyName: property.key.name,
256 + });
257 + }
258 }
259 }
260 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/dce-unused-const.expect.md new
+30
@@ -0,0 +1,30 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const _ = 42;
7 + return props.value;
8 +}
9 +
10 +export const FIXTURE_ENTRYPOINT = {
11 + fn: Component,
12 + params: [{ value: 42 }],
13 +};
14 +
15 +```
16 +
17 +## Code
18 +
19 +```javascript
20 +function Component(props) {
21 + return props.value;
22 +}
23 +
24 +export const FIXTURE_ENTRYPOINT = {
25 + fn: Component,
26 + params: [{ value: 42 }],
27 +};
28 +
29 +```
30 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/dce-unused-const.js new
+9
@@ -0,0 +1,9 @@
1 +function Component(props) {
2 + const _ = 42;
3 + return props.value;
4 +}
5 +
6 +export const FIXTURE_ENTRYPOINT = {
7 + fn: Component,
8 + params: [{ value: 42 }],
9 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction-sequence-expr.expect.md new
+30
@@ -0,0 +1,30 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { identity, mutate, mutateAndReturn } from "shared-runtime";
6 +
7 +function Component(props) {
8 + const key = {};
9 + const context = {
10 + [(mutate(key), key)]: identity([props.value]),
11 + };
12 + mutate(key);
13 + return context;
14 +}
15 +
16 +export const FIXTURE_ENTRYPOINT = {
17 + fn: Component,
18 + params: [{ value: 42 }],
19 +};
20 +
21 +```
22 +
23 +
24 +## Error
25 +
26 +```
27 +[ReactForget] Todo: (BuildHIR::lowerExpression) Expected Identifier, got SequenceExpression key in ObjectExpression (6:6)
28 +```
29 +
30 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction-sequence-expr.js new
+15
@@ -0,0 +1,15 @@
1 +import { identity, mutate, mutateAndReturn } from "shared-runtime";
2 +
3 +function Component(props) {
4 + const key = {};
5 + const context = {
6 + [(mutate(key), key)]: identity([props.value]),
7 + };
8 + mutate(key);
9 + return context;
10 +}
11 +
12 +export const FIXTURE_ENTRYPOINT = {
13 + fn: Component,
14 + params: [{ value: 42 }],
15 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction.expect.md new
+30
@@ -0,0 +1,30 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { identity, mutate, mutateAndReturn } from "shared-runtime";
6 +
7 +function Component(props) {
8 + const key = {};
9 + const context = {
10 + [mutateAndReturn(key)]: identity([props.value]),
11 + };
12 + mutate(key);
13 + return context;
14 +}
15 +
16 +export const FIXTURE_ENTRYPOINT = {
17 + fn: Component,
18 + params: [{ value: 42 }],
19 +};
20 +
21 +```
22 +
23 +
24 +## Error
25 +
26 +```
27 +[ReactForget] Todo: (BuildHIR::lowerExpression) Expected Identifier, got CallExpression key in ObjectExpression (6:6)
28 +```
29 +
30 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-modified-during-after-construction.js new
+15
@@ -0,0 +1,15 @@
1 +import { identity, mutate, mutateAndReturn } from "shared-runtime";
2 +
3 +function Component(props) {
4 + const key = {};
5 + const context = {
6 + [mutateAndReturn(key)]: identity([props.value]),
7 + };
8 + mutate(key);
9 + return context;
10 +}
11 +
12 +export const FIXTURE_ENTRYPOINT = {
13 + fn: Component,
14 + params: [{ value: 42 }],
15 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-mutate-key-while-constructing-object.expect.md new
+29
@@ -0,0 +1,29 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { identity, mutate, mutateAndReturn } from "shared-runtime";
6 +
7 +function Component(props) {
8 + const key = {};
9 + const context = {
10 + [mutateAndReturn(key)]: identity([props.value]),
11 + };
12 + return context;
13 +}
14 +
15 +export const FIXTURE_ENTRYPOINT = {
16 + fn: Component,
17 + params: [{ value: 42 }],
18 +};
19 +
20 +```
21 +
22 +
23 +## Error
24 +
25 +```
26 +[ReactForget] Todo: (BuildHIR::lowerExpression) Expected Identifier, got CallExpression key in ObjectExpression (6:6)
27 +```
28 +
29 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-object-expression-computed-key-mutate-key-while-constructing-object.js new
+14
@@ -0,0 +1,14 @@
1 +import { identity, mutate, mutateAndReturn } from "shared-runtime";
2 +
3 +function Component(props) {
4 + const key = {};
5 + const context = {
6 + [mutateAndReturn(key)]: identity([props.value]),
7 + };
8 + return context;
9 +}
10 +
11 +export const FIXTURE_ENTRYPOINT = {
12 + fn: Component,
13 + params: [{ value: 42 }],
14 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/object-expression-computed-key-constant-number.expect.md new
+56
@@ -0,0 +1,56 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { identity } from "shared-runtime";
6 +
7 +function Component(props) {
8 + const key = 42;
9 + const context = {
10 + [key]: identity([props.value]),
11 + };
12 + return context;
13 +}
14 +
15 +export const FIXTURE_ENTRYPOINT = {
16 + fn: Component,
17 + params: [{ value: "hello!" }],
18 +};
19 +
20 +```
21 +
22 +## Code
23 +
24 +```javascript
25 +import { unstable_useMemoCache as useMemoCache } from "react";
26 +import { identity } from "shared-runtime";
27 +
28 +function Component(props) {
29 + const $ = useMemoCache(4);
30 + let t0;
31 + if ($[0] !== props.value) {
32 + t0 = identity([props.value]);
33 + $[0] = props.value;
34 + $[1] = t0;
35 + } else {
36 + t0 = $[1];
37 + }
38 + let t1;
39 + if ($[2] !== t0) {
40 + t1 = { [42]: t0 };
41 + $[2] = t0;
42 + $[3] = t1;
43 + } else {
44 + t1 = $[3];
45 + }
46 + const context = t1;
47 + return context;
48 +}
49 +
50 +export const FIXTURE_ENTRYPOINT = {
51 + fn: Component,
52 + params: [{ value: "hello!" }],
53 +};
54 +
55 +```
56 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/object-expression-computed-key-constant-number.js new
+14
@@ -0,0 +1,14 @@
1 +import { identity } from "shared-runtime";
2 +
3 +function Component(props) {
4 + const key = 42;
5 + const context = {
6 + [key]: identity([props.value]),
7 + };
8 + return context;
9 +}
10 +
11 +export const FIXTURE_ENTRYPOINT = {
12 + fn: Component,
13 + params: [{ value: "hello!" }],
14 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/object-expression-computed-key-constant-string.expect.md new
+56
@@ -0,0 +1,56 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { identity } from "shared-runtime";
6 +
7 +function Component(props) {
8 + const key = "KeyName";
9 + const context = {
10 + [key]: identity([props.value]),
11 + };
12 + return context;
13 +}
14 +
15 +export const FIXTURE_ENTRYPOINT = {
16 + fn: Component,
17 + params: [{ value: 42 }],
18 +};
19 +
20 +```
21 +
22 +## Code
23 +
24 +```javascript
25 +import { unstable_useMemoCache as useMemoCache } from "react";
26 +import { identity } from "shared-runtime";
27 +
28 +function Component(props) {
29 + const $ = useMemoCache(4);
30 + let t0;
31 + if ($[0] !== props.value) {
32 + t0 = identity([props.value]);
33 + $[0] = props.value;
34 + $[1] = t0;
35 + } else {
36 + t0 = $[1];
37 + }
38 + let t1;
39 + if ($[2] !== t0) {
40 + t1 = { ["KeyName"]: t0 };
41 + $[2] = t0;
42 + $[3] = t1;
43 + } else {
44 + t1 = $[3];
45 + }
46 + const context = t1;
47 + return context;
48 +}
49 +
50 +export const FIXTURE_ENTRYPOINT = {
51 + fn: Component,
52 + params: [{ value: 42 }],
53 +};
54 +
55 +```
56 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/object-expression-computed-key-constant-string.js new
+14
@@ -0,0 +1,14 @@
1 +import { identity } from "shared-runtime";
2 +
3 +function Component(props) {
4 + const key = "KeyName";
5 + const context = {
6 + [key]: identity([props.value]),
7 + };
8 + return context;
9 +}
10 +
11 +export const FIXTURE_ENTRYPOINT = {
12 + fn: Component,
13 + params: [{ value: 42 }],
14 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/object-expression-computed-key-non-reactive.expect.md new
+60
@@ -0,0 +1,60 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { identity } from "shared-runtime";
6 +
7 +const SCALE = 2;
8 +
9 +function Component(props) {
10 + const key = SCALE;
11 + const context = {
12 + [key]: identity([props.value]),
13 + };
14 + return context;
15 +}
16 +
17 +export const FIXTURE_ENTRYPOINT = {
18 + fn: Component,
19 + params: [{ key: "Sathya", value: "Compiler" }],
20 +};
21 +
22 +```
23 +
24 +## Code
25 +
26 +```javascript
27 +import { unstable_useMemoCache as useMemoCache } from "react";
28 +import { identity } from "shared-runtime";
29 +
30 +const SCALE = 2;
31 +
32 +function Component(props) {
33 + const $ = useMemoCache(4);
34 + let t0;
35 + if ($[0] !== props.value) {
36 + t0 = identity([props.value]);
37 + $[0] = props.value;
38 + $[1] = t0;
39 + } else {
40 + t0 = $[1];
41 + }
42 + let t1;
43 + if ($[2] !== t0) {
44 + t1 = { [SCALE]: t0 };
45 + $[2] = t0;
46 + $[3] = t1;
47 + } else {
48 + t1 = $[3];
49 + }
50 + const context = t1;
51 + return context;
52 +}
53 +
54 +export const FIXTURE_ENTRYPOINT = {
55 + fn: Component,
56 + params: [{ key: "Sathya", value: "Compiler" }],
57 +};
58 +
59 +```
60 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/object-expression-computed-key-non-reactive.js new
+16
@@ -0,0 +1,16 @@
1 +import { identity } from "shared-runtime";
2 +
3 +const SCALE = 2;
4 +
5 +function Component(props) {
6 + const key = SCALE;
7 + const context = {
8 + [key]: identity([props.value]),
9 + };
10 + return context;
11 +}
12 +
13 +export const FIXTURE_ENTRYPOINT = {
14 + fn: Component,
15 + params: [{ key: "Sathya", value: "Compiler" }],
16 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/object-expression-computed-key-object-mutated-later.expect.md new
+67
@@ -0,0 +1,67 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { identity, mutate } from "shared-runtime";
6 +
7 +function Component(props) {
8 + const key = {};
9 + const context = {
10 + [key]: identity([props.value]),
11 + };
12 + mutate(key);
13 + return context;
14 +}
15 +
16 +export const FIXTURE_ENTRYPOINT = {
17 + fn: Component,
18 + params: [{ value: 42 }],
19 +};
20 +
21 +```
22 +
23 +## Code
24 +
25 +```javascript
26 +import { unstable_useMemoCache as useMemoCache } from "react";
27 +import { identity, mutate } from "shared-runtime";
28 +
29 +function Component(props) {
30 + const $ = useMemoCache(5);
31 + let t0;
32 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
33 + t0 = {};
34 + $[0] = t0;
35 + } else {
36 + t0 = $[0];
37 + }
38 + const key = t0;
39 + let t1;
40 + if ($[1] !== props.value) {
41 + t1 = identity([props.value]);
42 + $[1] = props.value;
43 + $[2] = t1;
44 + } else {
45 + t1 = $[2];
46 + }
47 + let t2;
48 + if ($[3] !== t1) {
49 + t2 = { [key]: t1 };
50 + $[3] = t1;
51 + $[4] = t2;
52 + } else {
53 + t2 = $[4];
54 + }
55 + const context = t2;
56 +
57 + mutate(key);
58 + return context;
59 +}
60 +
61 +export const FIXTURE_ENTRYPOINT = {
62 + fn: Component,
63 + params: [{ value: 42 }],
64 +};
65 +
66 +```
67 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/object-expression-computed-key-object-mutated-later.js new
+15
@@ -0,0 +1,15 @@
1 +import { identity, mutate } from "shared-runtime";
2 +
3 +function Component(props) {
4 + const key = {};
5 + const context = {
6 + [key]: identity([props.value]),
7 + };
8 + mutate(key);
9 + return context;
10 +}
11 +
12 +export const FIXTURE_ENTRYPOINT = {
13 + fn: Component,
14 + params: [{ value: 42 }],
15 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/object-expression-computed-key.expect.md new
+62
@@ -0,0 +1,62 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import { identity } from "shared-runtime";
6 +
7 +const SCALE = 2;
8 +
9 +function Component(props) {
10 + const { key } = props;
11 + const context = {
12 + [key]: identity([props.value, SCALE]),
13 + };
14 + return context;
15 +}
16 +
17 +export const FIXTURE_ENTRYPOINT = {
18 + fn: Component,
19 + params: [{ key: "Sathya", value: "Compiler" }],
20 +};
21 +
22 +```
23 +
24 +## Code
25 +
26 +```javascript
27 +import { unstable_useMemoCache as useMemoCache } from "react";
28 +import { identity } from "shared-runtime";
29 +
30 +const SCALE = 2;
31 +
32 +function Component(props) {
33 + const $ = useMemoCache(5);
34 + const { key } = props;
35 + let t0;
36 + if ($[0] !== props.value) {
37 + t0 = identity([props.value, SCALE]);
38 + $[0] = props.value;
39 + $[1] = t0;
40 + } else {
41 + t0 = $[1];
42 + }
43 + let t1;
44 + if ($[2] !== key || $[3] !== t0) {
45 + t1 = { [key]: t0 };
46 + $[2] = key;
47 + $[3] = t0;
48 + $[4] = t1;
49 + } else {
50 + t1 = $[4];
51 + }
52 + const context = t1;
53 + return context;
54 +}
55 +
56 +export const FIXTURE_ENTRYPOINT = {
57 + fn: Component,
58 + params: [{ key: "Sathya", value: "Compiler" }],
59 +};
60 +
61 +```
62 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/object-expression-computed-key.js new
+16
@@ -0,0 +1,16 @@
1 +import { identity } from "shared-runtime";
2 +
3 +const SCALE = 2;
4 +
5 +function Component(props) {
6 + const { key } = props;
7 + const context = {
8 + [key]: identity([props.value, SCALE]),
9 + };
10 + return context;
11 +}
12 +
13 +export const FIXTURE_ENTRYPOINT = {
14 + fn: Component,
15 + params: [{ key: "Sathya", value: "Compiler" }],
16 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo.object-pattern-computed-key.js new
+12
@@ -0,0 +1,12 @@
1 +import { identity } from "shared-runtime";
2 +
3 +const SCALE = 2;
4 +function Component(props) {
5 + const { [props.name]: value } = props;
6 + return value;
7 +}
8 +
9 +export const FIXTURE_ENTRYPOINT = {
10 + fn: Component,
11 + params: [{ name: "Sathya" }],
12 +};
compiler/packages/sprout/src/shared-runtime.ts
+6 -1
@@ -5,8 +5,8 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import React from "react";
8 import { IntlVariations, IntlViewerContext, init } from "fbt";
9 +import React from "react";
10
11 /**
12 * This file is meant for use by `runner-evaluator` and fixture tests.
@@ -68,6 +68,11 @@ export function mutate(arg: any): void {
68 }
69 }
70
71 +export function mutateAndReturn<T>(arg: T): T {
72 + mutate(arg);
73 + return arg;
74 +}
75 +
76 export function setProperty(arg: any, property: any): void {
77 // don't mutate primitive
78 if (typeof arg === null || typeof arg !== "object") {