The library for web and native user interfaces.
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
+};