[rhir] Refactor ReactiveScopeDependency, conditional dependencies (2/2)
--- See comment block in `PropagateScopeDependencies` and added test case `reduce-reactive-conditional-dependencies` for correctness properties / dependency merging logic.
Mofei Zhang committed
Feb 28, 2023 at 16:35 UTC
6b129b59ed76d530882ecf1ea2e1eb7fc1bd6ad4
22 files changed
+828
-210
compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts
+195
-58
@@ -22,9 +22,7 @@ import {
22
ReactiveValue,
23
} from "../HIR/HIR";
24
import { eachInstructionValueOperand } from "../HIR/visitors";
25
-import { todoInvariant } from "../Utils/todo";
25
import { assertExhaustive } from "../Utils/utils";
27
-import { eachReactiveValueOperand } from "./visitors";
26
27
/**
28
* Infers the dependencies of each scope to include variables whose values
@@ -60,10 +58,10 @@ type Scopes = Array<ReactiveScope>;
58
// TODO(@mofeiZ): remove once we replace Context.#dependencies, #properties with tree
59
// representation
60
function areDependenciesEqual(
63
- dep1: ReactiveScopeDependency,
64
- dep2: ReactiveScopeDependency
61
+ dep1: ReactiveScopeDependencyInfo,
62
+ dep2: ReactiveScopeDependencyInfo
63
): boolean {
66
- if (dep1.identifier.id !== dep2.identifier.id) {
64
+ if (dep1.identifier.id !== dep2.identifier.id || dep1.cond !== dep2.cond) {
65
return false;
66
}
67
const dep1Path = dep1.path;
@@ -75,7 +73,7 @@ function areDependenciesEqual(
73
} else if (
74
dep1Path === null ||
75
dep2Path === null ||
78
- dep2Path.length != dep1Path.length
76
+ dep2Path.length !== dep1Path.length
77
) {
78
return false;
79
}
@@ -85,6 +83,8 @@ function areDependenciesEqual(
83
});
84
}
85
86
+type ReactiveScopeDependencyInfo = ReactiveScopeDependency & { cond: boolean };
87
+
88
/**
89
* Enum representing the access type of single property on a parent object.
90
* We distinguish on two independent axes:
@@ -97,23 +97,59 @@ function areDependenciesEqual(
97
* - Dependency: this property is read as a dependency and we must track changes
98
* to it for correctness.
99
*
100
+ * ```javascript
101
+ * // props.a is a dependency here and must be tracked
102
+ * deps: {props.a, props.a.b} ---> minimalDeps: {props.a}
103
+ * // props.a is just an access here and does not need to be tracked
104
+ * deps: {props.a.b} ---> minimalDeps: {props.a.b}
105
+ * ```
106
*/
107
enum PropertyAccessType {
108
+ ConditionalAccess = "ConditionalAccess",
109
UnconditionalAccess = "UnconditionalAccess",
110
+ ConditionalDependency = "ConditionalDependency",
111
UnconditionalDependency = "UnconditionalDependency",
112
}
113
114
+function isUnconditional(access: PropertyAccessType) {
115
+ return (
116
+ access === PropertyAccessType.UnconditionalAccess ||
117
+ access === PropertyAccessType.UnconditionalDependency
118
+ );
119
+}
120
+function isDependency(access: PropertyAccessType) {
121
+ return (
122
+ access === PropertyAccessType.ConditionalDependency ||
123
+ access === PropertyAccessType.UnconditionalDependency
124
+ );
125
+}
126
+
127
function merge(
128
access1: PropertyAccessType,
129
access2: PropertyAccessType
130
): PropertyAccessType {
110
- if (
111
- access1 === PropertyAccessType.UnconditionalDependency ||
112
- access2 === PropertyAccessType.UnconditionalDependency
113
- ) {
114
- return PropertyAccessType.UnconditionalDependency;
131
+ const resultIsUnconditional =
132
+ isUnconditional(access1) || isUnconditional(access2);
133
+ const resultIsDependency = isDependency(access1) || isDependency(access2);
134
+
135
+ // Straightforward merge.
136
+ // This can be represented as bitwise OR, but is written out for readability
137
+ //
138
+ // Observe that `UnconditionalAccess | ConditionalDependency` produces an
139
+ // unconditionally accessed conditional dependency. We currently use these
140
+ // as we use unconditional dependencies. (i.e. to codegen change variables)
141
+ if (resultIsUnconditional) {
142
+ if (resultIsDependency) {
143
+ return PropertyAccessType.UnconditionalDependency;
144
+ } else {
145
+ return PropertyAccessType.UnconditionalAccess;
146
+ }
147
} else {
116
- return PropertyAccessType.UnconditionalAccess;
148
+ if (resultIsDependency) {
149
+ return PropertyAccessType.ConditionalDependency;
150
+ } else {
151
+ return PropertyAccessType.ConditionalAccess;
152
+ }
153
}
154
}
155
@@ -134,12 +170,24 @@ const promoteUncondResult = [
170
},
171
];
172
173
+const promoteCondResult = [
174
+ {
175
+ relativePath: [],
176
+ accessType: PropertyAccessType.ConditionalDependency,
177
+ },
178
+];
179
+
180
+/**
181
+ * Recursively calculates minimal dependencies in a subtree.
182
+ * @param dep DependencyNode representing a dependency subtree.
183
+ * @returns a minimal list of dependencies in this subtree.
184
+ */
185
function deriveMinimalDependenciesInSubtree(
186
dep: DependencyNode
187
): Array<ReduceResultNode> {
188
const results: Array<ReduceResultNode> = [];
189
for (const [childName, childNode] of dep.properties) {
142
- const reduceResult = deriveMinimalDependenciesInSubtree(childNode).map(
190
+ const childResult = deriveMinimalDependenciesInSubtree(childNode).map(
191
({ relativePath, accessType }) => {
192
return {
193
relativePath: [childName, ...relativePath],
@@ -147,7 +195,7 @@ function deriveMinimalDependenciesInSubtree(
195
};
196
}
197
);
150
- results.push(...reduceResult);
198
+ results.push(...childResult);
199
}
200
201
switch (dep.accessType) {
@@ -155,13 +203,42 @@ function deriveMinimalDependenciesInSubtree(
203
return promoteUncondResult;
204
}
205
case PropertyAccessType.UnconditionalAccess: {
158
- // all children are unconditional dependencies, return them to preserve granularity
159
- return results;
206
+ if (
207
+ results.every(
208
+ ({ accessType }) =>
209
+ accessType === PropertyAccessType.UnconditionalDependency
210
+ )
211
+ ) {
212
+ // all children are unconditional dependencies, return them to preserve granularity
213
+ return results;
214
+ } else {
215
+ // at least one child is accessed conditionally, so this node needs to be promoted to
216
+ // unconditional dependency
217
+ return promoteUncondResult;
218
+ }
219
+ }
220
+ case PropertyAccessType.ConditionalAccess:
221
+ case PropertyAccessType.ConditionalDependency: {
222
+ if (
223
+ results.every(
224
+ ({ accessType }) =>
225
+ accessType === PropertyAccessType.ConditionalDependency
226
+ )
227
+ ) {
228
+ // No children are accessed unconditionally, so we cannot promote this node to
229
+ // unconditional access.
230
+ // Truncate results of child nodes here, since we shouldn't access them anyways
231
+ return promoteCondResult;
232
+ } else {
233
+ // at least one child is accessed unconditionally, so this node can be promoted to
234
+ // unconditional dependency
235
+ return promoteUncondResult;
236
+ }
237
}
238
default: {
162
- todoInvariant(
163
- false,
164
- "[PropgateScopeDependencies] Handle conditional dependencies."
239
+ assertExhaustive(
240
+ dep.accessType,
241
+ "[PropgateScopeDependencies] Unhandled access type!"
242
);
243
}
244
}
@@ -193,7 +270,7 @@ function deriveMinimalDependenciesInSubtree(
270
// TODO(@mofeiZ): change once we replace Context.#dependencies, #properties with tree
271
// representation
272
function deriveMinimalDependencies(
196
- initialDeps: Set<ReactiveScopeDependency>
273
+ initialDeps: Set<ReactiveScopeDependencyInfo>
274
): Set<ReactiveScopeDependency> {
275
const depRoots = new Map<IdentifierId, [Identifier, DependencyNode]>();
276
@@ -209,9 +286,13 @@ function deriveMinimalDependencies(
286
depRoots.set(dep.identifier.id, [dep.identifier, root]);
287
}
288
let currNode: DependencyNode = root;
212
- // TODO(@mofeiZ) add conditional access/dependencies here
213
- const accessType = PropertyAccessType.UnconditionalAccess;
214
- const depType = PropertyAccessType.UnconditionalDependency;
289
+
290
+ const accessType = dep.cond
291
+ ? PropertyAccessType.ConditionalAccess
292
+ : PropertyAccessType.UnconditionalAccess;
293
+ const depType = dep.cond
294
+ ? PropertyAccessType.ConditionalDependency
295
+ : PropertyAccessType.UnconditionalDependency;
296
297
for (const property of path) {
298
// all properties read 'on the way' to a dependency are marked as 'access'
@@ -256,20 +337,49 @@ function deriveMinimalDependencies(
337
class Context {
338
#declarations: DeclMap = new Map();
339
#reassignments: Map<Identifier, Decl> = new Map();
259
- #dependencies: Set<ReactiveScopeDependency> = new Set();
260
- #properties: Map<Identifier, ReactiveScopeDependency> = new Map();
340
+ #dependencies: Set<ReactiveScopeDependencyInfo> = new Set();
341
+ #properties: Map<Identifier, ReactiveScopeDependencyInfo> = new Map();
342
#temporaries: Map<Identifier, Place> = new Map();
343
+ #inConditionalWithinScope: boolean = false;
344
#scopes: Scopes = [];
345
346
enter(scope: ReactiveScope, fn: () => void): Set<ReactiveScopeDependency> {
347
+ // Save context of previous scope
348
+ const prevInConditional = this.#inConditionalWithinScope;
349
const previousDependencies = this.#dependencies;
266
- const scopedDependencies = new Set<ReactiveScopeDependency>();
350
+
351
+ // Set context for new scope
352
+ // A nested scope should add all deps it directly uses as its own
353
+ // unconditional deps, regardless of whether the nested scope is itself
354
+ // within a conditional
355
+ const scopedDependencies = new Set<ReactiveScopeDependencyInfo>();
356
+ this.#inConditionalWithinScope = false;
357
this.#dependencies = scopedDependencies;
358
this.#scopes.push(scope);
359
+
360
fn();
361
+
362
+ // Restore context of previous scope
363
this.#scopes.pop();
364
this.#dependencies = previousDependencies;
272
- return deriveMinimalDependencies(scopedDependencies);
365
+ this.#inConditionalWithinScope = prevInConditional;
366
+
367
+ const minScopeDependencies = deriveMinimalDependencies(scopedDependencies);
368
+ // propagate dependencies upward using the same rules as normal dependency
369
+ // collection. child scopes may have dependencies on values created within
370
+ // the outer scope, which necessarily cannot be dependencies of the outer
371
+ // scope
372
+ for (const dep of minScopeDependencies) {
373
+ this.visitDependency({ ...dep, cond: this.#inConditionalWithinScope });
374
+ }
375
+ return minScopeDependencies;
376
+ }
377
+
378
+ enterConditional(fn: () => void): void {
379
+ const prevInConditional = this.#inConditionalWithinScope;
380
+ this.#inConditionalWithinScope = true;
381
+ fn();
382
+ this.#inConditionalWithinScope = prevInConditional;
383
}
384
385
/**
@@ -292,16 +402,18 @@ class Context {
402
declareProperty(lvalue: Place, object: Place, property: string): void {
403
const resolvedObject = this.#temporaries.get(object.identifier) ?? object;
404
const objectDependency = this.#properties.get(resolvedObject.identifier);
295
- let nextDependency: ReactiveScopeDependency;
405
+ let nextDependency: ReactiveScopeDependencyInfo;
406
if (objectDependency === undefined) {
407
nextDependency = {
408
identifier: resolvedObject.identifier,
409
path: [property],
410
+ cond: this.#inConditionalWithinScope,
411
};
412
} else {
413
nextDependency = {
414
identifier: objectDependency.identifier,
415
path: [...(objectDependency.path ?? []), property],
416
+ cond: this.#inConditionalWithinScope,
417
};
418
}
419
this.#properties.set(lvalue.identifier, nextDependency);
@@ -317,29 +429,35 @@ class Context {
429
430
visitOperand(place: Place): void {
431
const resolved = this.#temporaries.get(place.identifier) ?? place;
320
- this.visitDependency({ identifier: resolved.identifier, path: null });
432
+ this.visitDependency({
433
+ identifier: resolved.identifier,
434
+ path: null,
435
+ cond: this.#inConditionalWithinScope,
436
+ });
437
}
438
439
visitProperty(object: Place, property: string): void {
440
const resolvedObject = this.#temporaries.get(object.identifier) ?? object;
441
const objectDependency = this.#properties.get(resolvedObject.identifier);
326
- let nextDependency: ReactiveScopeDependency;
442
+ let nextDependency: ReactiveScopeDependencyInfo;
443
if (objectDependency === undefined) {
444
nextDependency = {
445
identifier: resolvedObject.identifier,
446
path: [property],
447
+ cond: this.#inConditionalWithinScope,
448
};
449
} else {
450
nextDependency = {
451
identifier: objectDependency.identifier,
452
path: [...(objectDependency.path ?? []), property],
453
+ cond: this.#inConditionalWithinScope,
454
};
455
}
456
this.visitDependency(nextDependency);
457
}
458
341
- visitDependency(dependency: ReactiveScopeDependency): void {
342
- let maybeDependency: ReactiveScopeDependency;
459
+ visitDependency(dependency: ReactiveScopeDependencyInfo): void {
460
+ let maybeDependency: ReactiveScopeDependencyInfo;
461
if (dependency.path !== null) {
462
// Operands may have memberPaths when propagating depenencies of an inner scope upward
463
// In this case we use the dependency as-is
@@ -349,12 +467,11 @@ class Context {
467
// the expanded Place. Fall back to using the operand as-is.
468
let propDep = this.#properties.get(dependency.identifier);
469
if (dependency.identifier.name === null && propDep !== undefined) {
352
- maybeDependency = propDep;
470
+ maybeDependency = { ...propDep, cond: dependency.cond };
471
} else {
472
maybeDependency = dependency;
473
}
474
}
357
-
475
// Any value used after its originally defining scope has concluded must be added as an
476
// output of its defining scope. Regardless of whether its a const or not,
477
// some later code needs access to the value. If the current
@@ -429,13 +546,6 @@ function visit(context: Context, block: ReactiveBlock): void {
546
visit(context, item.instructions);
547
});
548
item.scope.dependencies = scopeDependencies;
432
- for (const dep of scopeDependencies) {
433
- // propagate dependencies upward using the same rules as
434
- // normal dependency collection. child scopes may have dependencies
435
- // on values created within the outer scope, which necessarily cannot
436
- // be dependencies of the outer scope
437
- context.visitDependency(dep);
438
- }
549
break;
550
}
551
case "instruction": {
@@ -462,30 +572,53 @@ function visit(context: Context, block: ReactiveBlock): void {
572
case "for": {
573
visitReactiveValue(context, terminal.init);
574
visitReactiveValue(context, terminal.test);
465
- visitReactiveValue(context, terminal.update);
466
- visit(context, terminal.loop);
575
+ context.enterConditional(() => {
576
+ visitReactiveValue(context, terminal.update);
577
+ visit(context, terminal.loop);
578
+ });
579
break;
580
}
581
case "while": {
582
visitReactiveValue(context, terminal.test);
471
- visit(context, terminal.loop);
583
+ context.enterConditional(() => {
584
+ visit(context, terminal.loop);
585
+ });
586
break;
587
}
588
case "if": {
589
context.visitOperand(terminal.test);
476
- visit(context, terminal.consequent);
477
- if (terminal.alternate !== null) {
478
- visit(context, terminal.alternate);
479
- }
590
+ /**
591
+ * TODO: Track dependencies always accessed within consequent and ones
592
+ * always accessed within alternate. If a dependency is always accessed in
593
+ * both, we can promote it to an unconditional dependency.
594
+ *
595
+ * e.g. props.a.b is unconditionally accessed here.
596
+ * if (foo(...)) {
597
+ * access(props.a.b);
598
+ * } else {
599
+ * access(props.a.b);
600
+ * }
601
+ *
602
+ * To deal with nested if-branches, enterConditional should return a list
603
+ * of dependencies unconditionally accessed within the callback.
604
+ */
605
+ context.enterConditional(() => {
606
+ visit(context, terminal.consequent);
607
+ if (terminal.alternate !== null) {
608
+ visit(context, terminal.alternate);
609
+ }
610
+ });
611
break;
612
}
613
case "switch": {
614
context.visitOperand(terminal.test);
484
- for (const case_ of terminal.cases) {
485
- if (case_.block !== undefined) {
486
- visit(context, case_.block);
615
+ context.enterConditional(() => {
616
+ for (const case_ of terminal.cases) {
617
+ if (case_.block !== undefined) {
618
+ visit(context, case_.block);
619
+ }
620
}
488
- }
621
+ });
622
break;
623
}
624
default: {
@@ -508,13 +641,19 @@ function visitReactiveValue(context: Context, value: ReactiveValue): void {
641
switch (value.kind) {
642
case "LogicalExpression": {
643
visitReactiveValue(context, value.left);
511
- visitReactiveValue(context, value.right);
644
+
645
+ context.enterConditional(() => {
646
+ visitReactiveValue(context, value.right);
647
+ });
648
break;
649
}
650
case "ConditionalExpression": {
651
visitReactiveValue(context, value.test);
516
- visitReactiveValue(context, value.consequent);
517
- visitReactiveValue(context, value.alternate);
652
+
653
+ context.enterConditional(() => {
654
+ visitReactiveValue(context, value.consequent);
655
+ visitReactiveValue(context, value.alternate);
656
+ });
657
break;
658
}
659
case "SequenceExpression": {
@@ -553,9 +692,7 @@ function visitInstructionValue(
692
context.visitProperty(value.object, value.property);
693
}
694
} else {
556
- for (const operand of eachReactiveValueOperand(value)) {
557
- context.visitOperand(operand);
558
- }
695
+ visitReactiveValue(context, value);
696
}
697
}
698
compiler/forget/src/__tests__/fixtures/hir/conditional-break.expect.md
+18
-36
@@ -92,13 +92,10 @@ function ComponentA(props) {
92
* props.b *does* influence `a`
93
*/
94
function ComponentB(props) {
95
- const $ = React.unstable_useMemoCache(5);
96
- const c_0 = $[0] !== props.a;
97
- const c_1 = $[1] !== props.b;
98
- const c_2 = $[2] !== props.c;
99
- const c_3 = $[3] !== props.d;
95
+ const $ = React.unstable_useMemoCache(2);
96
+ const c_0 = $[0] !== props;
97
let a;
101
- if (c_0 || c_1 || c_2 || c_3) {
98
+ if (c_0) {
99
a = [];
100
a.push(props.a);
101
if (props.b) {
@@ -106,13 +103,10 @@ function ComponentB(props) {
103
}
104
105
a.push(props.d);
109
- $[0] = props.a;
110
- $[1] = props.b;
111
- $[2] = props.c;
112
- $[3] = props.d;
113
- $[4] = a;
106
+ $[0] = props;
107
+ $[1] = a;
108
} else {
115
- a = $[4];
109
+ a = $[1];
110
}
111
return a;
112
}
@@ -121,13 +115,10 @@ function ComponentB(props) {
115
* props.b *does* influence `a`, but only in a way that is never observable
116
*/
117
function ComponentC(props) {
124
- const $ = React.unstable_useMemoCache(5);
125
- const c_0 = $[0] !== props.a;
126
- const c_1 = $[1] !== props.b;
127
- const c_2 = $[2] !== props.c;
128
- const c_3 = $[3] !== props.d;
118
+ const $ = React.unstable_useMemoCache(2);
119
+ const c_0 = $[0] !== props;
120
let a;
130
- if (c_0 || c_1 || c_2 || c_3) {
121
+ if (c_0) {
122
a = [];
123
a.push(props.a);
124
if (props.b) {
@@ -136,13 +127,10 @@ function ComponentC(props) {
127
}
128
129
a.push(props.d);
139
- $[0] = props.a;
140
- $[1] = props.b;
141
- $[2] = props.c;
142
- $[3] = props.d;
143
- $[4] = a;
130
+ $[0] = props;
131
+ $[1] = a;
132
} else {
145
- a = $[4];
133
+ a = $[1];
134
}
135
return a;
136
}
@@ -151,13 +139,10 @@ function ComponentC(props) {
139
* props.b *does* influence `a`
140
*/
141
function ComponentD(props) {
154
- const $ = React.unstable_useMemoCache(5);
155
- const c_0 = $[0] !== props.a;
156
- const c_1 = $[1] !== props.b;
157
- const c_2 = $[2] !== props.c;
158
- const c_3 = $[3] !== props.d;
142
+ const $ = React.unstable_useMemoCache(2);
143
+ const c_0 = $[0] !== props;
144
let a;
160
- if (c_0 || c_1 || c_2 || c_3) {
145
+ if (c_0) {
146
a = [];
147
a.push(props.a);
148
if (props.b) {
@@ -166,13 +151,10 @@ function ComponentD(props) {
151
}
152
153
a.push(props.d);
169
- $[0] = props.a;
170
- $[1] = props.b;
171
- $[2] = props.c;
172
- $[3] = props.d;
173
- $[4] = a;
154
+ $[0] = props;
155
+ $[1] = a;
156
} else {
175
- a = $[4];
157
+ a = $[1];
158
}
159
return a;
160
}
compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md
+30
-38
@@ -35,13 +35,11 @@ function mayMutate() {}
35
36
```javascript
37
function ComponentA(props) {
38
- const $ = React.unstable_useMemoCache(8);
39
- const c_0 = $[0] !== props.p0;
40
- const c_1 = $[1] !== props.p1;
41
- const c_2 = $[2] !== props.p2;
38
+ const $ = React.unstable_useMemoCache(6);
39
+ const c_0 = $[0] !== props;
40
let a;
41
let b;
44
- if (c_0 || c_1 || c_2) {
42
+ if (c_0) {
43
a = [];
44
b = [];
45
if (b) {
@@ -50,37 +48,33 @@ function ComponentA(props) {
48
if (props.p1) {
49
b.push(props.p2);
50
}
53
- $[0] = props.p0;
54
- $[1] = props.p1;
55
- $[2] = props.p2;
56
- $[3] = a;
57
- $[4] = b;
51
+ $[0] = props;
52
+ $[1] = a;
53
+ $[2] = b;
54
} else {
59
- a = $[3];
60
- b = $[4];
55
+ a = $[1];
56
+ b = $[2];
57
}
62
- const c_5 = $[5] !== a;
63
- const c_6 = $[6] !== b;
58
+ const c_3 = $[3] !== a;
59
+ const c_4 = $[4] !== b;
60
let t0;
65
- if (c_5 || c_6) {
61
+ if (c_3 || c_4) {
62
t0 = <Foo a={a} b={b}></Foo>;
67
- $[5] = a;
68
- $[6] = b;
69
- $[7] = t0;
63
+ $[3] = a;
64
+ $[4] = b;
65
+ $[5] = t0;
66
} else {
71
- t0 = $[7];
67
+ t0 = $[5];
68
}
69
return t0;
70
}
71
72
function ComponentB(props) {
77
- const $ = React.unstable_useMemoCache(8);
78
- const c_0 = $[0] !== props.p0;
79
- const c_1 = $[1] !== props.p1;
80
- const c_2 = $[2] !== props.p2;
73
+ const $ = React.unstable_useMemoCache(6);
74
+ const c_0 = $[0] !== props;
75
let a;
76
let b;
83
- if (c_0 || c_1 || c_2) {
77
+ if (c_0) {
78
a = [];
79
b = [];
80
if (mayMutate(b)) {
@@ -89,25 +83,23 @@ function ComponentB(props) {
83
if (props.p1) {
84
b.push(props.p2);
85
}
92
- $[0] = props.p0;
93
- $[1] = props.p1;
94
- $[2] = props.p2;
95
- $[3] = a;
96
- $[4] = b;
86
+ $[0] = props;
87
+ $[1] = a;
88
+ $[2] = b;
89
} else {
98
- a = $[3];
99
- b = $[4];
90
+ a = $[1];
91
+ b = $[2];
92
}
101
- const c_5 = $[5] !== a;
102
- const c_6 = $[6] !== b;
93
+ const c_3 = $[3] !== a;
94
+ const c_4 = $[4] !== b;
95
let t0;
104
- if (c_5 || c_6) {
96
+ if (c_3 || c_4) {
97
t0 = <Foo a={a} b={b}></Foo>;
106
- $[5] = a;
107
- $[6] = b;
108
- $[7] = t0;
98
+ $[3] = a;
99
+ $[4] = b;
100
+ $[5] = t0;
101
} else {
110
- t0 = $[7];
102
+ t0 = $[5];
103
}
104
return t0;
105
}
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-no-uncond.expect.md
new
+53
@@ -0,0 +1,53 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// When an object's properties are only read conditionally, we should
6
+// track the base object as a dependency.
7
+function TestOnlyConditionalDependencies(props, other) {
8
+ const x = {};
9
+ if (foo(other)) {
10
+ x.b = props.a.b;
11
+ x.c = props.a.b.c;
12
+ }
13
+ return x;
14
+}
15
+
16
+```
17
+
18
+## Code
19
+
20
+```javascript
21
+// When an object's properties are only read conditionally, we should
22
+// track the base object as a dependency.
23
+function TestOnlyConditionalDependencies(props, other) {
24
+ const $ = React.unstable_useMemoCache(5);
25
+ const c_0 = $[0] !== other;
26
+ const c_1 = $[1] !== props;
27
+ let x;
28
+ if (c_0 || c_1) {
29
+ x = {};
30
+ const c_3 = $[3] !== other;
31
+ let t0;
32
+ if (c_3) {
33
+ t0 = foo(other);
34
+ $[3] = other;
35
+ $[4] = t0;
36
+ } else {
37
+ t0 = $[4];
38
+ }
39
+ if (t0) {
40
+ x.b = props.a.b;
41
+ x.c = props.a.b.c;
42
+ }
43
+ $[0] = other;
44
+ $[1] = props;
45
+ $[2] = x;
46
+ } else {
47
+ x = $[2];
48
+ }
49
+ return x;
50
+}
51
+
52
+```
53
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-no-uncond.js
new
+10
@@ -0,0 +1,10 @@
1
+// When an object's properties are only read conditionally, we should
2
+// track the base object as a dependency.
3
+function TestOnlyConditionalDependencies(props, other) {
4
+ const x = {};
5
+ if (foo(other)) {
6
+ x.b = props.a.b;
7
+ x.c = props.a.b.c;
8
+ }
9
+ return x;
10
+}
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-promote-uncond.expect.md
new
+55
@@ -0,0 +1,55 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// When a conditional dependency `props.a.b.c` has no unconditional dependency
6
+// in its subpath or superpath, we should find the nearest unconditional access
7
+// and promote it to an unconditional dependency.
8
+function TestPromoteUnconditionalAccessToDependency(props, other) {
9
+ const x = {};
10
+ x.a = props.a.a.a;
11
+ if (foo(other)) {
12
+ x.c = props.a.b.c;
13
+ }
14
+ return x;
15
+}
16
+
17
+```
18
+
19
+## Code
20
+
21
+```javascript
22
+// When a conditional dependency `props.a.b.c` has no unconditional dependency
23
+// in its subpath or superpath, we should find the nearest unconditional access
24
+// and promote it to an unconditional dependency.
25
+function TestPromoteUnconditionalAccessToDependency(props, other) {
26
+ const $ = React.unstable_useMemoCache(5);
27
+ const c_0 = $[0] !== props.a;
28
+ const c_1 = $[1] !== other;
29
+ let x;
30
+ if (c_0 || c_1) {
31
+ x = {};
32
+ x.a = props.a.a.a;
33
+ const c_3 = $[3] !== other;
34
+ let t0;
35
+ if (c_3) {
36
+ t0 = foo(other);
37
+ $[3] = other;
38
+ $[4] = t0;
39
+ } else {
40
+ t0 = $[4];
41
+ }
42
+ if (t0) {
43
+ x.c = props.a.b.c;
44
+ }
45
+ $[0] = props.a;
46
+ $[1] = other;
47
+ $[2] = x;
48
+ } else {
49
+ x = $[2];
50
+ }
51
+ return x;
52
+}
53
+
54
+```
55
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-promote-uncond.js
new
+11
@@ -0,0 +1,11 @@
1
+// When a conditional dependency `props.a.b.c` has no unconditional dependency
2
+// in its subpath or superpath, we should find the nearest unconditional access
3
+// and promote it to an unconditional dependency.
4
+function TestPromoteUnconditionalAccessToDependency(props, other) {
5
+ const x = {};
6
+ x.a = props.a.a.a;
7
+ if (foo(other)) {
8
+ x.c = props.a.b.c;
9
+ }
10
+ return x;
11
+}
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order1.expect.md
new
+59
@@ -0,0 +1,59 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// When a conditional dependency `props.a` is a subpath of an unconditional
6
+// dependency `props.a.b`, we can access `props.a` while preserving program
7
+// semantics (with respect to nullthrows).
8
+// deps: {`props.a`, `props.a.b`} can further reduce to just `props.a`
9
+// ordering of accesses should not matter
10
+function TestConditionalSubpath1(props, other) {
11
+ const x = {};
12
+ x.b = props.a.b;
13
+ if (foo(other)) {
14
+ x.a = props.a;
15
+ }
16
+ return x;
17
+}
18
+
19
+```
20
+
21
+## Code
22
+
23
+```javascript
24
+// When a conditional dependency `props.a` is a subpath of an unconditional
25
+// dependency `props.a.b`, we can access `props.a` while preserving program
26
+// semantics (with respect to nullthrows).
27
+// deps: {`props.a`, `props.a.b`} can further reduce to just `props.a`
28
+// ordering of accesses should not matter
29
+function TestConditionalSubpath1(props, other) {
30
+ const $ = React.unstable_useMemoCache(5);
31
+ const c_0 = $[0] !== props.a;
32
+ const c_1 = $[1] !== other;
33
+ let x;
34
+ if (c_0 || c_1) {
35
+ x = {};
36
+ x.b = props.a.b;
37
+ const c_3 = $[3] !== other;
38
+ let t0;
39
+ if (c_3) {
40
+ t0 = foo(other);
41
+ $[3] = other;
42
+ $[4] = t0;
43
+ } else {
44
+ t0 = $[4];
45
+ }
46
+ if (t0) {
47
+ x.a = props.a;
48
+ }
49
+ $[0] = props.a;
50
+ $[1] = other;
51
+ $[2] = x;
52
+ } else {
53
+ x = $[2];
54
+ }
55
+ return x;
56
+}
57
+
58
+```
59
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order1.js
new
+13
@@ -0,0 +1,13 @@
1
+// When a conditional dependency `props.a` is a subpath of an unconditional
2
+// dependency `props.a.b`, we can access `props.a` while preserving program
3
+// semantics (with respect to nullthrows).
4
+// deps: {`props.a`, `props.a.b`} can further reduce to just `props.a`
5
+// ordering of accesses should not matter
6
+function TestConditionalSubpath1(props, other) {
7
+ const x = {};
8
+ x.b = props.a.b;
9
+ if (foo(other)) {
10
+ x.a = props.a;
11
+ }
12
+ return x;
13
+}
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order2.expect.md
new
+59
@@ -0,0 +1,59 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// When a conditional dependency `props.a` is a subpath of an unconditional
6
+// dependency `props.a.b`, we can access `props.a` while preserving program
7
+// semantics (with respect to nullthrows).
8
+// deps: {`props.a`, `props.a.b`} can further reduce to just `props.a`
9
+// ordering of accesses should not matter
10
+function TestConditionalSubpath2(props, other) {
11
+ const x = {};
12
+ if (foo(other)) {
13
+ x.a = props.a;
14
+ }
15
+ x.b = props.a.b;
16
+ return x;
17
+}
18
+
19
+```
20
+
21
+## Code
22
+
23
+```javascript
24
+// When a conditional dependency `props.a` is a subpath of an unconditional
25
+// dependency `props.a.b`, we can access `props.a` while preserving program
26
+// semantics (with respect to nullthrows).
27
+// deps: {`props.a`, `props.a.b`} can further reduce to just `props.a`
28
+// ordering of accesses should not matter
29
+function TestConditionalSubpath2(props, other) {
30
+ const $ = React.unstable_useMemoCache(5);
31
+ const c_0 = $[0] !== other;
32
+ const c_1 = $[1] !== props.a;
33
+ let x;
34
+ if (c_0 || c_1) {
35
+ x = {};
36
+ const c_3 = $[3] !== other;
37
+ let t0;
38
+ if (c_3) {
39
+ t0 = foo(other);
40
+ $[3] = other;
41
+ $[4] = t0;
42
+ } else {
43
+ t0 = $[4];
44
+ }
45
+ if (t0) {
46
+ x.a = props.a;
47
+ }
48
+ x.b = props.a.b;
49
+ $[0] = other;
50
+ $[1] = props.a;
51
+ $[2] = x;
52
+ } else {
53
+ x = $[2];
54
+ }
55
+ return x;
56
+}
57
+
58
+```
59
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order2.js
new
+13
@@ -0,0 +1,13 @@
1
+// When a conditional dependency `props.a` is a subpath of an unconditional
2
+// dependency `props.a.b`, we can access `props.a` while preserving program
3
+// semantics (with respect to nullthrows).
4
+// deps: {`props.a`, `props.a.b`} can further reduce to just `props.a`
5
+// ordering of accesses should not matter
6
+function TestConditionalSubpath2(props, other) {
7
+ const x = {};
8
+ if (foo(other)) {
9
+ x.a = props.a;
10
+ }
11
+ x.b = props.a.b;
12
+ return x;
13
+}
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order1.expect.md
new
+57
@@ -0,0 +1,57 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// When an unconditional dependency `props.a` is the subpath of a conditional
6
+// dependency `props.a.b`, we can safely overestimate and only track `props.a`
7
+// as a dependency
8
+// ordering of accesses should not matter
9
+function TestConditionalSuperpath1(props, other) {
10
+ const x = {};
11
+ x.a = props.a;
12
+ if (foo(other)) {
13
+ x.b = props.a.b;
14
+ }
15
+ return x;
16
+}
17
+
18
+```
19
+
20
+## Code
21
+
22
+```javascript
23
+// When an unconditional dependency `props.a` is the subpath of a conditional
24
+// dependency `props.a.b`, we can safely overestimate and only track `props.a`
25
+// as a dependency
26
+// ordering of accesses should not matter
27
+function TestConditionalSuperpath1(props, other) {
28
+ const $ = React.unstable_useMemoCache(5);
29
+ const c_0 = $[0] !== props.a;
30
+ const c_1 = $[1] !== other;
31
+ let x;
32
+ if (c_0 || c_1) {
33
+ x = {};
34
+ x.a = props.a;
35
+ const c_3 = $[3] !== other;
36
+ let t0;
37
+ if (c_3) {
38
+ t0 = foo(other);
39
+ $[3] = other;
40
+ $[4] = t0;
41
+ } else {
42
+ t0 = $[4];
43
+ }
44
+ if (t0) {
45
+ x.b = props.a.b;
46
+ }
47
+ $[0] = props.a;
48
+ $[1] = other;
49
+ $[2] = x;
50
+ } else {
51
+ x = $[2];
52
+ }
53
+ return x;
54
+}
55
+
56
+```
57
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order1.js
new
+12
@@ -0,0 +1,12 @@
1
+// When an unconditional dependency `props.a` is the subpath of a conditional
2
+// dependency `props.a.b`, we can safely overestimate and only track `props.a`
3
+// as a dependency
4
+// ordering of accesses should not matter
5
+function TestConditionalSuperpath1(props, other) {
6
+ const x = {};
7
+ x.a = props.a;
8
+ if (foo(other)) {
9
+ x.b = props.a.b;
10
+ }
11
+ return x;
12
+}
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order2.expect.md
new
+57
@@ -0,0 +1,57 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// When an unconditional dependency `props.a` is the subpath of a conditional
6
+// dependency `props.a.b`, we can safely overestimate and only track `props.a`
7
+// as a dependency
8
+// ordering of accesses should not matter
9
+function TestConditionalSuperpath2(props, other) {
10
+ const x = {};
11
+ if (foo(other)) {
12
+ x.b = props.a.b;
13
+ }
14
+ x.a = props.a;
15
+ return x;
16
+}
17
+
18
+```
19
+
20
+## Code
21
+
22
+```javascript
23
+// When an unconditional dependency `props.a` is the subpath of a conditional
24
+// dependency `props.a.b`, we can safely overestimate and only track `props.a`
25
+// as a dependency
26
+// ordering of accesses should not matter
27
+function TestConditionalSuperpath2(props, other) {
28
+ const $ = React.unstable_useMemoCache(5);
29
+ const c_0 = $[0] !== other;
30
+ const c_1 = $[1] !== props.a;
31
+ let x;
32
+ if (c_0 || c_1) {
33
+ x = {};
34
+ const c_3 = $[3] !== other;
35
+ let t0;
36
+ if (c_3) {
37
+ t0 = foo(other);
38
+ $[3] = other;
39
+ $[4] = t0;
40
+ } else {
41
+ t0 = $[4];
42
+ }
43
+ if (t0) {
44
+ x.b = props.a.b;
45
+ }
46
+ x.a = props.a;
47
+ $[0] = other;
48
+ $[1] = props.a;
49
+ $[2] = x;
50
+ } else {
51
+ x = $[2];
52
+ }
53
+ return x;
54
+}
55
+
56
+```
57
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order2.js
new
+12
@@ -0,0 +1,12 @@
1
+// When an unconditional dependency `props.a` is the subpath of a conditional
2
+// dependency `props.a.b`, we can safely overestimate and only track `props.a`
3
+// as a dependency
4
+// ordering of accesses should not matter
5
+function TestConditionalSuperpath2(props, other) {
6
+ const x = {};
7
+ if (foo(other)) {
8
+ x.b = props.a.b;
9
+ }
10
+ x.a = props.a;
11
+ return x;
12
+}
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-deps-cond-scope.expect.md
new
+82
@@ -0,0 +1,82 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// Some reactive scopes are created within a conditional. If a child scope
6
+// is within a conditional, its reactive dependencies should be propagated
7
+// as conditionals
8
+//
9
+// In this test:
10
+// ```javascript
11
+// scope @0 (deps=[???] decls=[x]) {
12
+// const x = {};
13
+// if (foo) {
14
+// scope @1 (deps=[props.a.b] decls=[tmp]) {
15
+// const tmp = bar(props.a.b);
16
+// }
17
+// x.a = tmp;
18
+// }
19
+// }
20
+// return x;
21
+// ```
22
+
23
+function TestReactiveDepsInCondScope(props) {
24
+ let x = {};
25
+ if (foo) {
26
+ let tmp = bar(props.a.b);
27
+ x.a = tmp;
28
+ }
29
+ return x;
30
+}
31
+
32
+```
33
+
34
+## Code
35
+
36
+```javascript
37
+// Some reactive scopes are created within a conditional. If a child scope
38
+// is within a conditional, its reactive dependencies should be propagated
39
+// as conditionals
40
+//
41
+// In this test:
42
+// ```javascript
43
+// scope @0 (deps=[???] decls=[x]) {
44
+// const x = {};
45
+// if (foo) {
46
+// scope @1 (deps=[props.a.b] decls=[tmp]) {
47
+// const tmp = bar(props.a.b);
48
+// }
49
+// x.a = tmp;
50
+// }
51
+// }
52
+// return x;
53
+// ```
54
+
55
+function TestReactiveDepsInCondScope(props) {
56
+ const $ = React.unstable_useMemoCache(4);
57
+ const c_0 = $[0] !== props;
58
+ let x;
59
+ if (c_0) {
60
+ x = {};
61
+ if (foo) {
62
+ const c_2 = $[2] !== props.a.b;
63
+ let tmp;
64
+ if (c_2) {
65
+ tmp = bar(props.a.b);
66
+ $[2] = props.a.b;
67
+ $[3] = tmp;
68
+ } else {
69
+ tmp = $[3];
70
+ }
71
+ x.a = tmp;
72
+ }
73
+ $[0] = props;
74
+ $[1] = x;
75
+ } else {
76
+ x = $[1];
77
+ }
78
+ return x;
79
+}
80
+
81
+```
82
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-deps-cond-scope.js
new
+26
@@ -0,0 +1,26 @@
1
+// Some reactive scopes are created within a conditional. If a child scope
2
+// is within a conditional, its reactive dependencies should be propagated
3
+// as conditionals
4
+//
5
+// In this test:
6
+// ```javascript
7
+// scope @0 (deps=[???] decls=[x]) {
8
+// const x = {};
9
+// if (foo) {
10
+// scope @1 (deps=[props.a.b] decls=[tmp]) {
11
+// const tmp = bar(props.a.b);
12
+// }
13
+// x.a = tmp;
14
+// }
15
+// }
16
+// return x;
17
+// ```
18
+
19
+function TestReactiveDepsInCondScope(props) {
20
+ let x = {};
21
+ if (foo) {
22
+ let tmp = bar(props.a.b);
23
+ x.a = tmp;
24
+ }
25
+ return x;
26
+}
compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.expect.md
+2
-2
@@ -57,12 +57,12 @@ function foo(props) {
57
} else {
58
x = $[1];
59
}
60
- const c_2 = $[2] !== props;
60
+ const c_2 = $[2] !== props.showHeader;
61
const c_3 = $[3] !== x;
62
let t0;
63
if (c_2 || c_3) {
64
t0 = props.showHeader ? <div>{x}</div> : null;
65
- $[2] = props;
65
+ $[2] = props.showHeader;
66
$[3] = x;
67
$[4] = t0;
68
} else {
compiler/forget/src/__tests__/fixtures/hir/ssa-leave-case.expect.md
+15
-17
@@ -23,41 +23,39 @@ function Component(props) {
23
24
```javascript
25
function Component(props) {
26
- const $ = React.unstable_useMemoCache(7);
27
- const c_0 = $[0] !== props.p0;
28
- const c_1 = $[1] !== props.p1;
26
+ const $ = React.unstable_useMemoCache(6);
27
+ const c_0 = $[0] !== props;
28
let x;
29
let y;
31
- if (c_0 || c_1) {
30
+ if (c_0) {
31
x = [];
32
y = undefined;
33
if (props.p0) {
34
x.push(props.p1);
35
y = x;
36
}
38
- $[0] = props.p0;
39
- $[1] = props.p1;
40
- $[2] = x;
41
- $[3] = y;
37
+ $[0] = props;
38
+ $[1] = x;
39
+ $[2] = y;
40
} else {
43
- x = $[2];
44
- y = $[3];
41
+ x = $[1];
42
+ y = $[2];
43
}
46
- const c_4 = $[4] !== x;
47
- const c_5 = $[5] !== y;
44
+ const c_3 = $[3] !== x;
45
+ const c_4 = $[4] !== y;
46
let t0;
49
- if (c_4 || c_5) {
47
+ if (c_3 || c_4) {
48
t0 = (
49
<Component>
50
{x}
51
{y}
52
</Component>
53
);
56
- $[4] = x;
57
- $[5] = y;
58
- $[6] = t0;
54
+ $[3] = x;
55
+ $[4] = y;
56
+ $[5] = t0;
57
} else {
60
- t0 = $[6];
58
+ t0 = $[5];
59
}
60
return t0;
61
}
compiler/forget/src/__tests__/fixtures/hir/ssa-renaming-with-mutation.expect.md
+6
-10
@@ -20,12 +20,10 @@ function foo(props) {
20
21
```javascript
22
function foo(props) {
23
- const $ = React.unstable_useMemoCache(4);
24
- const c_0 = $[0] !== props.bar;
25
- const c_1 = $[1] !== props.cond;
26
- const c_2 = $[2] !== props.foo;
23
+ const $ = React.unstable_useMemoCache(2);
24
+ const c_0 = $[0] !== props;
25
let x;
28
- if (c_0 || c_1 || c_2) {
26
+ if (c_0) {
27
x = [];
28
x.push(props.bar);
29
if (props.cond) {
@@ -34,12 +32,10 @@ function foo(props) {
32
}
33
34
mut(x);
37
- $[0] = props.bar;
38
- $[1] = props.cond;
39
- $[2] = props.foo;
40
- $[3] = x;
35
+ $[0] = props;
36
+ $[1] = x;
37
} else {
42
- x = $[3];
38
+ x = $[1];
39
}
40
return x;
41
}
compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md
+23
-25
@@ -32,12 +32,11 @@ function Component(props) {
32
33
```javascript
34
function Component(props) {
35
- const $ = React.unstable_useMemoCache(10);
36
- const c_0 = $[0] !== props.p0;
37
- const c_1 = $[1] !== props.p2;
35
+ const $ = React.unstable_useMemoCache(9);
36
+ const c_0 = $[0] !== props;
37
let x;
38
let y;
40
- if (c_0 || c_1) {
39
+ if (c_0) {
40
x = [];
41
y = undefined;
42
bb1: switch (props.p0) {
@@ -47,11 +46,11 @@ function Component(props) {
46
case true: {
47
x.push(props.p2);
48
let t0;
50
- if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
49
+ if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
50
t0 = [];
52
- $[4] = t0;
51
+ $[3] = t0;
52
} else {
54
- t0 = $[4];
53
+ t0 = $[3];
54
}
55
y = t0;
56
break bb1;
@@ -63,34 +62,33 @@ function Component(props) {
62
y = x;
63
}
64
}
66
- $[0] = props.p0;
67
- $[1] = props.p2;
68
- $[2] = x;
69
- $[3] = y;
65
+ $[0] = props;
66
+ $[1] = x;
67
+ $[2] = y;
68
} else {
71
- x = $[2];
72
- y = $[3];
69
+ x = $[1];
70
+ y = $[2];
71
}
74
- const c_5 = $[5] !== x;
72
+ const c_4 = $[4] !== x;
73
let child;
76
- if (c_5) {
74
+ if (c_4) {
75
child = <Component data={x}></Component>;
78
- $[5] = x;
79
- $[6] = child;
76
+ $[4] = x;
77
+ $[5] = child;
78
} else {
81
- child = $[6];
79
+ child = $[5];
80
}
81
y.push(props.p4);
84
- const c_7 = $[7] !== y;
85
- const c_8 = $[8] !== child;
82
+ const c_6 = $[6] !== y;
83
+ const c_7 = $[7] !== child;
84
let t1;
87
- if (c_7 || c_8) {
85
+ if (c_6 || c_7) {
86
t1 = <Component data={y}>{child}</Component>;
89
- $[7] = y;
90
- $[8] = child;
91
- $[9] = t1;
87
+ $[6] = y;
88
+ $[7] = child;
89
+ $[8] = t1;
90
} else {
93
- t1 = $[9];
91
+ t1 = $[8];
92
}
93
return t1;
94
}
compiler/forget/src/__tests__/fixtures/hir/switch.expect.md
+20
-24
@@ -27,13 +27,11 @@ function Component(props) {
27
28
```javascript
29
function Component(props) {
30
- const $ = React.unstable_useMemoCache(10);
31
- const c_0 = $[0] !== props.p0;
32
- const c_1 = $[1] !== props.p2;
33
- const c_2 = $[2] !== props.p3;
30
+ const $ = React.unstable_useMemoCache(8);
31
+ const c_0 = $[0] !== props;
32
let x;
33
let y;
36
- if (c_0 || c_1 || c_2) {
34
+ if (c_0) {
35
x = [];
36
y = undefined;
37
switch (props.p0) {
@@ -45,35 +43,33 @@ function Component(props) {
43
y = x;
44
}
45
}
48
- $[0] = props.p0;
49
- $[1] = props.p2;
50
- $[2] = props.p3;
51
- $[3] = x;
52
- $[4] = y;
46
+ $[0] = props;
47
+ $[1] = x;
48
+ $[2] = y;
49
} else {
54
- x = $[3];
55
- y = $[4];
50
+ x = $[1];
51
+ y = $[2];
52
}
57
- const c_5 = $[5] !== x;
53
+ const c_3 = $[3] !== x;
54
let child;
59
- if (c_5) {
55
+ if (c_3) {
56
child = <Component data={x}></Component>;
61
- $[5] = x;
62
- $[6] = child;
57
+ $[3] = x;
58
+ $[4] = child;
59
} else {
64
- child = $[6];
60
+ child = $[4];
61
}
62
y.push(props.p4);
67
- const c_7 = $[7] !== y;
68
- const c_8 = $[8] !== child;
63
+ const c_5 = $[5] !== y;
64
+ const c_6 = $[6] !== child;
65
let t0;
70
- if (c_7 || c_8) {
66
+ if (c_5 || c_6) {
67
t0 = <Component data={y}>{child}</Component>;
72
- $[7] = y;
73
- $[8] = child;
74
- $[9] = t0;
68
+ $[5] = y;
69
+ $[6] = child;
70
+ $[7] = t0;
71
} else {
76
- t0 = $[9];
72
+ t0 = $[7];
73
}
74
return t0;
75
}