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") {