Basic ForInStatement support
First pass of ForInStatement support. This mostly just copies our handling of ForOfStatement, but the next PR updates to use a different instruction instead of `NextIterableOf`.
Joe Savona committed
Sep 11, 2023 at 15:56 UTC
51497c8c59d008983a40609158f75dc793093730
24 files changed
+582
-18
compiler/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts
+83
-1
@@ -910,6 +910,89 @@ function lowerStatement(
910
);
911
return;
912
}
913
+ case "ForInStatement": {
914
+ const stmt = stmtPath as NodePath<t.ForInStatement>;
915
+ const continuationBlock = builder.reserve("block");
916
+ const initBlock = builder.reserve("loop");
917
+
918
+ const loopBlock = builder.enter("block", (_blockId) => {
919
+ return builder.loop(label, initBlock.id, continuationBlock.id, () => {
920
+ const body = stmt.get("body");
921
+ lowerStatement(builder, body);
922
+ return {
923
+ kind: "goto",
924
+ block: initBlock.id,
925
+ variant: GotoVariant.Continue,
926
+ id: makeInstructionId(0),
927
+ loc: body.node.loc ?? GeneratedSource,
928
+ };
929
+ });
930
+ });
931
+
932
+ const loc = stmt.node.loc ?? GeneratedSource;
933
+ const value = lowerExpressionToTemporary(builder, stmt.get("right"));
934
+ builder.terminateWithContinuation(
935
+ {
936
+ kind: "for-in",
937
+ loc,
938
+ init: initBlock.id,
939
+ loop: loopBlock,
940
+ fallthrough: continuationBlock.id,
941
+ id: makeInstructionId(0),
942
+ },
943
+ initBlock
944
+ );
945
+
946
+ // The init of a ForOf statement is compound over a left (VariableDeclaration | LVal) and
947
+ // right (Expression), so we synthesize a new InstrValue and assignment (potentially multiple
948
+ // instructions when we handle other syntax like Patterns)
949
+ const left = stmt.get("left");
950
+ const leftLoc = left.node.loc ?? GeneratedSource;
951
+ let test: Place;
952
+ if (left.isVariableDeclaration()) {
953
+ const declarations = left.get("declarations");
954
+ CompilerError.invariant(declarations.length === 1, {
955
+ reason: `Expected only one declaration in the init of a ForOfStatement, got ${declarations.length}`,
956
+ description: null,
957
+ loc: left.node.loc ?? null,
958
+ suggestions: null,
959
+ });
960
+ const id = declarations[0].get("id");
961
+ const nextIterableOf = lowerValueToTemporary(builder, {
962
+ kind: "NextIterableOf", // TODO: change this to reflect for-in semantics (returns immutable keys, does not modify collection)
963
+ loc: leftLoc,
964
+ value,
965
+ });
966
+ const assign = lowerAssignment(
967
+ builder,
968
+ leftLoc,
969
+ InstructionKind.Let,
970
+ id,
971
+ nextIterableOf
972
+ );
973
+ test = lowerValueToTemporary(builder, assign);
974
+ } else {
975
+ builder.errors.push({
976
+ reason: `(BuildHIR::lowerStatement) Handle ${left.type} inits in ForOfStatement`,
977
+ severity: ErrorSeverity.Todo,
978
+ loc: left.node.loc ?? null,
979
+ suggestions: null,
980
+ });
981
+ return;
982
+ }
983
+ builder.terminateWithContinuation(
984
+ {
985
+ id: makeInstructionId(0),
986
+ kind: "branch",
987
+ test,
988
+ consequent: loopBlock,
989
+ alternate: continuationBlock.id,
990
+ loc: stmt.node.loc ?? GeneratedSource,
991
+ },
992
+ continuationBlock
993
+ );
994
+ return;
995
+ }
996
case "DebuggerStatement": {
997
const stmt = stmtPath as NodePath<t.DebuggerStatement>;
998
const loc = stmt.node.loc ?? GeneratedSource;
@@ -1028,7 +1111,6 @@ function lowerStatement(
1111
1112
return;
1113
}
1031
- case "ForInStatement":
1114
case "ClassDeclaration":
1115
case "DeclareClass":
1116
case "DeclareExportAllDeclaration":
compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts
+17
@@ -137,6 +137,7 @@ export type ReactiveTerminal =
137
| ReactiveWhileTerminal
138
| ReactiveForTerminal
139
| ReactiveForOfTerminal
140
+ | ReactiveForInTerminal
141
| ReactiveIfTerminal
142
| ReactiveLabelTerminal
143
| ReactiveTryTerminal;
@@ -198,6 +199,12 @@ export type ReactiveForOfTerminal = {
199
loop: ReactiveBlock;
200
id: InstructionId;
201
};
202
+export type ReactiveForInTerminal = {
203
+ kind: "for-in";
204
+ init: ReactiveValue;
205
+ loop: ReactiveBlock;
206
+ id: InstructionId;
207
+};
208
export type ReactiveIfTerminal = {
209
kind: "if";
210
test: Place;
@@ -281,6 +288,7 @@ export type Terminal =
288
| SwitchTerminal
289
| ForTerminal
290
| ForOfTerminal
291
+ | ForInTerminal
292
| DoWhileTerminal
293
| WhileTerminal
294
| LogicalTerminal
@@ -409,6 +417,15 @@ export type ForOfTerminal = {
417
id: InstructionId;
418
};
419
420
+export type ForInTerminal = {
421
+ kind: "for-in";
422
+ loc: SourceLocation;
423
+ init: BlockId;
424
+ loop: BlockId;
425
+ fallthrough: BlockId;
426
+ id: InstructionId;
427
+};
428
+
429
export type LogicalTerminal = {
430
kind: "logical";
431
operator: t.LogicalExpression["operator"];
compiler/packages/babel-plugin-react-forget/src/HIR/HIRBuilder.ts
+2
-4
@@ -745,10 +745,8 @@ export function reversePostorderBlocks(func: HIR): void {
745
visit(terminal.test);
746
break;
747
}
748
- case "for": {
749
- visit(terminal.init);
750
- break;
751
- }
748
+ case "for":
749
+ case "for-in":
750
case "for-of": {
751
visit(terminal.init);
752
break;
compiler/packages/babel-plugin-react-forget/src/HIR/PrintHIR.ts
+5
@@ -97,6 +97,7 @@ export function printMixedHIR(
97
case "unsupported":
98
case "goto":
99
case "do-while":
100
+ case "for-in":
101
case "for-of": {
102
const terminal = printTerminal(value);
103
if (Array.isArray(terminal)) {
@@ -218,6 +219,10 @@ export function printTerminal(terminal: Terminal): Array<string> | string {
219
value = `[${terminal.id}] ForOf init=bb${terminal.init} loop=bb${terminal.loop} fallthrough=bb${terminal.fallthrough}`;
220
break;
221
}
222
+ case "for-in": {
223
+ value = `[${terminal.id}] ForIn init=bb${terminal.init} loop=bb${terminal.loop} fallthrough=bb${terminal.fallthrough}`;
224
+ break;
225
+ }
226
case "label": {
227
value = `[${terminal.id}] Label block=bb${terminal.block} fallthrough=${
228
terminal.fallthrough ? `bb${terminal.fallthrough}` : ""
compiler/packages/babel-plugin-react-forget/src/HIR/visitors.ts
+24
@@ -708,6 +708,19 @@ export function mapTerminalSuccessors(
708
id: makeInstructionId(0),
709
};
710
}
711
+ case "for-in": {
712
+ const init = fn(terminal.init);
713
+ const loop = fn(terminal.loop);
714
+ const fallthrough = fn(terminal.fallthrough);
715
+ return {
716
+ kind: "for-in",
717
+ loc: terminal.loc,
718
+ init,
719
+ loop,
720
+ fallthrough,
721
+ id: makeInstructionId(0),
722
+ };
723
+ }
724
case "label": {
725
const block = fn(terminal.block);
726
const fallthrough =
@@ -787,6 +800,7 @@ export function terminalFallthrough(terminal: Terminal): BlockId | null {
800
case "try":
801
case "do-while":
802
case "for-of":
803
+ case "for-in":
804
case "for":
805
case "if":
806
case "label":
@@ -832,6 +846,10 @@ export function mapOptionalFallthroughs(
846
const _: BlockId = terminal.fallthrough;
847
break;
848
}
849
+ case "for-in": {
850
+ const _: BlockId = terminal.fallthrough;
851
+ break;
852
+ }
853
case "for": {
854
const _: BlockId = terminal.fallthrough;
855
break;
@@ -946,6 +964,10 @@ export function* eachTerminalSuccessor(terminal: Terminal): Iterable<BlockId> {
964
yield terminal.init;
965
break;
966
}
967
+ case "for-in": {
968
+ yield terminal.init;
969
+ break;
970
+ }
971
case "label": {
972
yield terminal.block;
973
break;
@@ -1020,6 +1042,7 @@ export function mapTerminalOperands(
1042
case "while":
1043
case "for":
1044
case "for-of":
1045
+ case "for-in":
1046
case "goto":
1047
case "unsupported": {
1048
// no-op
@@ -1070,6 +1093,7 @@ export function* eachTerminalOperand(terminal: Terminal): Iterable<Place> {
1093
case "while":
1094
case "for":
1095
case "for-of":
1096
+ case "for-in":
1097
case "goto":
1098
case "unsupported": {
1099
// no-op
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/BuildReactiveFunction.ts
+75
@@ -499,6 +499,81 @@ class Driver {
499
}
500
break;
501
}
502
+ case "for-in": {
503
+ const loopId =
504
+ !this.cx.isScheduled(terminal.loop) &&
505
+ terminal.loop !== terminal.fallthrough
506
+ ? terminal.loop
507
+ : null;
508
+
509
+ const fallthroughId =
510
+ terminal.fallthrough !== null &&
511
+ !this.cx.isScheduled(terminal.fallthrough)
512
+ ? terminal.fallthrough
513
+ : null;
514
+
515
+ const scheduleId = this.cx.scheduleLoop(
516
+ terminal.fallthrough,
517
+ terminal.init,
518
+ terminal.loop
519
+ );
520
+ scheduleIds.push(scheduleId);
521
+
522
+ const init = this.visitValueBlock(terminal.init, terminal.loc);
523
+ const initBlock = this.cx.ir.blocks.get(init.block)!;
524
+ let initValue = init.value;
525
+ if (initValue.kind === "SequenceExpression") {
526
+ const last = initBlock.instructions.at(-1)!;
527
+ initValue.instructions.push(last);
528
+ initValue.value = {
529
+ kind: "Primitive",
530
+ value: undefined,
531
+ loc: terminal.loc,
532
+ };
533
+ } else {
534
+ initValue = {
535
+ kind: "SequenceExpression",
536
+ instructions: [initBlock.instructions.at(-1)!],
537
+ id: terminal.id,
538
+ loc: terminal.loc,
539
+ value: {
540
+ kind: "Primitive",
541
+ value: undefined,
542
+ loc: terminal.loc,
543
+ },
544
+ };
545
+ }
546
+
547
+ let loopBody: ReactiveBlock;
548
+ if (loopId) {
549
+ loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
550
+ } else {
551
+ const break_ = this.visitBreak(terminal.loop, null);
552
+ CompilerError.invariant(break_ !== null, {
553
+ reason: "If loop body is already scheduled it must be a break",
554
+ description: null,
555
+ loc: null,
556
+ suggestions: null,
557
+ });
558
+ loopBody = [break_];
559
+ }
560
+
561
+ this.cx.unscheduleAll(scheduleIds);
562
+ blockValue.push({
563
+ kind: "terminal",
564
+ terminal: {
565
+ kind: "for-in",
566
+ init: initValue,
567
+ loop: loopBody,
568
+ id: terminal.id,
569
+ },
570
+ label: fallthroughId,
571
+ });
572
+ if (fallthroughId !== null) {
573
+ this.visitBlock(this.cx.ir.blocks.get(fallthroughId)!, blockValue);
574
+ }
575
+ break;
576
+ }
577
case "branch": {
578
let consequent: ReactiveBlock | null = null;
579
if (this.cx.isScheduled(terminal.consequent)) {
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+22
-9
@@ -403,6 +403,7 @@ function codegenTerminal(
403
codegenBlock(cx, terminal.loop)
404
);
405
}
406
+ case "for-in":
407
case "for-of": {
408
CompilerError.invariant(terminal.init.kind === "SequenceExpression", {
409
reason: `Expected a sequence expression init for ForOf`,
@@ -467,15 +468,27 @@ function codegenTerminal(
468
`Unhandled lvalue kind: ${iterableItem.value.lvalue.kind}`
469
);
470
}
470
- return t.forOfStatement(
471
- // Special handling here since we only want the VariableDeclarators without any inits
472
- // This needs to be updated when we handle non-trivial ForOf inits
473
- createVariableDeclaration(iterableItem.value.loc, varDeclKind, [
474
- t.variableDeclarator(lval, null),
475
- ]),
476
- codegenInstructionValue(cx, iterableCollection.value),
477
- codegenBlock(cx, terminal.loop)
478
- );
471
+ if (terminal.kind === "for-of") {
472
+ return t.forOfStatement(
473
+ // Special handling here since we only want the VariableDeclarators without any inits
474
+ // This needs to be updated when we handle non-trivial ForOf inits
475
+ createVariableDeclaration(iterableItem.value.loc, varDeclKind, [
476
+ t.variableDeclarator(lval, null),
477
+ ]),
478
+ codegenInstructionValue(cx, iterableCollection.value),
479
+ codegenBlock(cx, terminal.loop)
480
+ );
481
+ } else {
482
+ return t.forInStatement(
483
+ // Special handling here since we only want the VariableDeclarators without any inits
484
+ // This needs to be updated when we handle non-trivial ForOf inits
485
+ createVariableDeclaration(iterableItem.value.loc, varDeclKind, [
486
+ t.variableDeclarator(lval, null),
487
+ ]),
488
+ codegenInstructionValue(cx, iterableCollection.value),
489
+ codegenBlock(cx, terminal.loop)
490
+ );
491
+ }
492
}
493
case "if": {
494
const test = codegenPlace(cx, terminal.test);
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/FlattenReactiveLoops.ts
+2
-1
@@ -49,7 +49,8 @@ class Transform extends ReactiveFunctionTransform<boolean> {
49
case "do-while":
50
case "while":
51
case "for":
52
- case "for-of": {
52
+ case "for-of":
53
+ case "for-in": {
54
this.traverseTerminal(stmt, true);
55
break;
56
}
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PrintReactiveFunction.ts
+8
@@ -262,6 +262,14 @@ function printTerminal(writer: Writer, terminal: ReactiveTerminal): void {
262
writer.writeLine("}");
263
break;
264
}
265
+ case "for-in": {
266
+ writer.writeLine(`[${terminal.id}] for-in (`);
267
+ printReactiveValue(writer, terminal.init);
268
+ writer.writeLine(") {");
269
+ printReactiveInstructions(writer, terminal.loop);
270
+ writer.writeLine("}");
271
+ break;
272
+ }
273
case "throw": {
274
writer.writeLine(`[${terminal.id}] throw ${printPlace(terminal.value)}`);
275
break;
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PropagateScopeDependencies.ts
+7
@@ -612,6 +612,13 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
612
});
613
break;
614
}
615
+ case "for-in": {
616
+ this.visitReactiveValue(context, terminal.id, terminal.init);
617
+ context.enterConditional(() => {
618
+ this.visitBlock(terminal.loop, context);
619
+ });
620
+ break;
621
+ }
622
case "do-while": {
623
this.visitBlock(terminal.loop, context);
624
context.enterConditional(() => {
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/visitors.ts
+9
@@ -117,6 +117,11 @@ export class ReactiveFunctionVisitor<TState = void> {
117
this.visitBlock(terminal.loop, state);
118
break;
119
}
120
+ case "for-in": {
121
+ this.visitValue(terminal.id, terminal.init, state);
122
+ this.visitBlock(terminal.loop, state);
123
+ break;
124
+ }
125
case "do-while": {
126
this.visitBlock(terminal.loop, state);
127
this.visitValue(terminal.id, terminal.test, state);
@@ -342,6 +347,10 @@ export function mapTerminalBlocks(
347
terminal.loop = fn(terminal.loop);
348
break;
349
}
350
+ case "for-in": {
351
+ terminal.loop = fn(terminal.loop);
352
+ break;
353
+ }
354
case "do-while":
355
case "while": {
356
terminal.loop = fn(terminal.loop);
compiler/packages/babel-plugin-react-forget/src/SSA/LeaveSSA.ts
+5
-1
@@ -352,7 +352,11 @@ export function leaveSSA(fn: HIRFunction): void {
352
const loop = fn.body.blocks.get(terminal.loop)!;
353
pushPhis(loop);
354
}
355
- if (terminal.kind === "for" || terminal.kind === "for-of") {
355
+ if (
356
+ terminal.kind === "for" ||
357
+ terminal.kind === "for-of" ||
358
+ terminal.kind === "for-in"
359
+ ) {
360
const init = fn.body.blocks.get(terminal.init)!;
361
pushPhis(init);
362
compiler/packages/babel-plugin-react-forget/src/Utils/VisualizeHIRMermaid.ts
+2
@@ -139,6 +139,7 @@ function printTerminalLabel(terminal: Terminal): string {
139
buffer.push("Sequence");
140
break;
141
}
142
+ case "for-in":
143
case "try":
144
case "maybe-throw":
145
case "unsupported": {
@@ -248,6 +249,7 @@ function printTerminalArrows(blockId: BlockId, terminal: Terminal): string {
249
buffer.push(printJumpArrow(blockId, terminal.fallthrough, "fallthrough"));
250
break;
251
}
252
+ case "for-in":
253
case "try":
254
case "maybe-throw":
255
case "throw":
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md
-2
@@ -107,8 +107,6 @@ let moduleLocal = false;
107
108
[ReactForget] Todo: (BuildHIR::lowerStatement) Handle ObjectPattern inits in ForOfStatement (40:40)
109
110
-[ReactForget] Todo: (BuildHIR::lowerStatement) Handle ForInStatement statements (43:44)
111
-
110
[ReactForget] Todo: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument (49:49)
111
112
[ReactForget] Todo: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument (50:50)
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-in-statement-body-always-returns.expect.md
new
+35
@@ -0,0 +1,35 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ for (const x in props.value) {
7
+ return x;
8
+ }
9
+ return null;
10
+}
11
+
12
+export const FIXTURE_ENTRYPOINT = {
13
+ fn: Component,
14
+ params: [{ value: { a: "A!" } }],
15
+};
16
+
17
+```
18
+
19
+## Code
20
+
21
+```javascript
22
+function Component(props) {
23
+ for (const x in props.value) {
24
+ return x;
25
+ }
26
+ return null;
27
+}
28
+
29
+export const FIXTURE_ENTRYPOINT = {
30
+ fn: Component,
31
+ params: [{ value: { a: "A!" } }],
32
+};
33
+
34
+```
35
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-in-statement-body-always-returns.js
new
+11
@@ -0,0 +1,11 @@
1
+function Component(props) {
2
+ for (const x in props.value) {
3
+ return x;
4
+ }
5
+ return null;
6
+}
7
+
8
+export const FIXTURE_ENTRYPOINT = {
9
+ fn: Component,
10
+ params: [{ value: { a: "A!" } }],
11
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-in-statement-break.expect.md
new
+66
@@ -0,0 +1,66 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ let x;
7
+ const object = { ...props.value };
8
+ for (const y in object) {
9
+ if (y === "break") {
10
+ break;
11
+ }
12
+ x = object[y];
13
+ }
14
+ return x;
15
+}
16
+
17
+export const FIXTURE_ENTRYPOINT = {
18
+ fn: Component,
19
+ // should return 'a'
20
+ params: [{ a: "a", break: null, c: "C!" }],
21
+};
22
+
23
+```
24
+
25
+## Code
26
+
27
+```javascript
28
+import { unstable_useMemoCache as useMemoCache } from "react";
29
+function Component(props) {
30
+ const $ = useMemoCache(4);
31
+ const c_0 = $[0] !== props.value;
32
+ let x;
33
+ if (c_0) {
34
+ const c_2 = $[2] !== props.value;
35
+ let t0;
36
+ if (c_2) {
37
+ t0 = { ...props.value };
38
+ $[2] = props.value;
39
+ $[3] = t0;
40
+ } else {
41
+ t0 = $[3];
42
+ }
43
+ const object = t0;
44
+ for (const y in object) {
45
+ if (y === "break") {
46
+ break;
47
+ }
48
+
49
+ x = object[y];
50
+ }
51
+ $[0] = props.value;
52
+ $[1] = x;
53
+ } else {
54
+ x = $[1];
55
+ }
56
+ return x;
57
+}
58
+
59
+export const FIXTURE_ENTRYPOINT = {
60
+ fn: Component,
61
+ // should return 'a'
62
+ params: [{ a: "a", break: null, c: "C!" }],
63
+};
64
+
65
+```
66
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-in-statement-break.js
new
+17
@@ -0,0 +1,17 @@
1
+function Component(props) {
2
+ let x;
3
+ const object = { ...props.value };
4
+ for (const y in object) {
5
+ if (y === "break") {
6
+ break;
7
+ }
8
+ x = object[y];
9
+ }
10
+ return x;
11
+}
12
+
13
+export const FIXTURE_ENTRYPOINT = {
14
+ fn: Component,
15
+ // should return 'a'
16
+ params: [{ a: "a", break: null, c: "C!" }],
17
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-in-statement-continue.expect.md
new
+62
@@ -0,0 +1,62 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ let x;
7
+ const object = { ...props.value };
8
+ for (const y in object) {
9
+ if (y === "continue") {
10
+ continue;
11
+ }
12
+ x = object[y];
13
+ }
14
+ return x;
15
+}
16
+
17
+export const FIXTURE_ENTRYPOINT = {
18
+ fn: Component,
19
+ params: [{ value: { a: "a", continue: "skip", b: "b!" } }],
20
+};
21
+
22
+```
23
+
24
+## Code
25
+
26
+```javascript
27
+import { unstable_useMemoCache as useMemoCache } from "react";
28
+function Component(props) {
29
+ const $ = useMemoCache(3);
30
+ let x;
31
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
32
+ const c_1 = $[1] !== props.value;
33
+ let t0;
34
+ if (c_1) {
35
+ t0 = { ...props.value };
36
+ $[1] = props.value;
37
+ $[2] = t0;
38
+ } else {
39
+ t0 = $[2];
40
+ }
41
+ const object = t0;
42
+ for (const y in object) {
43
+ if (y === "continue") {
44
+ continue;
45
+ }
46
+
47
+ x = object[y];
48
+ }
49
+ $[0] = x;
50
+ } else {
51
+ x = $[0];
52
+ }
53
+ return x;
54
+}
55
+
56
+export const FIXTURE_ENTRYPOINT = {
57
+ fn: Component,
58
+ params: [{ value: { a: "a", continue: "skip", b: "b!" } }],
59
+};
60
+
61
+```
62
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-in-statement-continue.js
new
+16
@@ -0,0 +1,16 @@
1
+function Component(props) {
2
+ let x;
3
+ const object = { ...props.value };
4
+ for (const y in object) {
5
+ if (y === "continue") {
6
+ continue;
7
+ }
8
+ x = object[y];
9
+ }
10
+ return x;
11
+}
12
+
13
+export const FIXTURE_ENTRYPOINT = {
14
+ fn: Component,
15
+ params: [{ value: { a: "a", continue: "skip", b: "b!" } }],
16
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-in-statement-empty-body.expect.md
new
+35
@@ -0,0 +1,35 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ let x;
7
+ for (const y in props.value) {
8
+ }
9
+ return x;
10
+}
11
+
12
+export const FIXTURE_ENTRYPOINT = {
13
+ fn: Component,
14
+ params: [{ value: { a: "a", b: "B", c: "C!" } }],
15
+};
16
+
17
+```
18
+
19
+## Code
20
+
21
+```javascript
22
+function Component(props) {
23
+ let x;
24
+ for (const y in props.value) {
25
+ }
26
+ return x;
27
+}
28
+
29
+export const FIXTURE_ENTRYPOINT = {
30
+ fn: Component,
31
+ params: [{ value: { a: "a", b: "B", c: "C!" } }],
32
+};
33
+
34
+```
35
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-in-statement-empty-body.js
new
+11
@@ -0,0 +1,11 @@
1
+function Component(props) {
2
+ let x;
3
+ for (const y in props.value) {
4
+ }
5
+ return x;
6
+}
7
+
8
+export const FIXTURE_ENTRYPOINT = {
9
+ fn: Component,
10
+ params: [{ value: { a: "a", b: "B", c: "C!" } }],
11
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-in-statement.expect.md
new
+56
@@ -0,0 +1,56 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ let items = [];
7
+ for (const key in props) {
8
+ items.push(<div key={key}>{key}</div>);
9
+ }
10
+ return <div>{items}</div>;
11
+}
12
+
13
+export const FIXTURE_ENTRYPOINT = {
14
+ fn: Component,
15
+ params: [{ hello: null, world: undefined, "!": true }],
16
+};
17
+
18
+```
19
+
20
+## Code
21
+
22
+```javascript
23
+import { unstable_useMemoCache as useMemoCache } from "react";
24
+function Component(props) {
25
+ const $ = useMemoCache(4);
26
+ const c_0 = $[0] !== props;
27
+ let items;
28
+ if (c_0) {
29
+ items = [];
30
+ for (const key in props) {
31
+ items.push(<div key={key}>{key}</div>);
32
+ }
33
+ $[0] = props;
34
+ $[1] = items;
35
+ } else {
36
+ items = $[1];
37
+ }
38
+ const c_2 = $[2] !== items;
39
+ let t0;
40
+ if (c_2) {
41
+ t0 = <div>{items}</div>;
42
+ $[2] = items;
43
+ $[3] = t0;
44
+ } else {
45
+ t0 = $[3];
46
+ }
47
+ return t0;
48
+}
49
+
50
+export const FIXTURE_ENTRYPOINT = {
51
+ fn: Component,
52
+ params: [{ hello: null, world: undefined, "!": true }],
53
+};
54
+
55
+```
56
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-in-statement.js
new
+12
@@ -0,0 +1,12 @@
1
+function Component(props) {
2
+ let items = [];
3
+ for (const key in props) {
4
+ items.push(<div key={key}>{key}</div>);
5
+ }
6
+ return <div>{items}</div>;
7
+}
8
+
9
+export const FIXTURE_ENTRYPOINT = {
10
+ fn: Component,
11
+ params: [{ hello: null, world: undefined, "!": true }],
12
+};