[compiler] Delete LoweredFunction.dependencies and hoisted instructions

LoweredFunction dependencies were exclusively used for dependency extraction (in `propagateScopeDeps`). Now that we have a `propagateScopeDepsHIR` that recursively traverses into nested functions, we can delete `dependencies` and their associated artificial `LoadLocal`/`PropertyLoad` instructions.

'
This commit is contained in:
Mofei Zhang
2024-10-25 11:36:54 -07:00
parent 60152cad66
commit a392efd887
38 changed files with 131 additions and 335 deletions
@@ -7,7 +7,6 @@
import {NodePath, Scope} from '@babel/traverse';
import * as t from '@babel/types';
import {Expression} from '@babel/types';
import invariant from 'invariant';
import {
CompilerError,
@@ -3365,7 +3364,7 @@ function lowerFunction(
>,
): LoweredFunction | null {
const componentScope: Scope = builder.parentFunction.scope;
const captured = gatherCapturedDeps(builder, expr, componentScope);
const capturedContext = gatherCapturedContext(expr, componentScope);
/*
* TODO(gsn): In the future, we could only pass in the context identifiers
@@ -3379,7 +3378,7 @@ function lowerFunction(
expr,
builder.environment,
builder.bindings,
[...builder.context, ...captured.identifiers],
[...builder.context, ...capturedContext],
builder.parentFunction,
);
let loweredFunc: HIRFunction;
@@ -3392,7 +3391,6 @@ function lowerFunction(
loweredFunc = lowering.unwrap();
return {
func: loweredFunc,
dependencies: captured.refs,
};
}
@@ -4066,14 +4064,6 @@ function lowerAssignment(
}
}
function isValidDependency(path: NodePath<t.MemberExpression>): boolean {
const parent: NodePath<t.Node> = path.parentPath;
return (
!path.node.computed &&
!(parent.isCallExpression() && parent.get('callee') === path)
);
}
function captureScopes({from, to}: {from: Scope; to: Scope}): Set<Scope> {
let scopes: Set<Scope> = new Set();
while (from) {
@@ -4088,8 +4078,7 @@ function captureScopes({from, to}: {from: Scope; to: Scope}): Set<Scope> {
return scopes;
}
function gatherCapturedDeps(
builder: HIRBuilder,
function gatherCapturedContext(
fn: NodePath<
| t.FunctionExpression
| t.ArrowFunctionExpression
@@ -4097,10 +4086,8 @@ function gatherCapturedDeps(
| t.ObjectMethod
>,
componentScope: Scope,
): {identifiers: Array<t.Identifier>; refs: Array<Place>} {
const capturedIds: Map<t.Identifier, number> = new Map();
const capturedRefs: Set<Place> = new Set();
const seenPaths: Set<string> = new Set();
): Array<t.Identifier> {
const capturedIds = new Set<t.Identifier>();
/*
* Capture all the scopes from the parent of this function up to and including
@@ -4111,33 +4098,11 @@ function gatherCapturedDeps(
to: componentScope,
});
function addCapturedId(bindingIdentifier: t.Identifier): number {
if (!capturedIds.has(bindingIdentifier)) {
const index = capturedIds.size;
capturedIds.set(bindingIdentifier, index);
return index;
} else {
return capturedIds.get(bindingIdentifier)!;
}
}
function handleMaybeDependency(
path:
| NodePath<t.MemberExpression>
| NodePath<t.Identifier>
| NodePath<t.JSXOpeningElement>,
path: NodePath<t.Identifier> | NodePath<t.JSXOpeningElement>,
): void {
// Base context variable to depend on
let baseIdentifier: NodePath<t.Identifier> | NodePath<t.JSXIdentifier>;
/*
* Base expression to depend on, which (for now) may contain non side-effectful
* member expressions
*/
let dependency:
| NodePath<t.MemberExpression>
| NodePath<t.JSXMemberExpression>
| NodePath<t.Identifier>
| NodePath<t.JSXIdentifier>;
if (path.isJSXOpeningElement()) {
const name = path.get('name');
if (!(name.isJSXMemberExpression() || name.isJSXIdentifier())) {
@@ -4153,115 +4118,20 @@ function gatherCapturedDeps(
'Invalid logic in gatherCapturedDeps',
);
baseIdentifier = current;
/*
* Get the expression to depend on, which may involve PropertyLoads
* for member expressions
*/
let currentDep:
| NodePath<t.JSXMemberExpression>
| NodePath<t.Identifier>
| NodePath<t.JSXIdentifier> = baseIdentifier;
while (true) {
const nextDep: null | NodePath<t.Node> = currentDep.parentPath;
if (nextDep && nextDep.isJSXMemberExpression()) {
currentDep = nextDep;
} else {
break;
}
}
dependency = currentDep;
} else if (path.isMemberExpression()) {
// Calculate baseIdentifier
let currentId: NodePath<Expression> = path;
while (currentId.isMemberExpression()) {
currentId = currentId.get('object');
}
if (!currentId.isIdentifier()) {
return;
}
baseIdentifier = currentId;
/*
* Get the expression to depend on, which may involve PropertyLoads
* for member expressions
*/
let currentDep:
| NodePath<t.MemberExpression>
| NodePath<t.Identifier>
| NodePath<t.JSXIdentifier> = baseIdentifier;
while (true) {
const nextDep: null | NodePath<t.Node> = currentDep.parentPath;
if (
nextDep &&
nextDep.isMemberExpression() &&
isValidDependency(nextDep)
) {
currentDep = nextDep;
} else {
break;
}
}
dependency = currentDep;
} else {
baseIdentifier = path;
dependency = path;
}
/*
* Skip dependency path, as we already tried to recursively add it (+ all subexpressions)
* as a dependency.
*/
dependency.skip();
path.skip();
// Add the base identifier binding as a dependency.
const binding = baseIdentifier.scope.getBinding(baseIdentifier.node.name);
if (binding === undefined || !pureScopes.has(binding.scope)) {
return;
}
const idKey = String(addCapturedId(binding.identifier));
// Add the expression (potentially a memberexpr path) as a dependency.
let exprKey = idKey;
if (dependency.isMemberExpression()) {
let pathTokens = [];
let current: NodePath<Expression> = dependency;
while (current.isMemberExpression()) {
const property = current.get('property') as NodePath<t.Identifier>;
pathTokens.push(property.node.name);
current = current.get('object');
}
exprKey += '.' + pathTokens.reverse().join('.');
} else if (dependency.isJSXMemberExpression()) {
let pathTokens = [];
let current: NodePath<t.JSXMemberExpression | t.JSXIdentifier> =
dependency;
while (current.isJSXMemberExpression()) {
const property = current.get('property');
pathTokens.push(property.node.name);
current = current.get('object');
}
}
if (!seenPaths.has(exprKey)) {
let loweredDep: Place;
if (dependency.isJSXIdentifier()) {
loweredDep = lowerValueToTemporary(builder, {
kind: 'LoadLocal',
place: lowerIdentifier(builder, dependency),
loc: path.node.loc ?? GeneratedSource,
});
} else if (dependency.isJSXMemberExpression()) {
loweredDep = lowerJsxMemberExpression(builder, dependency);
} else {
loweredDep = lowerExpressionToTemporary(builder, dependency);
}
capturedRefs.add(loweredDep);
seenPaths.add(exprKey);
if (binding !== undefined && pureScopes.has(binding.scope)) {
capturedIds.add(binding.identifier);
}
}
@@ -4292,13 +4162,13 @@ function gatherCapturedDeps(
return;
} else if (path.isJSXElement()) {
handleMaybeDependency(path.get('openingElement'));
} else if (path.isMemberExpression() || path.isIdentifier()) {
} else if (path.isIdentifier()) {
handleMaybeDependency(path);
}
},
});
return {identifiers: [...capturedIds.keys()], refs: [...capturedRefs]};
return [...capturedIds.keys()];
}
function notNull<T>(value: T | null): value is T {
@@ -131,15 +131,7 @@ function collectHoistablePropertyLoadsImpl(
fn: HIRFunction,
context: CollectHoistablePropertyLoadsContext,
): ReadonlyMap<BlockId, BlockInfo> {
const functionExpressionLoads = collectFunctionExpressionFakeLoads(fn);
const actuallyEvaluatedTemporaries = new Map(
[...context.temporaries].filter(([id]) => !functionExpressionLoads.has(id)),
);
const nodes = collectNonNullsInBlocks(fn, {
...context,
temporaries: actuallyEvaluatedTemporaries,
});
const nodes = collectNonNullsInBlocks(fn, context);
propagateNonNull(fn, nodes, context.registry);
if (DEBUG_PRINT) {
@@ -598,30 +590,3 @@ function reduceMaybeOptionalChains(
}
} while (changed);
}
function collectFunctionExpressionFakeLoads(
fn: HIRFunction,
): Set<IdentifierId> {
const sources = new Map<IdentifierId, IdentifierId>();
const functionExpressionReferences = new Set<IdentifierId>();
for (const [_, block] of fn.body.blocks) {
for (const {lvalue, value} of block.instructions) {
if (
value.kind === 'FunctionExpression' ||
value.kind === 'ObjectMethod'
) {
for (const reference of value.loweredFunc.dependencies) {
let curr: IdentifierId | undefined = reference.identifier.id;
while (curr != null) {
functionExpressionReferences.add(curr);
curr = sources.get(curr);
}
}
} else if (value.kind === 'PropertyLoad') {
sources.set(lvalue.identifier.id, value.object.identifier.id);
}
}
}
return functionExpressionReferences;
}
@@ -231,7 +231,6 @@ const EnvironmentConfigSchema = z.object({
enableUseTypeAnnotations: z.boolean().default(false),
enableFunctionDependencyRewrite: z.boolean().default(true),
/**
* Enables inlining ReactElement object literals in place of JSX
* An alternative to the standard JSX transform which replaces JSX with React's jsxProd() runtime
@@ -722,7 +722,6 @@ export type ObjectProperty = {
};
export type LoweredFunction = {
dependencies: Array<Place>;
func: HIRFunction;
};
@@ -538,9 +538,6 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
.split('\n')
.map(line => ` ${line}`)
.join('\n');
const deps = instrValue.loweredFunc.dependencies
.map(dep => printPlace(dep))
.join(',');
const context = instrValue.loweredFunc.func.context
.map(dep => printPlace(dep))
.join(',');
@@ -557,7 +554,7 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
})
.join(', ') ?? '';
const type = printType(instrValue.loweredFunc.func.returnType).trim();
value = `${kind} ${name} @deps[${deps}] @context[${context}] @effects[${effects}]${type !== '' ? ` return${type}` : ''}:\n${fn}`;
value = `${kind} ${name} @context[${context}] @effects[${effects}]${type !== '' ? ` return${type}` : ''}:\n${fn}`;
break;
}
case 'TaggedTemplateExpression': {
@@ -690,9 +690,8 @@ function collectDependencies(
}
for (const instr of block.instructions) {
if (
fn.env.config.enableFunctionDependencyRewrite &&
(instr.value.kind === 'FunctionExpression' ||
instr.value.kind === 'ObjectMethod')
instr.value.kind === 'FunctionExpression' ||
instr.value.kind === 'ObjectMethod'
) {
context.declare(instr.lvalue.identifier, {
id: instr.id,
@@ -193,7 +193,7 @@ export function* eachInstructionValueOperand(
}
case 'ObjectMethod':
case 'FunctionExpression': {
yield* instrValue.loweredFunc.dependencies;
yield* instrValue.loweredFunc.func.context;
break;
}
case 'TaggedTemplateExpression': {
@@ -517,8 +517,9 @@ export function mapInstructionValueOperands(
}
case 'ObjectMethod':
case 'FunctionExpression': {
instrValue.loweredFunc.dependencies =
instrValue.loweredFunc.dependencies.map(d => fn(d));
instrValue.loweredFunc.func.context =
instrValue.loweredFunc.func.context.map(d => fn(d));
break;
}
case 'TaggedTemplateExpression': {
@@ -10,7 +10,6 @@ import {
Effect,
HIRFunction,
Identifier,
IdentifierName,
LoweredFunction,
Place,
isRefOrRefValue,
@@ -41,20 +40,6 @@ export class IdentifierState {
return identifier;
}
declareProperty(lvalue: Place, object: Place, property: string): void {
const objectDependency = this.properties.get(object.identifier);
let nextDependency: Dependency;
if (objectDependency === undefined) {
nextDependency = {identifier: object.identifier, path: [property]};
} else {
nextDependency = {
identifier: objectDependency.identifier,
path: [...objectDependency.path, property],
};
}
this.properties.set(lvalue.identifier, nextDependency);
}
declareTemporary(lvalue: Place, value: Place): void {
const resolved: Dependency = this.properties.get(value.identifier) ?? {
identifier: value.identifier,
@@ -73,25 +58,10 @@ export default function analyseFunctions(func: HIRFunction): void {
case 'ObjectMethod':
case 'FunctionExpression': {
lower(instr.value.loweredFunc.func);
infer(instr.value.loweredFunc, state, func.context);
break;
}
case 'PropertyLoad': {
state.declareProperty(
instr.lvalue,
instr.value.object,
instr.value.property,
);
break;
}
case 'ComputedLoad': {
/*
* The path is set to an empty string as the path doesn't really
* matter for a computed load.
*/
state.declareProperty(instr.lvalue, instr.value.object, '');
infer(instr.value.loweredFunc, func.context);
break;
}
case 'LoadLocal':
case 'LoadContext': {
if (instr.lvalue.identifier.name === null) {
@@ -115,11 +85,8 @@ function lower(func: HIRFunction): void {
logHIRFunction('AnalyseFunction (inner)', func);
}
function infer(
loweredFunc: LoweredFunction,
state: IdentifierState,
context: Array<Place>,
): void {
// infer loweredFunc (inner) with outer function context
function infer(loweredFunc: LoweredFunction, context: Array<Place>): void {
const mutations = new Map<string, Effect>();
for (const operand of loweredFunc.func.context) {
if (
@@ -130,15 +97,13 @@ function infer(
}
}
for (const dep of loweredFunc.dependencies) {
let name: IdentifierName | null = null;
if (state.properties.has(dep.identifier)) {
const receiver = state.properties.get(dep.identifier)!;
name = receiver.identifier.name;
} else {
name = dep.identifier.name;
}
for (const dep of loweredFunc.func.context) {
CompilerError.invariant(dep.identifier.name !== null, {
reason: 'context refs should always have a name',
description: null,
loc: dep.loc,
suggestions: null,
});
if (isRefOrRefValue(dep.identifier)) {
/*
@@ -149,8 +114,8 @@ function infer(
* render
*/
dep.effect = Effect.Capture;
} else if (name !== null) {
const effect = mutations.get(name.value);
} else {
const effect = mutations.get(dep.identifier.name.value);
if (effect !== undefined) {
dep.effect = effect === Effect.Unknown ? Effect.Capture : effect;
}
@@ -176,7 +141,6 @@ function infer(
const effect = mutations.get(place.identifier.name.value);
if (effect !== undefined) {
place.effect = effect === Effect.Unknown ? Effect.Capture : effect;
loweredFunc.dependencies.push(place);
}
}
@@ -61,22 +61,6 @@ export function inferMutableContextVariables(fn: HIRFunction): void {
for (const [, block] of fn.body.blocks) {
for (const instr of block.instructions) {
switch (instr.value.kind) {
case 'PropertyLoad': {
state.declareProperty(
instr.lvalue,
instr.value.object,
instr.value.property,
);
break;
}
case 'ComputedLoad': {
/*
* The path is set to an empty string as the path doesn't really
* matter for a computed load.
*/
state.declareProperty(instr.lvalue, instr.value.object, '');
break;
}
case 'LoadLocal':
case 'LoadContext': {
if (instr.lvalue.identifier.name === null) {
@@ -270,7 +270,6 @@ function emitSelectorFn(env: Environment, keys: Array<string>): Instruction {
name: null,
loweredFunc: {
func: fn,
dependencies: [],
},
type: 'ArrowFunctionExpression',
loc: GeneratedSource,
@@ -24,7 +24,6 @@ export function outlineFunctions(
}
if (
value.kind === 'FunctionExpression' &&
value.loweredFunc.dependencies.length === 0 &&
value.loweredFunc.func.context.length === 0 &&
// TODO: handle outlining named functions
value.loweredFunc.func.id === null &&
@@ -7,12 +7,15 @@
import {CompilerError} from '../CompilerError';
import {BlockId, HIRFunction, Identifier, Place} from '../HIR/HIR';
import {printHIR, printIdentifier} from '../HIR/PrintHIR';
import {
eachInstructionLValue,
eachInstructionOperand,
eachTerminalOperand,
} from '../HIR/visitors';
const DEBUG = true;
/*
* Pass to eliminate redundant phi nodes:
* - all operands are the same identifier, ie `x2 = phi(x1, x1, x1)`.
@@ -141,6 +144,23 @@ export function eliminateRedundantPhi(
* have already propagated forwards since we visit in reverse postorder.
*/
} while (rewrites.size > size && hasBackEdge);
if (DEBUG) {
for (const [, block] of ir.blocks) {
for (const phi of block.phis) {
CompilerError.invariant(!rewrites.has(phi.place.identifier), {
reason: '[EliminateRedundantPhis]: rewrite not complete',
loc: phi.place.loc,
});
for (const [, operand] of phi.operands) {
CompilerError.invariant(!rewrites.has(operand.identifier), {
reason: '[EliminateRedundantPhis]: rewrite not complete',
loc: phi.place.loc,
});
}
}
}
}
}
function rewritePlace(
@@ -301,9 +301,6 @@ function enterSSAImpl(
entry.preds.add(blockId);
builder.defineFunction(loweredFunc);
builder.enter(() => {
loweredFunc.context = loweredFunc.context.map(p =>
builder.getPlace(p),
);
loweredFunc.params = loweredFunc.params.map(param => {
if (param.kind === 'Identifier') {
return builder.definePlace(param);
@@ -44,48 +44,44 @@ import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRen
import { useEffect, useRef, useState } from "react";
function Component() {
const $ = _c(6);
const $ = _c(5);
const ref = useRef(null);
const [state, setState] = useState(false);
let t0;
let t1;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = () => {};
t1 = [];
t0 = [];
$[0] = t0;
$[1] = t1;
} else {
t0 = $[0];
t1 = $[1];
}
useEffect(t0, t1);
useEffect(_temp, t0);
let t1;
let t2;
let t3;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
t2 = () => {
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
t1 = () => {
setState(true);
};
t3 = [];
t2 = [];
$[1] = t1;
$[2] = t2;
$[3] = t3;
} else {
t1 = $[1];
t2 = $[2];
t3 = $[3];
}
useEffect(t2, t3);
useEffect(t1, t2);
const t4 = String(state);
let t5;
if ($[4] !== t4) {
t5 = <Child key={t4} ref={ref} />;
const t3 = String(state);
let t4;
if ($[3] !== t3) {
t4 = <Child key={t3} ref={ref} />;
$[3] = t3;
$[4] = t4;
$[5] = t5;
} else {
t5 = $[5];
t4 = $[4];
}
return t5;
return t4;
}
function _temp() {}
function Child(t0) {
const { ref } = t0;
@@ -27,7 +27,6 @@ export const FIXTURE_ENTRYPOINT = {
import { c as _c } from "react/compiler-runtime";
function component(a, b) {
const $ = _c(2);
const y = { b };
let z;
if ($[0] !== a) {
z = { a };
@@ -31,12 +31,20 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { c as _c } from "react/compiler-runtime";
function Component(t0) {
const $ = _c(3);
const $ = _c(5);
const { a, b } = t0;
let z;
if ($[0] !== a || $[1] !== b) {
z = { a };
const y = { b };
let t1;
if ($[3] !== b) {
t1 = { b };
$[3] = b;
$[4] = t1;
} else {
t1 = $[4];
}
const y = t1;
const x = function () {
z.a = 2;
return Math.max(y.b, 0);
@@ -34,7 +34,6 @@ function component(a) {
const x = { a };
y = {};
y;
y = x;
mutate(y);
@@ -31,7 +31,6 @@ function bar(a) {
const x = [a];
y = {};
y;
y = x[0][1];
$[0] = a;
$[1] = y;
@@ -37,8 +37,6 @@ function bar(a, b) {
let t;
t = {};
y;
t;
y = x[0][1];
t = x[1][0];
$[0] = a;
@@ -31,7 +31,6 @@ function bar(a) {
const x = [a];
y = {};
y;
y = x[0].a[1];
$[0] = a;
$[1] = y;
@@ -30,7 +30,6 @@ function bar(a) {
const x = [a];
y = {};
y;
y = x[0];
$[0] = a;
$[1] = y;
@@ -25,7 +25,6 @@ function component(a) {
const x = { a };
y = 1;
y;
y = x;
mutate(y);
@@ -38,9 +38,8 @@ function useTest() {
const t1 = (w = 42);
const t2 = w;
w;
let t3;
w = 999;
t3 = 2;
t0 = makeArray(t1, t2, t3);
@@ -19,10 +19,10 @@ function foo() {
import { c as _c } from "react/compiler-runtime";
function foo() {
const $ = _c(1);
const getJSX = _temp;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const getJSX = () => <Child x={GLOBAL_IS_X} />;
t0 = getJSX();
$[0] = t0;
} else {
@@ -31,6 +31,9 @@ function foo() {
const result = t0;
return result;
}
function _temp() {
return <Child x={GLOBAL_IS_X} />;
}
```
@@ -23,13 +23,14 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
function foo() {
const f = () => {
console.log(42);
};
const f = _temp;
f();
return 42;
}
function _temp() {
console.log(42);
}
export const FIXTURE_ENTRYPOINT = {
fn: foo,
@@ -18,12 +18,10 @@ function Component(props) {
import { c as _c } from "react/compiler-runtime";
function Component(props) {
const $ = _c(1);
const onEvent = _temp;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const onEvent = () => {
console.log(42);
};
t0 = <Foo onEvent={onEvent} />;
$[0] = t0;
} else {
@@ -31,6 +29,9 @@ function Component(props) {
}
return t0;
}
function _temp() {
console.log(42);
}
```
@@ -34,9 +34,8 @@ function Component(props) {
let Component;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
Component = Stringify;
Component;
let t0;
t0 = Component;
Component = t0;
$[0] = Component;
@@ -24,13 +24,17 @@ export const FIXTURE_ENTRYPOINT = {
## Error
```
4 | }
5 | return baz(); // OK: FuncDecls are HoistableDeclarations that have both declaration and value hoisting
6 | function baz() {
> 6 | function baz() {
| ^^^^^^^^^^^^^^^^
> 7 | return bar();
| ^^^ Todo: Support functions with unreachable code that may contain hoisted declarations (7:7)
8 | }
| ^^^^^^^^^^^^^^^^^
> 8 | }
| ^^^^ Todo: Support functions with unreachable code that may contain hoisted declarations (6:8)
9 | }
10 |
11 | export const FIXTURE_ENTRYPOINT = {
```
@@ -22,7 +22,7 @@ function Component() {
4 | // NOTE: `i` is a context variable because it's reassigned and also referenced
5 | // within a closure, the `onClick` handler of each item
> 6 | for (let i = MIN; i <= MAX; i += INCREMENT) {
| ^^^^^^^^^^^ Todo: Support for loops where the index variable is a context variable. `i` is a context variable (6:6)
| ^ InvalidReact: Updating a value used previously in JSX is not allowed. Consider moving the mutation before the JSX. Found mutation of `i` (6:6)
7 | items.push(<Stringify key={i} onClick={() => data.set(i)} />);
8 | }
9 | return items;
@@ -22,7 +22,7 @@ function Component(props) {
7 | return hasErrors;
8 | }
> 9 | return hasErrors();
| ^^^^^^^^^ Invariant: [hoisting] Expected value for identifier to be initialized. hasErrors_0$16 (9:9)
| ^^^^^^^^^ Invariant: [hoisting] Expected value for identifier to be initialized. hasErrors_0$14 (9:9)
10 | }
11 |
```
@@ -25,10 +25,10 @@ import { c as _c } from "react/compiler-runtime";
import { Stringify } from "shared-runtime";
function useFoo() {
const $ = _c(1);
const callback = _temp;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const callback = () => <Stringify value={4} />;
t0 = callback();
$[0] = t0;
} else {
@@ -36,6 +36,9 @@ function useFoo() {
}
return t0;
}
function _temp() {
return <Stringify value={4} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
@@ -25,10 +25,10 @@ import { c as _c } from "react/compiler-runtime";
import * as SharedRuntime from "shared-runtime";
function useFoo() {
const $ = _c(1);
const callback = _temp;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const callback = () => <SharedRuntime.Text value={4} />;
t0 = callback();
$[0] = t0;
} else {
@@ -36,6 +36,9 @@ function useFoo() {
}
return t0;
}
function _temp() {
return <SharedRuntime.Text value={4} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
@@ -26,7 +26,6 @@ function f(a) {
const $ = _c(4);
let x;
if ($[0] !== a) {
x;
x = { a };
$[0] = a;
$[1] = x;
@@ -27,7 +27,6 @@ function f(a) {
const $ = _c(2);
let x;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
x;
x = {};
$[0] = x;
} else {
@@ -31,7 +31,7 @@ function Component(props) {
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = (e) => {
setX((currentX) => currentX + null);
setX(_temp);
};
$[0] = t0;
} else {
@@ -48,6 +48,9 @@ function Component(props) {
}
return t1;
}
function _temp(currentX) {
return currentX + null;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
@@ -35,7 +35,6 @@ function useFoo(arr1, arr2) {
if ($[0] !== arr1 || $[1] !== arr2) {
const x = [arr1];
y;
(y = x.concat(arr2)), y;
$[0] = arr1;
$[1] = arr2;
@@ -22,26 +22,21 @@ function Component() {
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
function Component() {
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = () => {
while (bar()) {
if (baz) {
bar();
}
}
return () => 4;
};
$[0] = t0;
} else {
t0 = $[0];
}
const get4 = t0;
const get4 = _temp2;
return get4;
}
function _temp2() {
while (bar()) {
if (baz) {
bar();
}
}
return _temp;
}
function _temp() {
return 4;
}
```
@@ -85,7 +85,6 @@ function Inner(props) {
input = use(FooContext);
}
input;
input;
let t0;
const t1 = input;