mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[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:
@@ -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 {
|
||||
|
||||
+1
-36
@@ -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': {
|
||||
|
||||
+2
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-16
@@ -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);
|
||||
|
||||
+18
-22
@@ -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;
|
||||
|
||||
-1
@@ -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 };
|
||||
|
||||
+10
-2
@@ -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);
|
||||
|
||||
-1
@@ -34,7 +34,6 @@ function component(a) {
|
||||
const x = { a };
|
||||
y = {};
|
||||
|
||||
y;
|
||||
y = x;
|
||||
|
||||
mutate(y);
|
||||
|
||||
-1
@@ -31,7 +31,6 @@ function bar(a) {
|
||||
const x = [a];
|
||||
y = {};
|
||||
|
||||
y;
|
||||
y = x[0][1];
|
||||
$[0] = a;
|
||||
$[1] = y;
|
||||
|
||||
-2
@@ -37,8 +37,6 @@ function bar(a, b) {
|
||||
let t;
|
||||
t = {};
|
||||
|
||||
y;
|
||||
t;
|
||||
y = x[0][1];
|
||||
t = x[1][0];
|
||||
$[0] = a;
|
||||
|
||||
-1
@@ -31,7 +31,6 @@ function bar(a) {
|
||||
const x = [a];
|
||||
y = {};
|
||||
|
||||
y;
|
||||
y = x[0].a[1];
|
||||
$[0] = a;
|
||||
$[1] = y;
|
||||
|
||||
-1
@@ -30,7 +30,6 @@ function bar(a) {
|
||||
const x = [a];
|
||||
y = {};
|
||||
|
||||
y;
|
||||
y = x[0];
|
||||
$[0] = a;
|
||||
$[1] = y;
|
||||
|
||||
-1
@@ -25,7 +25,6 @@ function component(a) {
|
||||
const x = { a };
|
||||
y = 1;
|
||||
|
||||
y;
|
||||
y = x;
|
||||
|
||||
mutate(y);
|
||||
|
||||
+1
-2
@@ -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);
|
||||
|
||||
+5
-2
@@ -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} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+4
-3
@@ -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,
|
||||
|
||||
+5
-4
@@ -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);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+1
-2
@@ -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;
|
||||
|
||||
+7
-3
@@ -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 = {
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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 |
|
||||
```
|
||||
|
||||
+5
-2
@@ -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,
|
||||
|
||||
+5
-2
@@ -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,
|
||||
|
||||
-1
@@ -26,7 +26,6 @@ function f(a) {
|
||||
const $ = _c(4);
|
||||
let x;
|
||||
if ($[0] !== a) {
|
||||
x;
|
||||
x = { a };
|
||||
$[0] = a;
|
||||
$[1] = x;
|
||||
|
||||
-1
@@ -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 {
|
||||
|
||||
+4
-1
@@ -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,
|
||||
|
||||
-1
@@ -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;
|
||||
|
||||
+12
-17
@@ -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;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
-1
@@ -85,7 +85,6 @@ function Inner(props) {
|
||||
input = use(FooContext);
|
||||
}
|
||||
|
||||
input;
|
||||
input;
|
||||
let t0;
|
||||
const t1 = input;
|
||||
|
||||
Reference in New Issue
Block a user