@samitouri / QOS-React-2 / commits / 9048f59237

Prune scopes whose values dont escape

Adds a new pass that uses escape analysis and React-specific heuristics to tune the amount of memoization applied. Specifically, the pass ensures that we only memoize: * Values which escape (are directly returned or transitively aliased by a returned value) * ...and that are not JSX elements * OR values which are _dependencies_ of scopes that produce an escaping value. The latter case is necessary to avoid breaking memoization of an escaping value bc a scope happened to have a non-escaping dependency. ## Algorithm 1. First we build up a graph, a mapping of IdentifierId to a node describing all the scopes and inputs involved in creating that identifier. Individual nodes are marked as definitely aliased, conditionally aliased, or unaliased: a. Arrays, objects, function calls all produce a new value and are always marked as aliased b. Conditional and logical expressions (and a few others) are conditinally aliased, depending on whether their result value is aliased. c. JSX is always unaliased (though its props children may be) 2. The same pass which builds the graph also stores the set of returned identifiers 3. We traverse the graph starting from the returned identifiers and mark reachable dependencies as escaping, based on the combination of the parent node's type and its children (eg a conditional node with an aliased dep promotes to aliased). 4. Finally we prune scopes whose outputs weren't marked.

Joe Savona committed Mar 9, 2023 at 09:51 UTC 9048f5923745ace73cc9210d4a4bb0bfdedb0d8d
89 files changed +1361 -1248
compiler/forget/src/CompilerPipeline.ts
+8
@@ -32,6 +32,7 @@ import {
32 mergeOverlappingReactiveScopes,
33 promoteUsedTemporaries,
34 propagateScopeDependencies,
35 + pruneNonEscapingScopes,
36 pruneNonReactiveDependencies,
37 pruneUnusedLabels,
38 pruneUnusedLValues,
@@ -145,6 +146,13 @@ export function* run(
146 value: reactiveFunction,
147 });
148
149 + pruneNonEscapingScopes(reactiveFunction);
150 + yield log({
151 + kind: "reactive",
152 + name: "PruneNonEscapingDependencies",
153 + value: reactiveFunction,
154 + });
155 +
156 pruneNonReactiveDependencies(reactiveFunction);
157 yield log({
158 kind: "reactive",
compiler/forget/src/ReactiveScopes/PrintReactiveFunction.ts
+1 -3
@@ -84,9 +84,7 @@ function printReactiveInstruction(
84 const id = `[${instruction.id}]`;
85
86 if (instruction.lvalue !== null) {
87 - writer.write(
88 - `${id} ${printIdentifier(instruction.lvalue.identifier)} = `
89 - );
87 + writer.write(`${id} ${printPlace(instruction.lvalue)} = `);
88 printReactiveValue(writer, instruction.value);
89 writer.newline();
90 } else {
compiler/forget/src/ReactiveScopes/PruneNonEscapingScopes.ts new
+581
@@ -0,0 +1,581 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import invariant from "invariant";
9 +import prettyFormat from "pretty-format";
10 +import { CompilerError } from "../CompilerError";
11 +import {
12 + Effect,
13 + IdentifierId,
14 + InstructionId,
15 + Place,
16 + ReactiveFunction,
17 + ReactiveInstruction,
18 + ReactiveScopeBlock,
19 + ReactiveStatement,
20 + ReactiveTerminal,
21 + ReactiveTerminalStatement,
22 + ReactiveValue,
23 + ScopeId,
24 +} from "../HIR";
25 +import { eachInstructionLValue } from "../HIR/visitors";
26 +import { log } from "../Utils/logger";
27 +import { assertExhaustive } from "../Utils/utils";
28 +import { getPlaceScope } from "./BuildReactiveBlocks";
29 +import { printReactiveFunction } from "./PrintReactiveFunction";
30 +import {
31 + eachReactiveValueOperand,
32 + ReactiveFunctionTransform,
33 + ReactiveFunctionVisitor,
34 + Transformed,
35 + visitReactiveFunction,
36 +} from "./visitors";
37 +
38 +/**
39 + * This pass prunes reactive scopes that are not necessary to bound downstream computation.
40 + * Specifically, the pass identifies the set of identifiers which are directly returned by
41 + * the function and/or transitively aliased by a return value - ie, values that "escape".
42 + *
43 + * Example to build intuition:
44 + *
45 + * ```javascript
46 + * function Component(props) {
47 + * const a = {}; // not aliased or returned: *not* memoized
48 + * const b = {}; // aliased by c, which is returned: memoized
49 + * const c = [b]; // directly returned: memoized
50 + * return c;
51 + * }
52 + * ```
53 + *
54 + * However, this logic alone is insufficient for two reasons:
55 + * - Statically memoizing JSX elements *may* be inefficient compared to using dynamic
56 + * memoization with `React.memo()`. Static memoization may be JIT'd and can look at
57 + * the precise props w/o dynamic iteration, but incurs potentially large code-size
58 + * overhead. Dynamic memoization with `React.memo()` incurs potentially increased
59 + * runtime overhead for smaller code size. We plan to experiment with both variants
60 + * for JSX.
61 + * - Because we merge values whose mutations _interleave_ into a single scope, there
62 + * can be cases where a non-escaping value needs to be memoized anyway to avoid breaking
63 + * a memoization input. As a rule, for any scope that has a memoized output, all of that
64 + * scope's transitive dependencies must also be memoized _even if they don't escape_.
65 + * Failing to memoize them would cause the scope to invalidate more often than necessary
66 + * and break downstream memoization.
67 + *
68 + * Example of this second case:
69 + *
70 + * ```javascript
71 + * function Component(props) {
72 + * // a can be independently memoized but it doesn't escape, so naively we may think its
73 + * // safe to not memoize. but not memoizing would break caching of b, which does
74 + * // escape.
75 + * const a = [props.a];
76 + *
77 + * // b and c are interleaved and grouped into a single scope,
78 + * // but they are independent values. c does not escape, but
79 + * // we need to ensure that a is memoized or else b will invalidate
80 + * // on every render since a is a dependency.
81 + * const b = [];
82 + * const c = {};
83 + * c.a = a;
84 + * b.push(props.b);
85 + *
86 + * return b;
87 + * }
88 + * ```
89 + *
90 + * ## Algorithm
91 + *
92 + * 1. First we build up a graph, a mapping of IdentifierId to a node describing all the
93 + * scopes and inputs involved in creating that identifier. Individual nodes are marked
94 + * as definitely aliased, conditionally aliased, or unaliased:
95 + * a. Arrays, objects, function calls all produce a new value and are always marked as aliased
96 + * b. Conditional and logical expressions (and a few others) are conditinally aliased,
97 + * depending on whether their result value is aliased.
98 + * c. JSX is always unaliased (though its props children may be)
99 + * 2. The same pass which builds the graph also stores the set of returned identifiers.
100 + * 3. We traverse the graph starting from the returned identifiers and mark reachable dependencies
101 + * as escaping, based on the combination of the parent node's type and its children (eg a
102 + * conditional node with an aliased dep promotes to aliased).
103 + * 4. Finally we prune scopes whose outputs weren't marked.
104 + */
105 +export function pruneNonEscapingScopes(fn: ReactiveFunction): void {
106 + // First build up a map of which instructions are involved in creating which values,
107 + // and which values are returned.
108 + const state = new State();
109 + if (fn.id !== null) {
110 + state.declare(fn.id.id);
111 + }
112 + for (const param of fn.params) {
113 + state.declare(param.identifier.id);
114 + }
115 + visitReactiveFunction(fn, new CollectDependenciesVisitor(), state);
116 +
117 + log(() => prettyFormat(state));
118 +
119 + // Then walk outward from the returned values and find all captured operands.
120 + // This forms the set of identifiers which should be memoized.
121 + const memoized = computeMemoizedIdentifiers(state);
122 +
123 + log(() => prettyFormat(memoized));
124 +
125 + log(() => printReactiveFunction(fn));
126 +
127 + // Prune scopes that do not declare/reassign any escaping values
128 + visitReactiveFunction(fn, new PruneScopesTransform(), memoized);
129 +}
130 +
131 +// Describes how to determine whether a value should be memoized, relative to dependees and dependencies
132 +enum MemoizationLevel {
133 + // The value should be memoized if it escapes
134 + Memoized = "Memoized",
135 + // Values that are memoized if their dependencies are memoized (used for logical/ternary and
136 + // other expressions that propagate dependencies wo changing them)
137 + Conditional = "Conditional",
138 + // Values that cannot be compared with Object.is, but which by default don't need to be memoized
139 + // unless forced
140 + Unmemoized = "Unmemoized",
141 + // The value will never be memoized: used for values that can be cheaply compared w Object.is
142 + Never = "Never",
143 +}
144 +
145 +// Given an identifier that appears as an lvalue multiple times with different memoization levels,
146 +// determines the final memoization level.
147 +function joinAliases(
148 + kind1: MemoizationLevel,
149 + kind2: MemoizationLevel
150 +): MemoizationLevel {
151 + if (
152 + kind1 === MemoizationLevel.Memoized ||
153 + kind2 === MemoizationLevel.Memoized
154 + ) {
155 + return MemoizationLevel.Memoized;
156 + } else if (
157 + kind1 === MemoizationLevel.Conditional ||
158 + kind2 === MemoizationLevel.Conditional
159 + ) {
160 + return MemoizationLevel.Conditional;
161 + } else if (
162 + kind1 === MemoizationLevel.Unmemoized ||
163 + kind2 === MemoizationLevel.Unmemoized
164 + ) {
165 + return MemoizationLevel.Unmemoized;
166 + } else {
167 + return MemoizationLevel.Never;
168 + }
169 +}
170 +
171 +// A node in the graph describing the memoization level of a given identifier as well as its dependencies and scopes.
172 +type IdentifierNode = {
173 + level: MemoizationLevel;
174 + memoized: boolean;
175 + dependencies: Set<IdentifierId>;
176 + scopes: Set<ScopeId>;
177 + seen: boolean;
178 +};
179 +
180 +// A scope node describing its dependencies
181 +type ScopeNode = {
182 + dependencies: Array<IdentifierId>;
183 + seen: boolean;
184 +};
185 +
186 +// Stores the identifier and scope graphs, set of returned identifiers, etc
187 +class State {
188 + // Maps lvalues for LoadLocal to the identifier being loaded, to resolve indirections
189 + // in subsequent lvalues/rvalues
190 + definitions: Map<IdentifierId, IdentifierId> = new Map();
191 +
192 + identifiers: Map<IdentifierId, IdentifierNode> = new Map();
193 + scopes: Map<ScopeId, ScopeNode> = new Map();
194 + returned: Set<IdentifierId> = new Set();
195 +
196 + /**
197 + * Declare a new identifier, used for function id and params
198 + */
199 + declare(id: IdentifierId): void {
200 + this.identifiers.set(id, {
201 + level: MemoizationLevel.Never,
202 + memoized: false,
203 + dependencies: new Set(),
204 + scopes: new Set(),
205 + seen: false,
206 + });
207 + }
208 +
209 + /**
210 + * Associates the identifier with its scope, if there is one and it is active for the given instruction id:
211 + * - Records the scope and its dependencies
212 + * - Associates the identifier with this scope
213 + */
214 + visitOperand(
215 + id: InstructionId,
216 + place: Place,
217 + identifier: IdentifierId
218 + ): void {
219 + const scope = getPlaceScope(id, place);
220 + if (scope !== null) {
221 + let node = this.scopes.get(scope.id);
222 + if (node === undefined) {
223 + node = {
224 + dependencies: [...scope.dependencies].map((dep) => dep.identifier.id),
225 + seen: false,
226 + };
227 + this.scopes.set(scope.id, node);
228 + }
229 + const identifierNode = this.identifiers.get(identifier);
230 + invariant(
231 + identifierNode !== undefined,
232 + "Expected identifier to be initialized"
233 + );
234 + identifierNode.scopes.add(scope.id);
235 + }
236 + }
237 +}
238 +
239 +/**
240 + * Given a state derived from visiting the function, walks the graph from the returned nodes
241 + * to determine which other values should be memoized. Returns a set of all identifiers
242 + * that should be memoized.
243 + */
244 +function computeMemoizedIdentifiers(state: State): Set<IdentifierId> {
245 + const memoized = new Set<IdentifierId>();
246 +
247 + // Visit an identifier, optionally forcing it to be memoized
248 + function visit(id: IdentifierId, forceMemoize: boolean = false): boolean {
249 + const node = state.identifiers.get(id);
250 + invariant(node !== undefined, "Expected a node for all identifiers");
251 + if (node.seen) {
252 + return node.memoized;
253 + }
254 + node.seen = true;
255 +
256 + // Note: in case of cycles we temporarily mark the identifier as non-memoized,
257 + // this is reset later after processing dependencies
258 + node.memoized = false;
259 +
260 + // Visit dependencies, determine if any of them are memoized
261 + let hasMemoizedDependency = false;
262 + for (const dep of node.dependencies) {
263 + const isDepMemoized = visit(dep);
264 + hasMemoizedDependency ||= isDepMemoized;
265 + }
266 +
267 + if (
268 + node.level === MemoizationLevel.Memoized ||
269 + (node.level === MemoizationLevel.Conditional &&
270 + (hasMemoizedDependency || forceMemoize)) ||
271 + (node.level === MemoizationLevel.Unmemoized && forceMemoize)
272 + ) {
273 + node.memoized = true;
274 + memoized.add(id);
275 + for (const scope of node.scopes) {
276 + forceMemoizeScopeDependencies(scope);
277 + }
278 + }
279 + return node.memoized;
280 + }
281 +
282 + // Force all the scope's optionally-memoizeable dependencies (not "Never") to be memoized
283 + function forceMemoizeScopeDependencies(id: ScopeId): void {
284 + const node = state.scopes.get(id);
285 + invariant(node !== undefined, "Expected a node for all scopes");
286 + if (node.seen) {
287 + return;
288 + }
289 + node.seen = true;
290 +
291 + for (const dep of node.dependencies) {
292 + visit(dep, true);
293 + }
294 + return;
295 + }
296 +
297 + // Walk from the "roots" aka returned identifiers.
298 + for (const returned of state.returned) {
299 + visit(returned);
300 + }
301 +
302 + return memoized;
303 +}
304 +
305 +/**
306 + * Given a value, returns a description of how it should be memoized:
307 + * - lvalues: optional extra places that are lvalue-like in the sense of
308 + * aliasing the rvalues
309 + * - rvalues: places that are aliased by the instruction's lvalues.
310 + * - level: the level of memoization to apply to this value
311 + */
312 +function computeMemoizationInputs(value: ReactiveValue): {
313 + // can optionally return a custom set of lvalues per instruction
314 + lvalues: Array<Place> | null;
315 + rvalues: Array<Place>;
316 + level: MemoizationLevel;
317 +} {
318 + switch (value.kind) {
319 + case "ConditionalExpression": {
320 + return {
321 + lvalues: null,
322 + rvalues: [
323 + // Conditionals do not alias their test value.
324 + ...computeMemoizationInputs(value.consequent).rvalues,
325 + ...computeMemoizationInputs(value.alternate).rvalues,
326 + ],
327 + // Only need to memoize if the rvalues are memoized
328 + level: MemoizationLevel.Conditional,
329 + };
330 + }
331 + case "LogicalExpression": {
332 + return {
333 + lvalues: null,
334 + rvalues: [
335 + ...computeMemoizationInputs(value.left).rvalues,
336 + ...computeMemoizationInputs(value.right).rvalues,
337 + ],
338 + // Only need to memoize if the rvalues are memoized
339 + level: MemoizationLevel.Conditional,
340 + };
341 + }
342 + case "SequenceExpression": {
343 + return {
344 + lvalues: null,
345 + // Only the final value of the sequence is a true rvalue:
346 + // values from the sequence's instructions are evaluated
347 + // as separate nodes
348 + rvalues: computeMemoizationInputs(value.value).rvalues,
349 + // Only memoize if the final value was memoized
350 + level: MemoizationLevel.Conditional,
351 + };
352 + }
353 + case "JsxExpression": {
354 + const operands: Array<Place> = [];
355 + operands.push(value.tag);
356 + for (const prop of value.props) {
357 + if (prop.kind === "JsxAttribute") {
358 + operands.push(prop.place);
359 + } else {
360 + operands.push(prop.argument);
361 + }
362 + }
363 + if (value.children !== null) {
364 + for (const child of value.children) {
365 + operands.push(child);
366 + }
367 + }
368 + return {
369 + lvalues: null,
370 + rvalues: operands,
371 + // JSX elements themselves are not memoized unless forced to
372 + // avoid breaking downstream memoization
373 + level: MemoizationLevel.Unmemoized,
374 + };
375 + }
376 + case "JsxFragment": {
377 + return {
378 + lvalues: null,
379 + rvalues: value.children,
380 + // JSX elements themselves are not memoized unless forced to
381 + // avoid breaking downstream memoization
382 + level: MemoizationLevel.Unmemoized,
383 + };
384 + }
385 + case "ComputedDelete":
386 + case "PropertyDelete":
387 + case "LoadGlobal":
388 + case "TemplateLiteral":
389 + case "Primitive":
390 + case "JSXText":
391 + case "BinaryExpression":
392 + case "UnaryExpression": {
393 + return {
394 + lvalues: null,
395 + rvalues: [],
396 + // All of these instructions return a primitive value and never need to be memoized
397 + level: MemoizationLevel.Never,
398 + };
399 + }
400 + case "TypeCastExpression": {
401 + return {
402 + lvalues: null,
403 + // Indirection for the inner value, memoized if the value is
404 + rvalues: [value.value],
405 + level: MemoizationLevel.Conditional,
406 + };
407 + }
408 + case "LoadLocal": {
409 + return {
410 + lvalues: null,
411 + // Indirection for the inner value, memoized if the value is
412 + rvalues: [value.place],
413 + level: MemoizationLevel.Conditional,
414 + };
415 + }
416 + case "Destructure":
417 + case "StoreLocal": {
418 + return {
419 + lvalues: null,
420 + // Indirection for the inner value, memoized if the value is
421 + rvalues: [value.value],
422 + level: MemoizationLevel.Conditional,
423 + };
424 + }
425 + case "ComputedLoad":
426 + case "PropertyLoad": {
427 + return {
428 + lvalues: null,
429 + // Only the object is aliased to the result, and the result only needs to be
430 + // memoized if the object is
431 + rvalues: [value.object],
432 + level: MemoizationLevel.Conditional,
433 + };
434 + }
435 + case "ComputedStore": {
436 + // The object being stored to acts as an lvalue (it aliases the value), but
437 + // the computed key is not aliased
438 + return {
439 + lvalues: [value.object],
440 + rvalues: [value.value],
441 + level: MemoizationLevel.Conditional,
442 + };
443 + }
444 + case "FunctionExpression":
445 + case "TaggedTemplateExpression":
446 + case "CallExpression":
447 + case "ArrayExpression":
448 + case "NewExpression":
449 + case "ObjectExpression":
450 + case "ComputedCall":
451 + case "PropertyCall":
452 + case "PropertyStore": {
453 + // All of these instructions may produce new values which must be memoized if
454 + // reachable from a return value. Any mutable rvalue may alias any other rvalue
455 + const operands = [...eachReactiveValueOperand(value)];
456 + return {
457 + lvalues: operands.filter((operand) => isMutableEffect(operand.effect)),
458 + rvalues: operands,
459 + level: MemoizationLevel.Memoized,
460 + };
461 + }
462 + case "UnsupportedNode": {
463 + CompilerError.invariant(`Unexpected unsupported node`, value.loc);
464 + }
465 + default: {
466 + assertExhaustive(value, `Unexpected value kind '${(value as any).kind}'`);
467 + }
468 + }
469 +}
470 +
471 +/**
472 + * Populates the input state with the set of returned identifiers and information about each
473 + * identifier's and scope's dependencies.
474 + */
475 +class CollectDependenciesVisitor extends ReactiveFunctionVisitor<State> {
476 + override visitInstruction(
477 + instruction: ReactiveInstruction,
478 + state: State
479 + ): void {
480 + this.traverseInstruction(instruction, state);
481 +
482 + // Determe the level of memoization for this value and the lvalues/rvalues
483 + const aliasing = computeMemoizationInputs(instruction.value);
484 +
485 + // Associate all the rvalues with the instruction's scope if it has one
486 + for (const operand of aliasing.rvalues) {
487 + const operandId =
488 + state.definitions.get(operand.identifier.id) ?? operand.identifier.id;
489 + state.visitOperand(instruction.id, operand, operandId);
490 + }
491 +
492 + // Add the operands as dependencies of all lvalues.
493 + const lvalues =
494 + aliasing.lvalues !== null
495 + ? [...eachInstructionLValue(instruction), ...aliasing.lvalues]
496 + : [...eachInstructionLValue(instruction)];
497 + for (const lvalue of lvalues) {
498 + const lvalueId =
499 + state.definitions.get(lvalue.identifier.id) ?? lvalue.identifier.id;
500 + let node = state.identifiers.get(lvalueId);
501 + if (node === undefined) {
502 + node = {
503 + level: MemoizationLevel.Never,
504 + memoized: false,
505 + dependencies: new Set(),
506 + scopes: new Set(),
507 + seen: false,
508 + };
509 + state.identifiers.set(lvalueId, node);
510 + }
511 + node.level = joinAliases(node.level, aliasing.level);
512 + // This looks like NxM iterations but in practice all instructions with multiple
513 + // lvalues have only a single rvalue
514 + for (const operand of aliasing.rvalues) {
515 + const operandId =
516 + state.definitions.get(operand.identifier.id) ?? operand.identifier.id;
517 + if (operandId === lvalueId) {
518 + continue;
519 + }
520 + node.dependencies.add(operandId);
521 + }
522 +
523 + state.visitOperand(instruction.id, lvalue, lvalueId);
524 + }
525 +
526 + if (instruction.value.kind === "LoadLocal" && instruction.lvalue !== null) {
527 + state.definitions.set(
528 + instruction.lvalue.identifier.id,
529 + instruction.value.place.identifier.id
530 + );
531 + }
532 + }
533 +
534 + override visitTerminal(
535 + stmt: ReactiveTerminalStatement<ReactiveTerminal>,
536 + state: State
537 + ): void {
538 + this.traverseTerminal(stmt, state);
539 +
540 + if (stmt.terminal.kind === "return" && stmt.terminal.value !== null) {
541 + state.returned.add(stmt.terminal.value.identifier.id);
542 + }
543 + }
544 +}
545 +
546 +/**
547 + * Prune reactive scopes that do not have any memoized outputs
548 + */
549 +class PruneScopesTransform extends ReactiveFunctionTransform<
550 + Set<IdentifierId>
551 +> {
552 + override transformScope(
553 + scope: ReactiveScopeBlock,
554 + state: Set<IdentifierId>
555 + ): Transformed<ReactiveStatement> {
556 + this.visitScope(scope, state);
557 + const hasMemoizedOutput =
558 + Array.from(scope.scope.declarations.keys()).some((id) => state.has(id)) ||
559 + Array.from(scope.scope.reassignments).some((identifier) =>
560 + state.has(identifier.id)
561 + );
562 + if (hasMemoizedOutput) {
563 + return { kind: "keep" };
564 + } else {
565 + return { kind: "replace-many", value: scope.instructions };
566 + }
567 + }
568 +}
569 +
570 +function isMutableEffect(effect: Effect): boolean {
571 + switch (effect) {
572 + case Effect.Capture:
573 + case Effect.Mutate:
574 + case Effect.Store: {
575 + return true;
576 + }
577 + default: {
578 + return false;
579 + }
580 + }
581 +}
compiler/forget/src/ReactiveScopes/index.ts
+1
@@ -16,6 +16,7 @@ export { mergeOverlappingReactiveScopes } from "./MergeOverlappingReactiveScopes
16 export { printReactiveFunction } from "./PrintReactiveFunction";
17 export { promoteUsedTemporaries } from "./PromoteUsedTemporaries";
18 export { propagateScopeDependencies } from "./PropagateScopeDependencies";
19 +export { pruneNonEscapingScopes } from "./PruneNonEscapingScopes";
20 export { pruneNonReactiveDependencies } from "./PruneNonReactiveDependencies";
21 export { pruneTemporaryLValues as pruneUnusedLValues } from "./PruneTemporaryLValues";
22 export { pruneUnusedLabels } from "./PruneUnusedLabels";
compiler/forget/src/__tests__/fixtures/hir/_bug.capturing-function-renamed-ref.expect.md
+3 -11
@@ -19,7 +19,7 @@ function component(a, b) {
19
20 ```javascript
21 function component(a, b) {
22 - const $ = React.unstable_useMemoCache(4);
22 + const $ = React.unstable_useMemoCache(2);
23 const c_0 = $[0] !== a;
24 let t0;
25 if (c_0) {
@@ -30,16 +30,8 @@ function component(a, b) {
30 t0 = $[1];
31 }
32 const z = t0;
33 - const c_2 = $[2] !== b;
34 - let t1;
35 - if (c_2) {
36 - t1 = { b };
37 - $[2] = b;
38 - $[3] = t1;
39 - } else {
40 - t1 = $[3];
41 - }
42 - const z_0 = t1;
33 +
34 + const z_0 = { b };
35 (function () {
36 mutate(z);
37 })();
compiler/forget/src/__tests__/fixtures/hir/allocating-primitive-as-dep-nested-scope.expect.md
+19 -28
@@ -24,7 +24,7 @@ function AllocatingPrimitiveAsDepNested(props) {
24 // Correctness:
25 // - y depends on either bar(props.b) or bar(props.b) + 1
26 function AllocatingPrimitiveAsDepNested(props) {
27 - const $ = React.unstable_useMemoCache(11);
27 + const $ = React.unstable_useMemoCache(9);
28 const c_0 = $[0] !== props.b;
29 const c_1 = $[1] !== props.a;
30 let x;
@@ -32,26 +32,17 @@ function AllocatingPrimitiveAsDepNested(props) {
32 if (c_0 || c_1) {
33 x = {};
34 mutate(x);
35 - const c_4 = $[4] !== props.b;
36 - let t0;
35 + const t0 = bar(props.b) + 1;
36 + const c_4 = $[4] !== t0;
37 + let t1;
38 if (c_4) {
38 - t0 = bar(props.b);
39 - $[4] = props.b;
40 - $[5] = t0;
39 + t1 = foo(t0);
40 + $[4] = t0;
41 + $[5] = t1;
42 } else {
42 - t0 = $[5];
43 + t1 = $[5];
44 }
44 - const t1 = t0 + 1;
45 - const c_6 = $[6] !== t1;
46 - let t2;
47 - if (c_6) {
48 - t2 = foo(t1);
49 - $[6] = t1;
50 - $[7] = t2;
51 - } else {
52 - t2 = $[7];
53 - }
54 - y = t2;
45 + y = t1;
46 mutate(x, props.a);
47 $[0] = props.b;
48 $[1] = props.a;
@@ -61,18 +52,18 @@ function AllocatingPrimitiveAsDepNested(props) {
52 x = $[2];
53 y = $[3];
54 }
64 - const c_8 = $[8] !== x;
65 - const c_9 = $[9] !== y;
66 - let t3;
67 - if (c_8 || c_9) {
68 - t3 = [x, y];
69 - $[8] = x;
70 - $[9] = y;
71 - $[10] = t3;
55 + const c_6 = $[6] !== x;
56 + const c_7 = $[7] !== y;
57 + let t2;
58 + if (c_6 || c_7) {
59 + t2 = [x, y];
60 + $[6] = x;
61 + $[7] = y;
62 + $[8] = t2;
63 } else {
73 - t3 = $[10];
64 + t2 = $[8];
65 }
75 - return t3;
66 + return t2;
67 }
68
69 ```
compiler/forget/src/__tests__/fixtures/hir/allocating-primitive-as-dep.expect.md
+9 -18
@@ -21,27 +21,18 @@ function AllocatingPrimitiveAsDep(props) {
21 // Correctness:
22 // - y depends on either bar(props.b) or bar(props.b) + 1
23 function AllocatingPrimitiveAsDep(props) {
24 - const $ = React.unstable_useMemoCache(4);
25 - const c_0 = $[0] !== props;
26 - let t0;
24 + const $ = React.unstable_useMemoCache(2);
25 + const t0 = bar(props).b + 1;
26 + const c_0 = $[0] !== t0;
27 + let t1;
28 if (c_0) {
28 - t0 = bar(props);
29 - $[0] = props;
30 - $[1] = t0;
29 + t1 = foo(t0);
30 + $[0] = t0;
31 + $[1] = t1;
32 } else {
32 - t0 = $[1];
33 + t1 = $[1];
34 }
34 - const t1 = t0.b + 1;
35 - const c_2 = $[2] !== t1;
36 - let t2;
37 - if (c_2) {
38 - t2 = foo(t1);
39 - $[2] = t1;
40 - $[3] = t2;
41 - } else {
42 - t2 = $[3];
43 - }
44 - const y = t2;
35 + const y = t1;
36 return y;
37 }
38
compiler/forget/src/__tests__/fixtures/hir/call.expect.md
+2 -9
@@ -21,7 +21,7 @@ function Component(props) {
21 function foo() {}
22
23 function Component(props) {
24 - const $ = React.unstable_useMemoCache(3);
24 + const $ = React.unstable_useMemoCache(2);
25 let a;
26 let b;
27 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
@@ -36,14 +36,7 @@ function Component(props) {
36 a = $[0];
37 b = $[1];
38 }
39 - let t0;
40 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
41 - t0 = <div a={a} b={b}></div>;
42 - $[2] = t0;
43 - } else {
44 - t0 = $[2];
45 - }
46 - return t0;
39 + return <div a={a} b={b}></div>;
40 }
41
42 ```
compiler/forget/src/__tests__/fixtures/hir/capture-func-passed-to-jsx.expect.md
+2 -11
@@ -20,7 +20,7 @@ function component(a, b) {
20
21 ```javascript
22 function component(a, b) {
23 - const $ = React.unstable_useMemoCache(9);
23 + const $ = React.unstable_useMemoCache(7);
24 const c_0 = $[0] !== b;
25 let t0;
26 if (c_0) {
@@ -56,16 +56,7 @@ function component(a, b) {
56 t2 = $[6];
57 }
58 const x = t2;
59 - const c_7 = $[7] !== x;
60 - let t3;
61 - if (c_7) {
62 - t3 = <Foo x={x}></Foo>;
63 - $[7] = x;
64 - $[8] = t3;
65 - } else {
66 - t3 = $[8];
67 - }
68 - const t = t3;
59 + const t = <Foo x={x}></Foo>;
60 mutate(x);
61 return t;
62 }
compiler/forget/src/__tests__/fixtures/hir/capturing-function-member-expr-call.expect.md
+2 -13
@@ -20,7 +20,7 @@ function component({ mutator }) {
20
21 ```javascript
22 function component(t27) {
23 - const $ = React.unstable_useMemoCache(7);
23 + const $ = React.unstable_useMemoCache(4);
24 const { mutator } = t27;
25 const c_0 = $[0] !== mutator;
26 let t0;
@@ -46,18 +46,7 @@ function component(t27) {
46 t1 = $[3];
47 }
48 const hide = t1;
49 - const c_4 = $[4] !== poke;
50 - const c_5 = $[5] !== hide;
51 - let t2;
52 - if (c_4 || c_5) {
53 - t2 = <Foo poke={poke} hide={hide}></Foo>;
54 - $[4] = poke;
55 - $[5] = hide;
56 - $[6] = t2;
57 - } else {
58 - t2 = $[6];
59 - }
60 - return t2;
49 + return <Foo poke={poke} hide={hide}></Foo>;
50 }
51
52 ```
compiler/forget/src/__tests__/fixtures/hir/component.expect.md
+8 -37
@@ -35,7 +35,7 @@ function Component(props) {
35
36 ```javascript
37 function Component(props) {
38 - const $ = React.unstable_useMemoCache(10);
38 + const $ = React.unstable_useMemoCache(3);
39 const items = props.items;
40 const maxItems = props.maxItems;
41 const c_0 = $[0] !== maxItems;
@@ -44,16 +44,7 @@ function Component(props) {
44 if (c_0 || c_1) {
45 renderedItems = [];
46 const seen = new Set();
47 - const c_3 = $[3] !== maxItems;
48 - let t0;
49 - if (c_3) {
50 - t0 = Math.max(0, maxItems);
51 - $[3] = maxItems;
52 - $[4] = t0;
53 - } else {
54 - t0 = $[4];
55 - }
56 - const max = t0;
47 + const max = Math.max(0, maxItems);
48 for (let i = 0; i < items.length; i = i + 1, i) {
49 const item = items.at(i);
50 if (item == null || seen.has(item)) {
@@ -74,32 +65,12 @@ function Component(props) {
65 }
66
67 const count = renderedItems.length;
77 - const c_5 = $[5] !== count;
78 - let t1;
79 - if (c_5) {
80 - t1 = <h1>{count} Items</h1>;
81 - $[5] = count;
82 - $[6] = t1;
83 - } else {
84 - t1 = $[6];
85 - }
86 - const c_7 = $[7] !== t1;
87 - const c_8 = $[8] !== renderedItems;
88 - let t2;
89 - if (c_7 || c_8) {
90 - t2 = (
91 - <div>
92 - {t1}
93 - {renderedItems}
94 - </div>
95 - );
96 - $[7] = t1;
97 - $[8] = renderedItems;
98 - $[9] = t2;
99 - } else {
100 - t2 = $[9];
101 - }
102 - return t2;
68 + return (
69 + <div>
70 + {<h1>{count} Items</h1>}
71 + {renderedItems}
72 + </div>
73 + );
74 }
75
76 ```
compiler/forget/src/__tests__/fixtures/hir/concise-arrow-expr.expect.md
+2 -11
@@ -14,7 +14,7 @@ function component() {
14
15 ```javascript
16 function component() {
17 - const $ = React.unstable_useMemoCache(4);
17 + const $ = React.unstable_useMemoCache(2);
18 const [x, setX] = useState(0);
19 const c_0 = $[0] !== setX;
20 let t0;
@@ -26,16 +26,7 @@ function component() {
26 t0 = $[1];
27 }
28 const handler = t0;
29 - const c_2 = $[2] !== handler;
30 - let t1;
31 - if (c_2) {
32 - t1 = <Foo handler={handler}></Foo>;
33 - $[2] = handler;
34 - $[3] = t1;
35 - } else {
36 - t1 = $[3];
37 - }
38 - return t1;
29 + return <Foo handler={handler}></Foo>;
30 }
31
32 ```
compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md
+4 -26
@@ -35,7 +35,7 @@ function mayMutate() {}
35
36 ```javascript
37 function ComponentA(props) {
38 - const $ = React.unstable_useMemoCache(6);
38 + const $ = React.unstable_useMemoCache(3);
39 const c_0 = $[0] !== props;
40 let a;
41 let b;
@@ -55,22 +55,11 @@ function ComponentA(props) {
55 a = $[1];
56 b = $[2];
57 }
58 - const c_3 = $[3] !== a;
59 - const c_4 = $[4] !== b;
60 - let t0;
61 - if (c_3 || c_4) {
62 - t0 = <Foo a={a} b={b}></Foo>;
63 - $[3] = a;
64 - $[4] = b;
65 - $[5] = t0;
66 - } else {
67 - t0 = $[5];
68 - }
69 - return t0;
58 + return <Foo a={a} b={b}></Foo>;
59 }
60
61 function ComponentB(props) {
73 - const $ = React.unstable_useMemoCache(6);
62 + const $ = React.unstable_useMemoCache(3);
63 const c_0 = $[0] !== props;
64 let a;
65 let b;
@@ -90,18 +79,7 @@ function ComponentB(props) {
79 a = $[1];
80 b = $[2];
81 }
93 - const c_3 = $[3] !== a;
94 - const c_4 = $[4] !== b;
95 - let t0;
96 - if (c_3 || c_4) {
97 - t0 = <Foo a={a} b={b}></Foo>;
98 - $[3] = a;
99 - $[4] = b;
100 - $[5] = t0;
101 - } else {
102 - t0 = $[5];
103 - }
104 - return t0;
82 + return <Foo a={a} b={b}></Foo>;
83 }
84
85 function Foo() {}
compiler/forget/src/__tests__/fixtures/hir/constant-propagation-for.expect.md
+3 -10
@@ -16,16 +16,9 @@ function foo() {
16
17 ```javascript
18 function foo() {
19 - const $ = React.unstable_useMemoCache(1);
20 - let y;
21 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22 - y = 0;
23 - for (const x = 100; false; 100) {
24 - y = y + 1;
25 - }
26 - $[0] = y;
27 - } else {
28 - y = $[0];
19 + let y = 0;
20 + for (const x = 100; false; 100) {
21 + y = y + 1;
22 }
23 return y;
24 }
compiler/forget/src/__tests__/fixtures/hir/constant-propagation-while.expect.md
+3 -10
@@ -17,16 +17,9 @@ function foo() {
17
18 ```javascript
19 function foo() {
20 - const $ = React.unstable_useMemoCache(1);
21 - let y;
22 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
23 - y = 0;
24 - while (false) {
25 - y = y + 1;
26 - }
27 - $[0] = y;
28 - } else {
29 - y = $[0];
20 + let y = 0;
21 + while (false) {
22 + y = y + 1;
23 }
24 return y;
25 }
compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md
+2 -9
@@ -21,7 +21,7 @@ function Component(props) {
21 function Foo() {}
22
23 function Component(props) {
24 - const $ = React.unstable_useMemoCache(3);
24 + const $ = React.unstable_useMemoCache(2);
25 let a;
26 let b;
27 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
@@ -35,14 +35,7 @@ function Component(props) {
35 a = $[0];
36 b = $[1];
37 }
38 - let t0;
39 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
40 - t0 = <div a={a} b={b}></div>;
41 - $[2] = t0;
42 - } else {
43 - t0 = $[2];
44 - }
45 - return t0;
38 + return <div a={a} b={b}></div>;
39 }
40
41 ```
compiler/forget/src/__tests__/fixtures/hir/controlled-input.expect.md
+2 -13
@@ -14,7 +14,7 @@ function component() {
14
15 ```javascript
16 function component() {
17 - const $ = React.unstable_useMemoCache(5);
17 + const $ = React.unstable_useMemoCache(2);
18 const [x, setX] = useState(0);
19 const c_0 = $[0] !== setX;
20 let t0;
@@ -26,18 +26,7 @@ function component() {
26 t0 = $[1];
27 }
28 const handler = t0;
29 - const c_2 = $[2] !== handler;
30 - const c_3 = $[3] !== x;
31 - let t1;
32 - if (c_2 || c_3) {
33 - t1 = <input onChange={handler} value={x}></input>;
34 - $[2] = handler;
35 - $[3] = x;
36 - $[4] = t1;
37 - } else {
38 - t1 = $[4];
39 - }
40 - return t1;
29 + return <input onChange={handler} value={x}></input>;
30 }
31
32 ```
compiler/forget/src/__tests__/fixtures/hir/dce-loop.expect.md
+3 -12
@@ -18,18 +18,9 @@ function foo(props) {
18
19 ```javascript
20 function foo(props) {
21 - const $ = React.unstable_useMemoCache(2);
22 - const c_0 = $[0] !== props.max;
23 - let y;
24 - if (c_0) {
25 - y = 0;
26 - while (y < props.max) {
27 - y = y + 1;
28 - }
29 - $[0] = props.max;
30 - $[1] = y;
31 - } else {
32 - y = $[1];
21 + let y = 0;
22 + while (y < props.max) {
23 + y = y + 1;
24 }
25 return y;
26 }
compiler/forget/src/__tests__/fixtures/hir/destructure-direct-reassignment.expect.md
+2 -16
@@ -16,22 +16,8 @@ function foo(props) {
16
17 ```javascript
18 function foo(props) {
19 - const $ = React.unstable_useMemoCache(4);
20 - const c_0 = $[0] !== props.a;
21 - const c_1 = $[1] !== props.b;
22 - let x;
23 - let y;
24 - if (c_0 || c_1) {
25 - ({ x, y } = { x: props.a, y: props.b });
26 - console.log(x);
27 - $[0] = props.a;
28 - $[1] = props.b;
29 - $[2] = x;
30 - $[3] = y;
31 - } else {
32 - x = $[2];
33 - y = $[3];
34 - }
19 + let { x, y } = { x: props.a, y: props.b };
20 + console.log(x);
21 x = props.c;
22 return x + y;
23 }
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-jsx-child.expect.md new
+59
@@ -0,0 +1,59 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo(a, b, c) {
6 + const x = [];
7 + if (a) {
8 + const y = [];
9 + if (b) {
10 + y.push(c);
11 + }
12 + x.push(<div>{y}</div>);
13 + }
14 + return x;
15 +}
16 +
17 +```
18 +
19 +## Code
20 +
21 +```javascript
22 +function foo(a, b, c) {
23 + const $ = React.unstable_useMemoCache(7);
24 + const c_0 = $[0] !== a;
25 + const c_1 = $[1] !== b;
26 + const c_2 = $[2] !== c;
27 + let x;
28 + if (c_0 || c_1 || c_2) {
29 + x = [];
30 + if (a) {
31 + const c_4 = $[4] !== b;
32 + const c_5 = $[5] !== c;
33 + let y;
34 + if (c_4 || c_5) {
35 + y = [];
36 + if (b) {
37 + y.push(c);
38 + }
39 + $[4] = b;
40 + $[5] = c;
41 + $[6] = y;
42 + } else {
43 + y = $[6];
44 + }
45 +
46 + x.push(<div>{y}</div>);
47 + }
48 + $[0] = a;
49 + $[1] = b;
50 + $[2] = c;
51 + $[3] = x;
52 + } else {
53 + x = $[3];
54 + }
55 + return x;
56 +}
57 +
58 +```
59 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-jsx-child.js new
+11
@@ -0,0 +1,11 @@
1 +function foo(a, b, c) {
2 + const x = [];
3 + if (a) {
4 + const y = [];
5 + if (b) {
6 + y.push(c);
7 + }
8 + x.push(<div>{y}</div>);
9 + }
10 + return x;
11 +}
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-logical.expect.md new
+68
@@ -0,0 +1,68 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const a = [props.a];
7 + const b = [props.b];
8 + const c = [props.c];
9 + // We don't do constant folding for non-primitive values (yet) so we consider
10 + // that any of a, b, or c could return here
11 + return (a && b) || c;
12 +}
13 +
14 +```
15 +
16 +## Code
17 +
18 +```javascript
19 +function Component(props) {
20 + const $ = React.unstable_useMemoCache(10);
21 + const c_0 = $[0] !== props.a;
22 + let t0;
23 + if (c_0) {
24 + t0 = [props.a];
25 + $[0] = props.a;
26 + $[1] = t0;
27 + } else {
28 + t0 = $[1];
29 + }
30 + const a = t0;
31 + const c_2 = $[2] !== props.b;
32 + let t1;
33 + if (c_2) {
34 + t1 = [props.b];
35 + $[2] = props.b;
36 + $[3] = t1;
37 + } else {
38 + t1 = $[3];
39 + }
40 + const b = t1;
41 + const c_4 = $[4] !== props.c;
42 + let t2;
43 + if (c_4) {
44 + t2 = [props.c];
45 + $[4] = props.c;
46 + $[5] = t2;
47 + } else {
48 + t2 = $[5];
49 + }
50 + const c = t2;
51 + const c_6 = $[6] !== a;
52 + const c_7 = $[7] !== b;
53 + const c_8 = $[8] !== c;
54 + let t3;
55 + if (c_6 || c_7 || c_8) {
56 + t3 = (a && b) || c;
57 + $[6] = a;
58 + $[7] = b;
59 + $[8] = c;
60 + $[9] = t3;
61 + } else {
62 + t3 = $[9];
63 + }
64 + return t3;
65 +}
66 +
67 +```
68 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-logical.js new
+8
@@ -0,0 +1,8 @@
1 +function Component(props) {
2 + const a = [props.a];
3 + const b = [props.b];
4 + const c = [props.c];
5 + // We don't do constant folding for non-primitive values (yet) so we consider
6 + // that any of a, b, or c could return here
7 + return (a && b) || c;
8 +}
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-allocating-dependency.expect.md new
+57
@@ -0,0 +1,57 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + // a can be independently memoized, is not mutated later
7 + const a = [props.a];
8 +
9 + // b and c are interleaved and grouped into a single scope,
10 + // but they are independent values. c does not escape, but
11 + // we need to ensure that a is memoized or else b will invalidate
12 + // on every render since a is a dependency.
13 + const b = [];
14 + const c = {};
15 + c.a = a;
16 + b.push(props.b);
17 +
18 + return b;
19 +}
20 +
21 +```
22 +
23 +## Code
24 +
25 +```javascript
26 +function Component(props) {
27 + const $ = React.unstable_useMemoCache(5);
28 + const c_0 = $[0] !== props.a;
29 + let t0;
30 + if (c_0) {
31 + t0 = [props.a];
32 + $[0] = props.a;
33 + $[1] = t0;
34 + } else {
35 + t0 = $[1];
36 + }
37 + const a = t0;
38 + const c_2 = $[2] !== a;
39 + const c_3 = $[3] !== props.b;
40 + let b;
41 + if (c_2 || c_3) {
42 + b = [];
43 + const c = {};
44 + c.a = a;
45 +
46 + b.push(props.b);
47 + $[2] = a;
48 + $[3] = props.b;
49 + $[4] = b;
50 + } else {
51 + b = $[4];
52 + }
53 + return b;
54 +}
55 +
56 +```
57 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-allocating-dependency.js new
+15
@@ -0,0 +1,15 @@
1 +function Component(props) {
2 + // a can be independently memoized, is not mutated later
3 + const a = [props.a];
4 +
5 + // b and c are interleaved and grouped into a single scope,
6 + // but they are independent values. c does not escape, but
7 + // we need to ensure that a is memoized or else b will invalidate
8 + // on every render since a is a dependency.
9 + const b = [];
10 + const c = {};
11 + c.a = a;
12 + b.push(props.b);
13 +
14 + return b;
15 +}
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-primitive-dependency.expect.md new
+51
@@ -0,0 +1,51 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + // a does not need to be memoized ever, even though it's a
7 + // dependency of c, which exists in a scope that has a memoized
8 + // output. it doesn't need to be memoized bc the value is a primitive type.
9 + const a = props.a + props.b;
10 +
11 + // b and c are interleaved and grouped into a single scope,
12 + // but they are independent values. c does not escape, but
13 + // we need to ensure that a is memoized or else b will invalidate
14 + // on every render since a is a dependency.
15 + const b = [];
16 + const c = {};
17 + c.a = a;
18 + b.push(props.c);
19 +
20 + return b;
21 +}
22 +
23 +```
24 +
25 +## Code
26 +
27 +```javascript
28 +function Component(props) {
29 + const $ = React.unstable_useMemoCache(3);
30 +
31 + const a = props.a + props.b;
32 + const c_0 = $[0] !== a;
33 + const c_1 = $[1] !== props.c;
34 + let b;
35 + if (c_0 || c_1) {
36 + b = [];
37 + const c = {};
38 + c.a = a;
39 +
40 + b.push(props.c);
41 + $[0] = a;
42 + $[1] = props.c;
43 + $[2] = b;
44 + } else {
45 + b = $[2];
46 + }
47 + return b;
48 +}
49 +
50 +```
51 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-primitive-dependency.js new
+17
@@ -0,0 +1,17 @@
1 +function Component(props) {
2 + // a does not need to be memoized ever, even though it's a
3 + // dependency of c, which exists in a scope that has a memoized
4 + // output. it doesn't need to be memoized bc the value is a primitive type.
5 + const a = props.a + props.b;
6 +
7 + // b and c are interleaved and grouped into a single scope,
8 + // but they are independent values. c does not escape, but
9 + // we need to ensure that a is memoized or else b will invalidate
10 + // on every render since a is a dependency.
11 + const b = [];
12 + const c = {};
13 + c.a = a;
14 + b.push(props.c);
15 +
16 + return b;
17 +}
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-conditional-test.expect.md new
+23
@@ -0,0 +1,23 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const x = [props.a];
7 + const y = x ? props.b : props.c;
8 + return y;
9 +}
10 +
11 +```
12 +
13 +## Code
14 +
15 +```javascript
16 +function Component(props) {
17 + const x = [props.a];
18 + const y = x ? props.b : props.c;
19 + return y;
20 +}
21 +
22 +```
23 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-conditional-test.js new
+5
@@ -0,0 +1,5 @@
1 +function Component(props) {
2 + const x = [props.a];
3 + const y = x ? props.b : props.c;
4 + return y;
5 +}
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-if-test.expect.md new
+33
@@ -0,0 +1,33 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const x = [props.a];
7 + let y;
8 + if (x) {
9 + y = props.b;
10 + } else {
11 + y = props.c;
12 + }
13 + return y;
14 +}
15 +
16 +```
17 +
18 +## Code
19 +
20 +```javascript
21 +function Component(props) {
22 + const x = [props.a];
23 + let y = undefined;
24 + if (x) {
25 + y = props.b;
26 + } else {
27 + y = props.c;
28 + }
29 + return y;
30 +}
31 +
32 +```
33 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-if-test.js new
+10
@@ -0,0 +1,10 @@
1 +function Component(props) {
2 + const x = [props.a];
3 + let y;
4 + if (x) {
5 + y = props.b;
6 + } else {
7 + y = props.c;
8 + }
9 + return y;
10 +}
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-case.expect.md new
+33
@@ -0,0 +1,33 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const a = [props.a];
7 + let x = props.b;
8 + switch (props.c) {
9 + case a: {
10 + x = props.d;
11 + }
12 + }
13 + return x;
14 +}
15 +
16 +```
17 +
18 +## Code
19 +
20 +```javascript
21 +function Component(props) {
22 + const a = [props.a];
23 + let x = props.b;
24 + switch (props.c) {
25 + case a: {
26 + x = props.d;
27 + }
28 + }
29 + return x;
30 +}
31 +
32 +```
33 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-case.js new
+10
@@ -0,0 +1,10 @@
1 +function Component(props) {
2 + const a = [props.a];
3 + let x = props.b;
4 + switch (props.c) {
5 + case a: {
6 + x = props.d;
7 + }
8 + }
9 + return x;
10 +}
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-test.expect.md new
+33
@@ -0,0 +1,33 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const a = [props.a];
7 + let x = props.b;
8 + switch (a) {
9 + case true: {
10 + x = props.c;
11 + }
12 + }
13 + return x;
14 +}
15 +
16 +```
17 +
18 +## Code
19 +
20 +```javascript
21 +function Component(props) {
22 + const a = [props.a];
23 + let x = props.b;
24 + switch (a) {
25 + case true: {
26 + x = props.c;
27 + }
28 + }
29 + return x;
30 +}
31 +
32 +```
33 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-test.js new
+10
@@ -0,0 +1,10 @@
1 +function Component(props) {
2 + const a = [props.a];
3 + let x = props.b;
4 + switch (a) {
5 + case true: {
6 + x = props.c;
7 + }
8 + }
9 + return x;
10 +}
compiler/forget/src/__tests__/fixtures/hir/for-logical.expect.md
+8 -17
@@ -21,23 +21,14 @@ function foo(props) {
21
22 ```javascript
23 function foo(props) {
24 - const $ = React.unstable_useMemoCache(2);
25 - const c_0 = $[0] !== props;
26 - let y;
27 - if (c_0) {
28 - y = 0;
29 - for (
30 - let x = 0;
31 - x > props.min && x < props.max;
32 - x = x + (props.cond ? props.increment : 2), x
33 - ) {
34 - x = x * 2;
35 - y = y + x;
36 - }
37 - $[0] = props;
38 - $[1] = y;
39 - } else {
40 - y = $[1];
24 + let y = 0;
25 + for (
26 + let x = 0;
27 + x > props.min && x < props.max;
28 + x = x + (props.cond ? props.increment : 2), x
29 + ) {
30 + x = x * 2;
31 + y = y + x;
32 }
33 return y;
34 }
compiler/forget/src/__tests__/fixtures/hir/gating-test-export-default-function.expect.md
+2 -22
@@ -28,17 +28,7 @@ function Bar_uncompiled(props) {
28 return <div>{props.bar}</div>;
29 }
30 function Bar_forget(props) {
31 - const $ = React.unstable_useMemoCache(2);
32 - const c_0 = $[0] !== props.bar;
33 - let t0;
34 - if (c_0) {
35 - t0 = <div>{props.bar}</div>;
36 - $[0] = props.bar;
37 - $[1] = t0;
38 - } else {
39 - t0 = $[1];
40 - }
41 - return t0;
31 + return <div>{props.bar}</div>;
32 }
33 const Bar = isForgetEnabled ? Bar_forget : Bar_uncompiled;
34 export default Bar;
@@ -52,17 +42,7 @@ function Foo_uncompiled(props) {
42 return <Foo>{props.bar}</Foo>;
43 }
44 function Foo_forget(props) {
55 - const $ = React.unstable_useMemoCache(2);
56 - const c_0 = $[0] !== props.bar;
57 - let t0;
58 - if (c_0) {
59 - t0 = <Foo>{props.bar}</Foo>;
60 - $[0] = props.bar;
61 - $[1] = t0;
62 - } else {
63 - t0 = $[1];
64 - }
65 - return t0;
45 + return <Foo>{props.bar}</Foo>;
46 }
47 const Foo = isForgetEnabled ? Foo_forget : Foo_uncompiled;
48
compiler/forget/src/__tests__/fixtures/hir/gating-test-export-function-and-default.expect.md
+2 -22
@@ -28,17 +28,7 @@ function Bar_uncompiled(props) {
28 return <div>{props.bar}</div>;
29 }
30 function Bar_forget(props) {
31 - const $ = React.unstable_useMemoCache(2);
32 - const c_0 = $[0] !== props.bar;
33 - let t0;
34 - if (c_0) {
35 - t0 = <div>{props.bar}</div>;
36 - $[0] = props.bar;
37 - $[1] = t0;
38 - } else {
39 - t0 = $[1];
40 - }
41 - return t0;
31 + return <div>{props.bar}</div>;
32 }
33 const Bar = isForgetEnabled ? Bar_forget : Bar_uncompiled;
34 export default Bar;
@@ -52,17 +42,7 @@ function Foo_uncompiled(props) {
42 return <Foo>{props.bar}</Foo>;
43 }
44 function Foo_forget(props) {
55 - const $ = React.unstable_useMemoCache(2);
56 - const c_0 = $[0] !== props.bar;
57 - let t0;
58 - if (c_0) {
59 - t0 = <Foo>{props.bar}</Foo>;
60 - $[0] = props.bar;
61 - $[1] = t0;
62 - } else {
63 - t0 = $[1];
64 - }
65 - return t0;
45 + return <Foo>{props.bar}</Foo>;
46 }
47 export const Foo = isForgetEnabled ? Foo_forget : Foo_uncompiled;
48
compiler/forget/src/__tests__/fixtures/hir/gating-test-export-function.expect.md
+2 -22
@@ -28,17 +28,7 @@ function Bar_uncompiled(props) {
28 return <div>{props.bar}</div>;
29 }
30 function Bar_forget(props) {
31 - const $ = React.unstable_useMemoCache(2);
32 - const c_0 = $[0] !== props.bar;
33 - let t0;
34 - if (c_0) {
35 - t0 = <div>{props.bar}</div>;
36 - $[0] = props.bar;
37 - $[1] = t0;
38 - } else {
39 - t0 = $[1];
40 - }
41 - return t0;
31 + return <div>{props.bar}</div>;
32 }
33 export const Bar = isForgetEnabled ? Bar_forget : Bar_uncompiled;
34
@@ -51,17 +41,7 @@ function Foo_uncompiled(props) {
41 return <Foo>{props.bar}</Foo>;
42 }
43 function Foo_forget(props) {
54 - const $ = React.unstable_useMemoCache(2);
55 - const c_0 = $[0] !== props.bar;
56 - let t0;
57 - if (c_0) {
58 - t0 = <Foo>{props.bar}</Foo>;
59 - $[0] = props.bar;
60 - $[1] = t0;
61 - } else {
62 - t0 = $[1];
63 - }
64 - return t0;
44 + return <Foo>{props.bar}</Foo>;
45 }
46 export const Foo = isForgetEnabled ? Foo_forget : Foo_uncompiled;
47
compiler/forget/src/__tests__/fixtures/hir/gating-test.expect.md
+2 -22
@@ -28,17 +28,7 @@ function Bar_uncompiled(props) {
28 return <div>{props.bar}</div>;
29 }
30 function Bar_forget(props) {
31 - const $ = React.unstable_useMemoCache(2);
32 - const c_0 = $[0] !== props.bar;
33 - let t0;
34 - if (c_0) {
35 - t0 = <div>{props.bar}</div>;
36 - $[0] = props.bar;
37 - $[1] = t0;
38 - } else {
39 - t0 = $[1];
40 - }
41 - return t0;
31 + return <div>{props.bar}</div>;
32 }
33 const Bar = isForgetEnabled ? Bar_forget : Bar_uncompiled;
34
@@ -51,17 +41,7 @@ function Foo_uncompiled(props) {
41 return <Foo>{props.bar}</Foo>;
42 }
43 function Foo_forget(props) {
54 - const $ = React.unstable_useMemoCache(2);
55 - const c_0 = $[0] !== props.bar;
56 - let t0;
57 - if (c_0) {
58 - t0 = <Foo>{props.bar}</Foo>;
59 - $[0] = props.bar;
60 - $[1] = t0;
61 - } else {
62 - t0 = $[1];
63 - }
64 - return t0;
44 + return <Foo>{props.bar}</Foo>;
45 }
46 const Foo = isForgetEnabled ? Foo_forget : Foo_uncompiled;
47
compiler/forget/src/__tests__/fixtures/hir/hook-call.expect.md
+7 -16
@@ -26,7 +26,7 @@ function useFreeze() {}
26 function foo() {}
27
28 function Component(props) {
29 - const $ = React.unstable_useMemoCache(3);
29 + const $ = React.unstable_useMemoCache(1);
30 let t0;
31 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
32 t0 = [];
@@ -37,21 +37,12 @@ function Component(props) {
37 const x = t0;
38 const y = useFreeze(x);
39 foo(y, x);
40 - const c_1 = $[1] !== y;
41 - let t1;
42 - if (c_1) {
43 - t1 = (
44 - <Component>
45 - {x}
46 - {y}
47 - </Component>
48 - );
49 - $[1] = y;
50 - $[2] = t1;
51 - } else {
52 - t1 = $[2];
53 - }
54 - return t1;
40 + return (
41 + <Component>
42 + {x}
43 + {y}
44 + </Component>
45 + );
46 }
47
48 ```
compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md
+2 -13
@@ -56,7 +56,7 @@ function Foo() {}
56 * return = <Foo a={a} b={b} />
57 */
58 function Component(props) {
59 - const $ = React.unstable_useMemoCache(8);
59 + const $ = React.unstable_useMemoCache(5);
60 const c_0 = $[0] !== props.a;
61 const c_1 = $[1] !== props.b;
62 const c_2 = $[2] !== props.c;
@@ -78,18 +78,7 @@ function Component(props) {
78 a = $[3];
79 b = $[4];
80 }
81 - const c_5 = $[5] !== a;
82 - const c_6 = $[6] !== b;
83 - let t0;
84 - if (c_5 || c_6) {
85 - t0 = <Foo a={a} b={b}></Foo>;
86 - $[5] = a;
87 - $[6] = b;
88 - $[7] = t0;
89 - } else {
90 - t0 = $[7];
91 - }
92 - return t0;
81 + return <Foo a={a} b={b}></Foo>;
82 }
83
84 ```
compiler/forget/src/__tests__/fixtures/hir/independent.expect.md
+2 -13
@@ -38,7 +38,7 @@ function Foo() {}
38 * return = <Foo a={a} b={b} />
39 */
40 function Component(props) {
41 - const $ = React.unstable_useMemoCache(7);
41 + const $ = React.unstable_useMemoCache(4);
42 const c_0 = $[0] !== props.a;
43 let t0;
44 if (c_0) {
@@ -59,18 +59,7 @@ function Component(props) {
59 t1 = $[3];
60 }
61 const b = t1;
62 - const c_4 = $[4] !== a;
63 - const c_5 = $[5] !== b;
64 - let t2;
65 - if (c_4 || c_5) {
66 - t2 = <Foo a={a} b={b}></Foo>;
67 - $[4] = a;
68 - $[5] = b;
69 - $[6] = t2;
70 - } else {
71 - t2 = $[6];
72 - }
73 - return t2;
62 + return <Foo a={a} b={b}></Foo>;
63 }
64
65 function compute() {}
compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md
+2 -13
@@ -45,7 +45,7 @@ function Foo() {}
45 * return = <Foo a={a} b={b} />
46 */
47 function Component(props) {
48 - const $ = React.unstable_useMemoCache(8);
48 + const $ = React.unstable_useMemoCache(5);
49 const c_0 = $[0] !== props.a;
50 const c_1 = $[1] !== props.b;
51 const c_2 = $[2] !== props.c;
@@ -66,18 +66,7 @@ function Component(props) {
66 a = $[3];
67 b = $[4];
68 }
69 - const c_5 = $[5] !== a;
70 - const c_6 = $[6] !== b;
71 - let t0;
72 - if (c_5 || c_6) {
73 - t0 = <Foo a={a} b={b}></Foo>;
74 - $[5] = a;
75 - $[6] = b;
76 - $[7] = t0;
77 - } else {
78 - t0 = $[7];
79 - }
80 - return t0;
69 + return <Foo a={a} b={b}></Foo>;
70 }
71
72 ```
compiler/forget/src/__tests__/fixtures/hir/interdependent.expect.md
+2 -13
@@ -37,7 +37,7 @@ function Foo() {}
37 * return = <Foo a={a} b={b} />
38 */
39 function Component(props) {
40 - const $ = React.unstable_useMemoCache(7);
40 + const $ = React.unstable_useMemoCache(4);
41 const c_0 = $[0] !== props.a;
42 const c_1 = $[1] !== props.b;
43 let a;
@@ -54,18 +54,7 @@ function Component(props) {
54 a = $[2];
55 b = $[3];
56 }
57 - const c_4 = $[4] !== a;
58 - const c_5 = $[5] !== b;
59 - let t0;
60 - if (c_4 || c_5) {
61 - t0 = <Foo a={a} b={b}></Foo>;
62 - $[4] = a;
63 - $[5] = b;
64 - $[6] = t0;
65 - } else {
66 - t0 = $[6];
67 - }
68 - return t0;
57 + return <Foo a={a} b={b}></Foo>;
58 }
59
60 function compute() {}
compiler/forget/src/__tests__/fixtures/hir/jsx-fragment.expect.md
+6 -30
@@ -19,36 +19,12 @@ function Foo(props) {
19
20 ```javascript
21 function Foo(props) {
22 - const $ = React.unstable_useMemoCache(4);
23 - let t0;
24 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
25 - t0 = <>Text</>;
26 - $[0] = t0;
27 - } else {
28 - t0 = $[0];
29 - }
30 - let t1;
31 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
32 - t1 = <div>{t0}</div>;
33 - $[1] = t1;
34 - } else {
35 - t1 = $[1];
36 - }
37 - const c_2 = $[2] !== props.greeting;
38 - let t2;
39 - if (c_2) {
40 - t2 = (
41 - <>
42 - Hello {props.greeting}
43 - {t1}
44 - </>
45 - );
46 - $[2] = props.greeting;
47 - $[3] = t2;
48 - } else {
49 - t2 = $[3];
50 - }
51 - return t2;
22 + return (
23 + <>
24 + Hello {props.greeting}
25 + {<div>{<>Text</>}</div>}
26 + </>
27 + );
28 }
29
30 ```
compiler/forget/src/__tests__/fixtures/hir/jsx-spread.expect.md
+9 -27
@@ -14,37 +14,19 @@ function Component(props) {
14
15 ```javascript
16 function Component(props) {
17 - const $ = React.unstable_useMemoCache(7);
18 - const c_0 = $[0] !== props;
19 - let t0;
20 - if (c_0) {
21 - t0 = props.cond ? props.foo : props.bar;
22 - $[0] = props;
23 - $[1] = t0;
24 - } else {
25 - t0 = $[1];
26 - }
27 - const c_2 = $[2] !== t0;
17 + const $ = React.unstable_useMemoCache(2);
18 +
19 + const t0 = props.cond ? props.foo : props.bar;
20 + const c_0 = $[0] !== t0;
21 let t1;
29 - if (c_2) {
22 + if (c_0) {
23 t1 = { bar: t0 };
31 - $[2] = t0;
32 - $[3] = t1;
33 - } else {
34 - t1 = $[3];
35 - }
36 - const c_4 = $[4] !== props;
37 - const c_5 = $[5] !== t1;
38 - let t2;
39 - if (c_4 || c_5) {
40 - t2 = <Component {...props} {...t1}></Component>;
41 - $[4] = props;
42 - $[5] = t1;
43 - $[6] = t2;
24 + $[0] = t0;
25 + $[1] = t1;
26 } else {
45 - t2 = $[6];
27 + t1 = $[1];
28 }
47 - return t2;
29 + return <Component {...props} {...t1}></Component>;
30 }
31
32 ```
compiler/forget/src/__tests__/fixtures/hir/logical-expression-object.expect.md
+13 -30
@@ -19,39 +19,22 @@ function component(props) {
19
20 ```javascript
21 function component(props) {
22 - const $ = React.unstable_useMemoCache(7);
23 - const c_0 = $[0] !== props;
22 + const $ = React.unstable_useMemoCache(3);
23 +
24 + const a = props.a || (props.b && props.c && props.d);
25 + const b = (props.a && props.b && props.c) || props.d;
26 + const c_0 = $[0] !== a;
27 + const c_1 = $[1] !== b;
28 let t0;
25 - if (c_0) {
26 - t0 = props.a || (props.b && props.c && props.d);
27 - $[0] = props;
28 - $[1] = t0;
29 - } else {
30 - t0 = $[1];
31 - }
32 - const a = t0;
33 - const c_2 = $[2] !== props;
34 - let t1;
35 - if (c_2) {
36 - t1 = (props.a && props.b && props.c) || props.d;
37 - $[2] = props;
38 - $[3] = t1;
39 - } else {
40 - t1 = $[3];
41 - }
42 - const b = t1;
43 - const c_4 = $[4] !== a;
44 - const c_5 = $[5] !== b;
45 - let t2;
46 - if (c_4 || c_5) {
47 - t2 = { a, b };
48 - $[4] = a;
49 - $[5] = b;
50 - $[6] = t2;
29 + if (c_0 || c_1) {
30 + t0 = { a, b };
31 + $[0] = a;
32 + $[1] = b;
33 + $[2] = t0;
34 } else {
52 - t2 = $[6];
35 + t0 = $[2];
36 }
54 - return t2;
37 + return t0;
38 }
39
40 ```
compiler/forget/src/__tests__/fixtures/hir/logical-expression.expect.md
+3 -35
@@ -14,41 +14,9 @@ function component(props) {
14
15 ```javascript
16 function component(props) {
17 - const $ = React.unstable_useMemoCache(8);
18 - const c_0 = $[0] !== props;
19 - let t0;
20 - if (c_0) {
21 - t0 = props.a || (props.b && props.c && props.d);
22 - $[0] = props;
23 - $[1] = t0;
24 - } else {
25 - t0 = $[1];
26 - }
27 - const a = t0;
28 - const c_2 = $[2] !== props;
29 - let t1;
30 - if (c_2) {
31 - t1 = (props.a && props.b && props.c) || props.d;
32 - $[2] = props;
33 - $[3] = t1;
34 - } else {
35 - t1 = $[3];
36 - }
37 - const b = t1;
38 - const c_4 = $[4] !== a;
39 - const c_5 = $[5] !== b;
40 - const c_6 = $[6] !== props;
41 - let t2;
42 - if (c_4 || c_5 || c_6) {
43 - t2 = a ? b : props.c;
44 - $[4] = a;
45 - $[5] = b;
46 - $[6] = props;
47 - $[7] = t2;
48 - } else {
49 - t2 = $[7];
50 - }
51 - return t2;
17 + const a = props.a || (props.b && props.c && props.d);
18 + const b = (props.a && props.b && props.c) || props.d;
19 + return a ? b : props.c;
20 }
21
22 ```
compiler/forget/src/__tests__/fixtures/hir/mutable-liverange-loop.expect.md
+1 -9
@@ -41,17 +41,9 @@ function mutate() {}
41 function cond() {}
42
43 function Component(props) {
44 - const $ = React.unstable_useMemoCache(1);
44 const a = {};
45 const b = {};
47 - let t0;
48 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
49 - t0 = {};
50 - $[0] = t0;
51 - } else {
52 - t0 = $[0];
53 - }
54 - const c = t0;
46 + const c = {};
47 const d = {};
48 while (true) {
49 mutate(a, b);
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md
+3 -11
@@ -21,7 +21,7 @@ function foo(a, b, c) {
21
22 ```javascript
23 function foo(a, b, c) {
24 - const $ = React.unstable_useMemoCache(9);
24 + const $ = React.unstable_useMemoCache(7);
25 const c_0 = $[0] !== a;
26 const c_1 = $[1] !== b;
27 const c_2 = $[2] !== c;
@@ -43,16 +43,8 @@ function foo(a, b, c) {
43 } else {
44 y = $[6];
45 }
46 - const c_7 = $[7] !== y;
47 - let t0;
48 - if (c_7) {
49 - t0 = <div>{y}</div>;
50 - $[7] = y;
51 - $[8] = t0;
52 - } else {
53 - t0 = $[8];
54 - }
55 - x.push(t0);
46 +
47 + x.push(<div>{y}</div>);
48 }
49 $[0] = a;
50 $[1] = b;
compiler/forget/src/__tests__/fixtures/hir/property-assignment.expect.md
+2 -13
@@ -17,7 +17,7 @@ function Component(props) {
17
18 ```javascript
19 function Component(props) {
20 - const $ = React.unstable_useMemoCache(6);
20 + const $ = React.unstable_useMemoCache(3);
21 const c_0 = $[0] !== props.p0;
22 let x;
23 let child;
@@ -35,18 +35,7 @@ function Component(props) {
35 x = $[1];
36 child = $[2];
37 }
38 - const c_3 = $[3] !== x;
39 - const c_4 = $[4] !== child;
40 - let t0;
41 - if (c_3 || c_4) {
42 - t0 = <Component data={x}>{child}</Component>;
43 - $[3] = x;
44 - $[4] = child;
45 - $[5] = t0;
46 - } else {
47 - t0 = $[5];
48 - }
49 - return t0;
38 + return <Component data={x}>{child}</Component>;
39 }
40
41 ```
compiler/forget/src/__tests__/fixtures/hir/reactive-scopes-if.expect.md
+2 -11
@@ -20,7 +20,7 @@ function foo(a, b, c) {
20
21 ```javascript
22 function foo(a, b, c) {
23 - const $ = React.unstable_useMemoCache(8);
23 + const $ = React.unstable_useMemoCache(6);
24 const c_0 = $[0] !== a;
25 const c_1 = $[1] !== b;
26 const c_2 = $[2] !== c;
@@ -38,16 +38,7 @@ function foo(a, b, c) {
38 } else {
39 y = $[5];
40 }
41 - const c_6 = $[6] !== y;
42 - let t0;
43 - if (c_6) {
44 - t0 = <div>{y}</div>;
45 - $[6] = y;
46 - $[7] = t0;
47 - } else {
48 - t0 = $[7];
49 - }
50 - x.push(t0);
41 + x.push(<div>{y}</div>);
42 } else {
43 x.push(c);
44 }
compiler/forget/src/__tests__/fixtures/hir/reactive-scopes.expect.md
+2 -11
@@ -19,7 +19,7 @@ function f(a, b) {
19
20 ```javascript
21 function f(a, b) {
22 - const $ = React.unstable_useMemoCache(5);
22 + const $ = React.unstable_useMemoCache(3);
23 const c_0 = $[0] !== a.length;
24 const c_1 = $[1] !== b;
25 let x;
@@ -36,16 +36,7 @@ function f(a, b) {
36 } else {
37 x = $[2];
38 }
39 - const c_3 = $[3] !== x;
40 - let t0;
41 - if (c_3) {
42 - t0 = <div>{x}</div>;
43 - $[3] = x;
44 - $[4] = t0;
45 - } else {
46 - t0 = $[4];
47 - }
48 - return t0;
39 + return <div>{x}</div>;
40 }
41
42 ```
compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md
+2 -13
@@ -23,7 +23,7 @@ function Component(props) {
23
24 ```javascript
25 function Component(props) {
26 - const $ = React.unstable_useMemoCache(6);
26 + const $ = React.unstable_useMemoCache(3);
27 const c_0 = $[0] !== props.p0;
28 let x;
29 if (c_0) {
@@ -47,18 +47,7 @@ function Component(props) {
47 }
48
49 y.push(props.p2);
50 - const c_3 = $[3] !== x;
51 - const c_4 = $[4] !== y;
52 - let t1;
53 - if (c_3 || c_4) {
54 - t1 = <Component x={x} y={y}></Component>;
55 - $[3] = x;
56 - $[4] = y;
57 - $[5] = t1;
58 - } else {
59 - t1 = $[5];
60 - }
61 - return t1;
50 + return <Component x={x} y={y}></Component>;
51 }
52
53 ```
compiler/forget/src/__tests__/fixtures/hir/reassignment-separate-scopes.expect.md
+19 -38
@@ -34,7 +34,7 @@ function foo(a, b, c) {
34
35 ```javascript
36 function foo(a, b, c) {
37 - const $ = React.unstable_useMemoCache(11);
37 + const $ = React.unstable_useMemoCache(6);
38 const c_0 = $[0] !== a;
39 let x;
40 if (c_0) {
@@ -47,58 +47,39 @@ function foo(a, b, c) {
47 } else {
48 x = $[1];
49 }
50 - const c_2 = $[2] !== x;
51 - let t0;
52 - if (c_2) {
53 - t0 = <div>{x}</div>;
54 - $[2] = x;
55 - $[3] = t0;
56 - } else {
57 - t0 = $[3];
58 - }
59 - const y = t0;
50 +
51 + const y = <div>{x}</div>;
52 bb3: switch (b) {
53 case 0: {
62 - const c_4 = $[4] !== b;
63 - if (c_4) {
54 + const c_2 = $[2] !== b;
55 + if (c_2) {
56 x = [];
57 x.push(b);
66 - $[4] = b;
67 - $[5] = x;
58 + $[2] = b;
59 + $[3] = x;
60 } else {
69 - x = $[5];
61 + x = $[3];
62 }
63 break bb3;
64 }
65 default: {
74 - const c_6 = $[6] !== c;
75 - if (c_6) {
66 + const c_4 = $[4] !== c;
67 + if (c_4) {
68 x = [];
69 x.push(c);
78 - $[6] = c;
79 - $[7] = x;
70 + $[4] = c;
71 + $[5] = x;
72 } else {
81 - x = $[7];
73 + x = $[5];
74 }
75 }
76 }
85 - const c_8 = $[8] !== y;
86 - const c_9 = $[9] !== x;
87 - let t1;
88 - if (c_8 || c_9) {
89 - t1 = (
90 - <div>
91 - {y}
92 - {x}
93 - </div>
94 - );
95 - $[8] = y;
96 - $[9] = x;
97 - $[10] = t1;
98 - } else {
99 - t1 = $[10];
100 - }
101 - return t1;
77 + return (
78 + <div>
79 + {y}
80 + {x}
81 + </div>
82 + );
83 }
84
85 ```
compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md
+2 -13
@@ -21,7 +21,7 @@ function Component(props) {
21
22 ```javascript
23 function Component(props) {
24 - const $ = React.unstable_useMemoCache(8);
24 + const $ = React.unstable_useMemoCache(5);
25 const c_0 = $[0] !== props.p0;
26 const c_1 = $[1] !== props.p1;
27 let x;
@@ -48,18 +48,7 @@ function Component(props) {
48 x = $[2];
49 y = $[3];
50 }
51 - const c_5 = $[5] !== x;
52 - const c_6 = $[6] !== y;
53 - let t1;
54 - if (c_5 || c_6) {
55 - t1 = <Component x={x} y={y}></Component>;
56 - $[5] = x;
57 - $[6] = y;
58 - $[7] = t1;
59 - } else {
60 - t1 = $[7];
61 - }
62 - return t1;
51 + return <Component x={x} y={y}></Component>;
52 }
53
54 ```
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-ifelse.expect.md
+2 -11
@@ -26,22 +26,13 @@ function TestCondDepInDirectIfElse(props, other) {
26 // paths
27
28 function TestCondDepInDirectIfElse(props, other) {
29 - const $ = React.unstable_useMemoCache(5);
29 + const $ = React.unstable_useMemoCache(3);
30 const c_0 = $[0] !== other;
31 const c_1 = $[1] !== props.a.b;
32 let x;
33 if (c_0 || c_1) {
34 x = {};
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) {
35 + if (foo(other)) {
36 x.b = props.a.b;
37 } else {
38 x.c = props.a.b;
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-nested-ifelse-missing.expect.md
+3 -19
@@ -26,30 +26,14 @@ function TestCondDepInNestedIfElse(props, other) {
26 // scope that produces x if it is not accessed in every path
27
28 function TestCondDepInNestedIfElse(props, other) {
29 - const $ = React.unstable_useMemoCache(6);
29 + const $ = React.unstable_useMemoCache(3);
30 const c_0 = $[0] !== other;
31 const c_1 = $[1] !== props;
32 let x;
33 if (c_0 || c_1) {
34 x = {};
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 - let t1;
46 - if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
47 - t1 = bar();
48 - $[5] = t1;
49 - } else {
50 - t1 = $[5];
51 - }
52 - if (t1) {
35 + if (foo(other)) {
36 + if (bar()) {
37 x.a = props.a.b;
38 }
39 } else {
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-nested-ifelse.expect.md
+4 -29
@@ -32,45 +32,20 @@ function TestCondDepInNestedIfElse(props, other) {
32 // paths
33
34 function TestCondDepInNestedIfElse(props, other) {
35 - const $ = React.unstable_useMemoCache(8);
35 + const $ = React.unstable_useMemoCache(3);
36 const c_0 = $[0] !== other;
37 const c_1 = $[1] !== props.a.b;
38 let x;
39 if (c_0 || c_1) {
40 x = {};
41 - const c_3 = $[3] !== other;
42 - let t0;
43 - if (c_3) {
44 - t0 = foo(other);
45 - $[3] = other;
46 - $[4] = t0;
47 - } else {
48 - t0 = $[4];
49 - }
50 - if (t0) {
51 - let t1;
52 - if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
53 - t1 = bar();
54 - $[5] = t1;
55 - } else {
56 - t1 = $[5];
57 - }
58 - if (t1) {
41 + if (foo(other)) {
42 + if (bar()) {
43 x.a = props.a.b;
44 } else {
45 x.b = props.a.b;
46 }
47 } else {
64 - const c_6 = $[6] !== other;
65 - let t2;
66 - if (c_6) {
67 - t2 = baz(other);
68 - $[6] = other;
69 - $[7] = t2;
70 - } else {
71 - t2 = $[7];
72 - }
73 - if (t2) {
48 + if (baz(other)) {
49 x.c = props.a.b;
50 } else {
51 x.d = props.a.b;
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch-missing-case.expect.md
+2 -11
@@ -30,22 +30,13 @@ function TestCondDepInSwitchMissingCase(props, other) {
30 // scope that produces x if it is not accessed in every path
31
32 function TestCondDepInSwitchMissingCase(props, other) {
33 - const $ = React.unstable_useMemoCache(5);
33 + const $ = React.unstable_useMemoCache(3);
34 const c_0 = $[0] !== other;
35 const c_1 = $[1] !== props;
36 let x;
37 if (c_0 || c_1) {
38 x = {};
39 - const c_3 = $[3] !== other;
40 - let t0;
41 - if (c_3) {
42 - t0 = foo(other);
43 - $[3] = other;
44 - $[4] = t0;
45 - } else {
46 - t0 = $[4];
47 - }
48 - bb1: switch (t0) {
39 + bb1: switch (foo(other)) {
40 case 1: {
41 x.a = props.a.b;
42 break bb1;
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch-missing-default.expect.md
+2 -11
@@ -27,22 +27,13 @@ function TestCondDepInSwitchMissingDefault(props, other) {
27 // scope that produces x if it is not accessed in the default case.
28
29 function TestCondDepInSwitchMissingDefault(props, other) {
30 - const $ = React.unstable_useMemoCache(5);
30 + const $ = React.unstable_useMemoCache(3);
31 const c_0 = $[0] !== other;
32 const c_1 = $[1] !== props;
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 - bb1: switch (t0) {
36 + bb1: switch (foo(other)) {
37 case 1: {
38 x.a = props.a.b;
39 break bb1;
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch.expect.md
+2 -11
@@ -31,22 +31,13 @@ function TestCondDepInSwitch(props, other) {
31 // paths
32
33 function TestCondDepInSwitch(props, other) {
34 - const $ = React.unstable_useMemoCache(5);
34 + const $ = React.unstable_useMemoCache(3);
35 const c_0 = $[0] !== other;
36 const c_1 = $[1] !== props.a.b;
37 let x;
38 if (c_0 || c_1) {
39 x = {};
40 - const c_3 = $[3] !== other;
41 - let t0;
42 - if (c_3) {
43 - t0 = foo(other);
44 - $[3] = other;
45 - $[4] = t0;
46 - } else {
47 - t0 = $[4];
48 - }
49 - bb1: switch (t0) {
40 + bb1: switch (foo(other)) {
41 case 1: {
42 x.a = props.a.b;
43 break bb1;
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-no-uncond.expect.md
+2 -11
@@ -21,22 +21,13 @@ function TestOnlyConditionalDependencies(props, other) {
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);
24 + const $ = React.unstable_useMemoCache(3);
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) {
30 + if (foo(other)) {
31 x.b = props.a.b;
32 x.c = props.a.b.c;
33 }
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-promote-uncond.expect.md
+2 -11
@@ -23,23 +23,14 @@ function TestPromoteUnconditionalAccessToDependency(props, other) {
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);
26 + const $ = React.unstable_useMemoCache(3);
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) {
33 + if (foo(other)) {
34 x.c = props.a.b.c;
35 }
36 $[0] = props.a;
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order1.expect.md
+2 -11
@@ -27,23 +27,14 @@ function TestConditionalSubpath1(props, other) {
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);
30 + const $ = React.unstable_useMemoCache(3);
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) {
37 + if (foo(other)) {
38 x.a = props.a;
39 }
40 $[0] = props.a;
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order2.expect.md
+2 -11
@@ -27,22 +27,13 @@ function TestConditionalSubpath2(props, other) {
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);
30 + const $ = React.unstable_useMemoCache(3);
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) {
36 + if (foo(other)) {
37 x.a = props.a;
38 }
39 x.b = props.a.b;
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order1.expect.md
+2 -11
@@ -25,23 +25,14 @@ function TestConditionalSuperpath1(props, other) {
25 // as a dependency
26 // ordering of accesses should not matter
27 function TestConditionalSuperpath1(props, other) {
28 - const $ = React.unstable_useMemoCache(5);
28 + const $ = React.unstable_useMemoCache(3);
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) {
35 + if (foo(other)) {
36 x.b = props.a.b;
37 }
38 $[0] = props.a;
compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order2.expect.md
+2 -11
@@ -25,22 +25,13 @@ function TestConditionalSuperpath2(props, other) {
25 // as a dependency
26 // ordering of accesses should not matter
27 function TestConditionalSuperpath2(props, other) {
28 - const $ = React.unstable_useMemoCache(5);
28 + const $ = React.unstable_useMemoCache(3);
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) {
34 + if (foo(other)) {
35 x.b = props.a.b;
36 }
37 x.a = props.a;
compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.expect.md
+27 -58
@@ -46,7 +46,7 @@ function foo(props) {
46 // note: comments are for the ideal scopes, not what is currently
47 // emitted
48 function foo(props) {
49 - const $ = React.unstable_useMemoCache(16);
49 + const $ = React.unstable_useMemoCache(7);
50 const c_0 = $[0] !== props.a;
51 let x;
52 if (c_0) {
@@ -57,70 +57,39 @@ function foo(props) {
57 } else {
58 x = $[1];
59 }
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.showHeader;
66 - $[3] = x;
67 - $[4] = t0;
68 - } else {
69 - t0 = $[4];
70 - }
71 - const header = t0;
72 - const c_5 = $[5] !== x;
73 - const c_6 = $[6] !== props.b;
74 - const c_7 = $[7] !== props.c;
60 +
61 + const header = props.showHeader ? <div>{x}</div> : null;
62 + const c_2 = $[2] !== x;
63 + const c_3 = $[3] !== props.b;
64 + const c_4 = $[4] !== props.c;
65 let y;
76 - if (c_5 || c_6 || c_7) {
66 + if (c_2 || c_3 || c_4) {
67 y = [x];
68 x = [];
69 y.push(props.b);
70 x.push(props.c);
81 - $[5] = x;
82 - $[6] = props.b;
83 - $[7] = props.c;
84 - $[8] = y;
85 - $[9] = x;
86 - } else {
87 - y = $[8];
88 - x = $[9];
89 - }
90 - const c_10 = $[10] !== x;
91 - const c_11 = $[11] !== y;
92 - let t1;
93 - if (c_10 || c_11) {
94 - t1 = (
95 - <div>
96 - {x}
97 - {y}
98 - </div>
99 - );
100 - $[10] = x;
101 - $[11] = y;
102 - $[12] = t1;
71 + $[2] = x;
72 + $[3] = props.b;
73 + $[4] = props.c;
74 + $[5] = y;
75 + $[6] = x;
76 } else {
104 - t1 = $[12];
77 + y = $[5];
78 + x = $[6];
79 }
106 - const content = t1;
107 - const c_13 = $[13] !== header;
108 - const c_14 = $[14] !== content;
109 - let t2;
110 - if (c_13 || c_14) {
111 - t2 = (
112 - <>
113 - {header}
114 - {content}
115 - </>
116 - );
117 - $[13] = header;
118 - $[14] = content;
119 - $[15] = t2;
120 - } else {
121 - t2 = $[15];
122 - }
123 - return t2;
80 +
81 + const content = (
82 + <div>
83 + {x}
84 + {y}
85 + </div>
86 + );
87 + return (
88 + <>
89 + {header}
90 + {content}
91 + </>
92 + );
93 }
94
95 ```
compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.expect.md
+26 -55
@@ -46,7 +46,7 @@ function foo(props) {
46 // note: comments are for the ideal scopes, not what is currently
47 // emitted
48 function foo(props) {
49 - const $ = React.unstable_useMemoCache(15);
49 + const $ = React.unstable_useMemoCache(7);
50 const c_0 = $[0] !== props.a;
51 let x;
52 if (c_0) {
@@ -57,68 +57,39 @@ function foo(props) {
57 } else {
58 x = $[1];
59 }
60 +
61 + const header = <div>{x}</div>;
62 const c_2 = $[2] !== x;
61 - let t0;
62 - if (c_2) {
63 - t0 = <div>{x}</div>;
64 - $[2] = x;
65 - $[3] = t0;
66 - } else {
67 - t0 = $[3];
68 - }
69 - const header = t0;
70 - const c_4 = $[4] !== x;
71 - const c_5 = $[5] !== props.b;
72 - const c_6 = $[6] !== props.c;
63 + const c_3 = $[3] !== props.b;
64 + const c_4 = $[4] !== props.c;
65 let y;
74 - if (c_4 || c_5 || c_6) {
66 + if (c_2 || c_3 || c_4) {
67 y = [x];
68 x = [];
69 y.push(props.b);
70 x.push(props.c);
79 - $[4] = x;
80 - $[5] = props.b;
81 - $[6] = props.c;
82 - $[7] = y;
83 - $[8] = x;
84 - } else {
85 - y = $[7];
86 - x = $[8];
87 - }
88 - const c_9 = $[9] !== x;
89 - const c_10 = $[10] !== y;
90 - let t1;
91 - if (c_9 || c_10) {
92 - t1 = (
93 - <div>
94 - {x}
95 - {y}
96 - </div>
97 - );
98 - $[9] = x;
99 - $[10] = y;
100 - $[11] = t1;
101 - } else {
102 - t1 = $[11];
103 - }
104 - const content = t1;
105 - const c_12 = $[12] !== header;
106 - const c_13 = $[13] !== content;
107 - let t2;
108 - if (c_12 || c_13) {
109 - t2 = (
110 - <>
111 - {header}
112 - {content}
113 - </>
114 - );
115 - $[12] = header;
116 - $[13] = content;
117 - $[14] = t2;
71 + $[2] = x;
72 + $[3] = props.b;
73 + $[4] = props.c;
74 + $[5] = y;
75 + $[6] = x;
76 } else {
119 - t2 = $[14];
77 + y = $[5];
78 + x = $[6];
79 }
121 - return t2;
80 +
81 + const content = (
82 + <div>
83 + {x}
84 + {y}
85 + </div>
86 + );
87 + return (
88 + <>
89 + {header}
90 + {content}
91 + </>
92 + );
93 }
94
95 ```
compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md
+3 -17
@@ -25,21 +25,14 @@ function Component(props) {
25 function foo() {}
26
27 function Component(props) {
28 - const $ = React.unstable_useMemoCache(4);
28 + const $ = React.unstable_useMemoCache(2);
29 let a;
30 let b;
31 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
32 a = [];
33 b = {};
34 foo(a, b);
35 - let t0;
36 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
37 - t0 = foo();
38 - $[2] = t0;
39 - } else {
40 - t0 = $[2];
41 - }
42 - if (t0) {
35 + if (foo()) {
36 }
37
38 foo(a, b);
@@ -49,14 +42,7 @@ function Component(props) {
42 a = $[0];
43 b = $[1];
44 }
52 - let t1;
53 - if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
54 - t1 = <div a={a} b={b}></div>;
55 - $[3] = t1;
56 - } else {
57 - t1 = $[3];
58 - }
59 - return t1;
45 + return <div a={a} b={b}></div>;
46 }
47
48 ```
compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx.expect.md
+2 -9
@@ -21,7 +21,7 @@ function Component(props) {
21 function foo() {}
22
23 function Component(props) {
24 - const $ = React.unstable_useMemoCache(3);
24 + const $ = React.unstable_useMemoCache(2);
25 let a;
26 let b;
27 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
@@ -36,14 +36,7 @@ function Component(props) {
36 a = $[0];
37 b = $[1];
38 }
39 - let t0;
40 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
41 - t0 = <div a={a} b={b}></div>;
42 - $[2] = t0;
43 - } else {
44 - t0 = $[2];
45 - }
46 - return t0;
39 + return <div a={a} b={b}></div>;
40 }
41
42 ```
compiler/forget/src/__tests__/fixtures/hir/ssa-cascading-eliminated-phis.expect.md
+2 -11
@@ -24,22 +24,13 @@ function Component(props) {
24
25 ```javascript
26 function Component(props) {
27 - const $ = React.unstable_useMemoCache(4);
27 + const $ = React.unstable_useMemoCache(2);
28 let x = 0;
29 const c_0 = $[0] !== props;
30 let values;
31 if (c_0) {
32 values = [];
33 - const c_2 = $[2] !== props;
34 - let t0;
35 - if (c_2) {
36 - t0 = props.a || props.b;
37 - $[2] = props;
38 - $[3] = t0;
39 - } else {
40 - t0 = $[3];
41 - }
42 - const y = t0;
33 + const y = props.a || props.b;
34 values.push(y);
35 if (props.c) {
36 x = 1;
compiler/forget/src/__tests__/fixtures/hir/ssa-for-trivial-update.expect.md
+3 -10
@@ -16,16 +16,9 @@ function foo() {
16
17 ```javascript
18 function foo() {
19 - const $ = React.unstable_useMemoCache(1);
20 - let x;
21 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22 - x = 1;
23 - for (const i = 0; true; 0) {
24 - x = x + 1;
25 - }
26 - $[0] = x;
27 - } else {
28 - x = $[0];
19 + let x = 1;
20 + for (const i = 0; true; 0) {
21 + x = x + 1;
22 }
23 return x;
24 }
compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md
+3 -10
@@ -16,16 +16,9 @@ function foo() {
16
17 ```javascript
18 function foo() {
19 - const $ = React.unstable_useMemoCache(1);
20 - let x;
21 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22 - x = 1;
23 - for (let i = 0; i < 10; i = i + 1, i) {
24 - x = x + 1;
25 - }
26 - $[0] = x;
27 - } else {
28 - x = $[0];
19 + let x = 1;
20 + for (let i = 0; i < 10; i = i + 1, i) {
21 + x = x + 1;
22 }
23 return x;
24 }
compiler/forget/src/__tests__/fixtures/hir/ssa-leave-case.expect.md
+7 -18
@@ -23,7 +23,7 @@ function Component(props) {
23
24 ```javascript
25 function Component(props) {
26 - const $ = React.unstable_useMemoCache(6);
26 + const $ = React.unstable_useMemoCache(3);
27 const c_0 = $[0] !== props;
28 let x;
29 let y;
@@ -41,23 +41,12 @@ function Component(props) {
41 x = $[1];
42 y = $[2];
43 }
44 - const c_3 = $[3] !== x;
45 - const c_4 = $[4] !== y;
46 - let t0;
47 - if (c_3 || c_4) {
48 - t0 = (
49 - <Component>
50 - {x}
51 - {y}
52 - </Component>
53 - );
54 - $[3] = x;
55 - $[4] = y;
56 - $[5] = t0;
57 - } else {
58 - t0 = $[5];
59 - }
60 - return t0;
44 + return (
45 + <Component>
46 + {x}
47 + {y}
48 + </Component>
49 + );
50 }
51
52 ```
compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md
+3 -10
@@ -17,16 +17,9 @@ function foo() {
17
18 ```javascript
19 function foo() {
20 - const $ = React.unstable_useMemoCache(1);
21 - let x;
22 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
23 - x = 1;
24 - while (x < 10) {
25 - x = x + 1;
26 - }
27 - $[0] = x;
28 - } else {
29 - x = $[0];
20 + let x = 1;
21 + while (x < 10) {
22 + x = x + 1;
23 }
24 return x;
25 }
compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md
+4 -23
@@ -32,7 +32,7 @@ function Component(props) {
32
33 ```javascript
34 function Component(props) {
35 - const $ = React.unstable_useMemoCache(9);
35 + const $ = React.unstable_useMemoCache(4);
36 const c_0 = $[0] !== props;
37 let x;
38 let y;
@@ -69,29 +69,10 @@ function Component(props) {
69 x = $[1];
70 y = $[2];
71 }
72 - const c_4 = $[4] !== x;
73 - let t1;
74 - if (c_4) {
75 - t1 = <Component data={x}></Component>;
76 - $[4] = x;
77 - $[5] = t1;
78 - } else {
79 - t1 = $[5];
80 - }
81 - const child = t1;
72 +
73 + const child = <Component data={x}></Component>;
74 y.push(props.p4);
83 - const c_6 = $[6] !== y;
84 - const c_7 = $[7] !== child;
85 - let t2;
86 - if (c_6 || c_7) {
87 - t2 = <Component data={y}>{child}</Component>;
88 - $[6] = y;
89 - $[7] = child;
90 - $[8] = t2;
91 - } else {
92 - t2 = $[8];
93 - }
94 - return t2;
75 + return <Component data={y}>{child}</Component>;
76 }
77
78 ```
compiler/forget/src/__tests__/fixtures/hir/switch.expect.md
+4 -23
@@ -27,7 +27,7 @@ function Component(props) {
27
28 ```javascript
29 function Component(props) {
30 - const $ = React.unstable_useMemoCache(8);
30 + const $ = React.unstable_useMemoCache(3);
31 const c_0 = $[0] !== props;
32 let x;
33 let y;
@@ -50,29 +50,10 @@ function Component(props) {
50 x = $[1];
51 y = $[2];
52 }
53 - const c_3 = $[3] !== x;
54 - let t0;
55 - if (c_3) {
56 - t0 = <Component data={x}></Component>;
57 - $[3] = x;
58 - $[4] = t0;
59 - } else {
60 - t0 = $[4];
61 - }
62 - const child = t0;
53 +
54 + const child = <Component data={x}></Component>;
55 y.push(props.p4);
64 - const c_5 = $[5] !== y;
65 - const c_6 = $[6] !== child;
66 - let t1;
67 - if (c_5 || c_6) {
68 - t1 = <Component data={y}>{child}</Component>;
69 - $[5] = y;
70 - $[6] = child;
71 - $[7] = t1;
72 - } else {
73 - t1 = $[7];
74 - }
75 - return t1;
56 + return <Component data={y}>{child}</Component>;
57 }
58
59 ```
compiler/forget/src/__tests__/fixtures/hir/temporary-at-start-of-value-block.expect.md
+1 -11
@@ -14,17 +14,7 @@ function component(props) {
14
15 ```javascript
16 function component(props) {
17 - const $ = React.unstable_useMemoCache(2);
18 - const c_0 = $[0] !== props;
19 - let t0;
20 - if (c_0) {
21 - t0 = isMenuShown ? <Bar> {props.a ? props.b : props.c}</Bar> : null;
22 - $[0] = props;
23 - $[1] = t0;
24 - } else {
25 - t0 = $[1];
26 - }
27 - const x = t0;
17 + const x = isMenuShown ? <Bar> {props.a ? props.b : props.c}</Bar> : null;
18 return x;
19 }
20
compiler/forget/src/__tests__/fixtures/hir/ternary-expression.expect.md
+3 -33
@@ -14,39 +14,9 @@ function ternary(props) {
14
15 ```javascript
16 function ternary(props) {
17 - const $ = React.unstable_useMemoCache(7);
18 - const c_0 = $[0] !== props;
19 - let t0;
20 - if (c_0) {
21 - t0 = props.a && props.b ? props.c || props.d : props.e ?? props.f;
22 - $[0] = props;
23 - $[1] = t0;
24 - } else {
25 - t0 = $[1];
26 - }
27 - const a = t0;
28 - const c_2 = $[2] !== props;
29 - let t1;
30 - if (c_2) {
31 - t1 = props.a ? (props.b && props.c ? props.d : props.e) : props.f;
32 - $[2] = props;
33 - $[3] = t1;
34 - } else {
35 - t1 = $[3];
36 - }
37 - const b = t1;
38 - const c_4 = $[4] !== a;
39 - const c_5 = $[5] !== b;
40 - let t2;
41 - if (c_4 || c_5) {
42 - t2 = a ? b : null;
43 - $[4] = a;
44 - $[5] = b;
45 - $[6] = t2;
46 - } else {
47 - t2 = $[6];
48 - }
49 - return t2;
17 + const a = props.a && props.b ? props.c || props.d : props.e ?? props.f;
18 + const b = props.a ? (props.b && props.c ? props.d : props.e) : props.f;
19 + return a ? b : null;
20 }
21
22 ```
compiler/forget/src/__tests__/fixtures/hir/timers.expect.md
+10 -31
@@ -19,43 +19,22 @@ function Component(props) {
19
20 ```javascript
21 function Component(props) {
22 - const $ = React.unstable_useMemoCache(4);
22 + const $ = React.unstable_useMemoCache(1);
23 + const start = performance.now();
24 let t0;
25 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
25 - t0 = performance.now();
26 + t0 = Date.now();
27 $[0] = t0;
28 } else {
29 t0 = $[0];
30 }
30 - const start = t0;
31 - let t1;
32 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
33 - t1 = Date.now();
34 - $[1] = t1;
35 - } else {
36 - t1 = $[1];
37 - }
38 - const now = t1;
39 - let t2;
40 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
41 - t2 = performance.now();
42 - $[2] = t2;
43 - } else {
44 - t2 = $[2];
45 - }
46 - const time = t2 - start;
47 - let t3;
48 - if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
49 - t3 = (
50 - <div>
51 - rendering took {time} at {now}
52 - </div>
53 - );
54 - $[3] = t3;
55 - } else {
56 - t3 = $[3];
57 - }
58 - return t3;
31 + const now = t0;
32 + const time = performance.now() - start;
33 + return (
34 + <div>
35 + rendering took {time} at {now}
36 + </div>
37 + );
38 }
39
40 ```
compiler/forget/src/__tests__/fixtures/hir/type-binary-operator.expect.md
+2 -17
@@ -16,23 +16,8 @@ function component() {
16
17 ```javascript
18 function component() {
19 - const $ = React.unstable_useMemoCache(2);
20 - let t0;
21 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22 - t0 = some();
23 - $[0] = t0;
24 - } else {
25 - t0 = $[0];
26 - }
27 - const a = t0;
28 - let t1;
29 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
30 - t1 = someOther();
31 - $[1] = t1;
32 - } else {
33 - t1 = $[1];
34 - }
35 - const b = t1;
19 + const a = some();
20 + const b = someOther();
21 if (a > b) {
22 }
23 }
compiler/forget/src/__tests__/fixtures/hir/type-test-return-type-inference.expect.md
+6 -20
@@ -19,7 +19,11 @@ function component() {
19
20 ```javascript
21 function component() {
22 - const $ = React.unstable_useMemoCache(3);
22 + const $ = React.unstable_useMemoCache(1);
23 + const x = foo();
24 + const y = foo();
25 + if (x > y) {
26 + }
27 let t0;
28 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
29 t0 = foo();
@@ -27,25 +31,7 @@ function component() {
31 } else {
32 t0 = $[0];
33 }
30 - const x = t0;
31 - let t1;
32 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
33 - t1 = foo();
34 - $[1] = t1;
35 - } else {
36 - t1 = $[1];
37 - }
38 - const y = t1;
39 - if (x > y) {
40 - }
41 - let t2;
42 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
43 - t2 = foo();
44 - $[2] = t2;
45 - } else {
46 - t2 = $[2];
47 - }
48 - const z_0 = t2;
34 + const z_0 = t0;
35 return z_0;
36 }
37
compiler/forget/src/__tests__/fixtures/hir/unary-expr.expect.md
+26 -47
@@ -20,58 +20,37 @@ function component(a) {
20
21 ```javascript
22 function component(a) {
23 - const $ = React.unstable_useMemoCache(14);
24 - const c_0 = $[0] !== a;
25 - let t0;
26 - let t;
27 - let z;
28 - let p;
29 - let q;
30 - if (c_0) {
31 - t = { t: a };
32 - z = +t.t;
33 - q = -t.t;
34 - p = void t.t;
35 - t0 = delete t.t;
36 - $[0] = a;
37 - $[1] = t0;
38 - $[2] = t;
39 - $[3] = z;
40 - $[4] = p;
41 - $[5] = q;
42 - } else {
43 - t0 = $[1];
44 - t = $[2];
45 - z = $[3];
46 - p = $[4];
47 - q = $[5];
48 - }
49 - const n = t0;
23 + const $ = React.unstable_useMemoCache(8);
24 + const t = { t: a };
25 + const z = +t.t;
26 + const q = -t.t;
27 + const p = void t.t;
28 + const n = delete t.t;
29 const m = !t.t;
30 const e = ~t.t;
31 const f = typeof t.t;
53 - const c_6 = $[6] !== z;
54 - const c_7 = $[7] !== p;
55 - const c_8 = $[8] !== q;
56 - const c_9 = $[9] !== n;
57 - const c_10 = $[10] !== m;
58 - const c_11 = $[11] !== e;
59 - const c_12 = $[12] !== f;
60 - let t1;
61 - if (c_6 || c_7 || c_8 || c_9 || c_10 || c_11 || c_12) {
62 - t1 = { z, p, q, n, m, e, f };
63 - $[6] = z;
64 - $[7] = p;
65 - $[8] = q;
66 - $[9] = n;
67 - $[10] = m;
68 - $[11] = e;
69 - $[12] = f;
70 - $[13] = t1;
32 + const c_0 = $[0] !== z;
33 + const c_1 = $[1] !== p;
34 + const c_2 = $[2] !== q;
35 + const c_3 = $[3] !== n;
36 + const c_4 = $[4] !== m;
37 + const c_5 = $[5] !== e;
38 + const c_6 = $[6] !== f;
39 + let t0;
40 + if (c_0 || c_1 || c_2 || c_3 || c_4 || c_5 || c_6) {
41 + t0 = { z, p, q, n, m, e, f };
42 + $[0] = z;
43 + $[1] = p;
44 + $[2] = q;
45 + $[3] = n;
46 + $[4] = m;
47 + $[5] = e;
48 + $[6] = f;
49 + $[7] = t0;
50 } else {
72 - t1 = $[13];
51 + t0 = $[7];
52 }
74 - return t1;
53 + return t0;
54 }
55
56 ```
compiler/forget/src/__tests__/fixtures/hir/use-callback-simple.expect.md
+2 -11
@@ -15,7 +15,7 @@ function component() {
15
16 ```javascript
17 function component() {
18 - const $ = React.unstable_useMemoCache(5);
18 + const $ = React.unstable_useMemoCache(3);
19 const [count, setCount] = useState(0);
20 const c_0 = $[0] !== setCount;
21 const c_1 = $[1] !== count;
@@ -29,16 +29,7 @@ function component() {
29 t0 = $[2];
30 }
31 const increment = t0;
32 - const c_3 = $[3] !== increment;
33 - let t1;
34 - if (c_3) {
35 - t1 = <Foo onClick={increment}></Foo>;
36 - $[3] = increment;
37 - $[4] = t1;
38 - } else {
39 - t1 = $[4];
40 - }
41 - return t1;
32 + return <Foo onClick={increment}></Foo>;
33 }
34
35 ```
compiler/forget/src/__tests__/fixtures/hir/useMemo-simple.expect.md
+2 -11
@@ -13,7 +13,7 @@ function component(a) {
13
14 ```javascript
15 function component(a) {
16 - const $ = React.unstable_useMemoCache(4);
16 + const $ = React.unstable_useMemoCache(2);
17 const c_0 = $[0] !== a;
18 let t0;
19 if (c_0) {
@@ -24,16 +24,7 @@ function component(a) {
24 t0 = $[1];
25 }
26 const x = t0;
27 - const c_2 = $[2] !== x;
28 - let t1;
29 - if (c_2) {
30 - t1 = <Foo x={x}></Foo>;
31 - $[2] = x;
32 - $[3] = t1;
33 - } else {
34 - t1 = $[3];
35 - }
36 - return t1;
27 + return <Foo x={x}></Foo>;
28 }
29
30 ```
compiler/forget/src/__tests__/fixtures/hir/while-property.expect.md
+3 -14
@@ -16,20 +16,9 @@ function foo(a, b) {
16
17 ```javascript
18 function foo(a, b) {
19 - const $ = React.unstable_useMemoCache(3);
20 - const c_0 = $[0] !== a.b.c;
21 - const c_1 = $[1] !== b;
22 - let x;
23 - if (c_0 || c_1) {
24 - x = 0;
25 - while (a.b.c) {
26 - x = x + b;
27 - }
28 - $[0] = a.b.c;
29 - $[1] = b;
30 - $[2] = x;
31 - } else {
32 - x = $[2];
19 + let x = 0;
20 + while (a.b.c) {
21 + x = x + b;
22 }
23 return x;
24 }