mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
First cut of inferring effect deps
This commit is contained in:
@@ -36,6 +36,7 @@ import {
|
||||
inferReactivePlaces,
|
||||
inferReferenceEffects,
|
||||
inlineImmediatelyInvokedFunctionExpressions,
|
||||
inferEffectDependencies,
|
||||
} from '../Inference';
|
||||
import {
|
||||
constantPropagation,
|
||||
@@ -353,6 +354,10 @@ function* runWithEnvironment(
|
||||
name: 'PropagateScopeDependenciesHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
if (env.config.EXPERIMENTAL_inferEffectDependencies) {
|
||||
inferEffectDependencies(env, hir);
|
||||
}
|
||||
|
||||
if (env.config.inlineJsxTransform) {
|
||||
inlineJsxTransform(hir, env.config.inlineJsxTransform);
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import { ArrayExpression, Effect, Environment, FunctionExpression, GeneratedSource, HIRFunction, IdentifierId, Instruction, isUseEffectHookType, makeInstructionId } from "../HIR";
|
||||
import { createTemporaryPlace } from "../HIR/HIRBuilder";
|
||||
|
||||
export function inferEffectDependencies(
|
||||
env: Environment,
|
||||
fn: HIRFunction,
|
||||
): void {
|
||||
const fnExpressions = new Map<IdentifierId, FunctionExpression>();
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
const {value, lvalue} = instr;
|
||||
if (
|
||||
value.kind === 'FunctionExpression'
|
||||
) {
|
||||
fnExpressions.set(lvalue.identifier.id, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
let newInstructions = [...block.instructions];
|
||||
let addedInstrs = 0;
|
||||
for (const [idx, instr] of block.instructions.entries()) {
|
||||
const {value} = instr;
|
||||
|
||||
/*
|
||||
* This check is not final. Right now we only look for useEffects without a dependency array.
|
||||
* This is likely not how we will ship this feature, but it is good enough for us to make progress
|
||||
* on the implementation and test it.
|
||||
*/
|
||||
if (
|
||||
value.kind === 'CallExpression' &&
|
||||
isUseEffectHookType(value.callee.identifier) &&
|
||||
value.args[0].kind === 'Identifier' &&
|
||||
value.args.length === 1
|
||||
) {
|
||||
const fnExpr = fnExpressions.get(value.args[0].identifier.id);
|
||||
if (fnExpr != null) {
|
||||
const deps: ArrayExpression = {
|
||||
kind: "ArrayExpression",
|
||||
elements: [...fnExpr.loweredFunc.dependencies],
|
||||
loc: GeneratedSource
|
||||
};
|
||||
const depsPlace = createTemporaryPlace(env, GeneratedSource);
|
||||
depsPlace.effect = Effect.Read;
|
||||
const newInstruction: Instruction = {
|
||||
id: makeInstructionId(0),
|
||||
loc: GeneratedSource,
|
||||
lvalue: depsPlace,
|
||||
value: deps,
|
||||
};
|
||||
newInstructions.splice(idx + addedInstrs, 0, newInstruction);
|
||||
addedInstrs++;
|
||||
value.args[1] = depsPlace;
|
||||
}
|
||||
}
|
||||
}
|
||||
block.instructions = newInstructions;
|
||||
}
|
||||
}
|
||||
@@ -11,3 +11,4 @@ export {inferMutableRanges} from './InferMutableRanges';
|
||||
export {inferReactivePlaces} from './InferReactivePlaces';
|
||||
export {default as inferReferenceEffects} from './InferReferenceEffects';
|
||||
export {inlineImmediatelyInvokedFunctionExpressions} from './InlineImmediatelyInvokedFunctionExpressions';
|
||||
export {inferEffectDependencies} from './InferEffectDependencies';
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies
|
||||
const nonreactive = 0;
|
||||
|
||||
function Component({foo, bar}) {
|
||||
useEffect(() => {
|
||||
console.log(foo);
|
||||
console.log(bar);
|
||||
console.log(nonreactive);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log(foo);
|
||||
console.log(bar?.baz);
|
||||
console.log(bar.qux);
|
||||
});
|
||||
|
||||
function f() {
|
||||
console.log(foo);
|
||||
}
|
||||
|
||||
useEffect(f);
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
|
||||
const nonreactive = 0;
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(8);
|
||||
const { foo, bar } = t0;
|
||||
let t1;
|
||||
if ($[0] !== foo || $[1] !== bar) {
|
||||
t1 = () => {
|
||||
console.log(foo);
|
||||
console.log(bar);
|
||||
console.log(nonreactive);
|
||||
};
|
||||
$[0] = foo;
|
||||
$[1] = bar;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
useEffect(t1, [foo, bar]);
|
||||
let t2;
|
||||
if ($[3] !== foo || $[4] !== bar) {
|
||||
t2 = () => {
|
||||
console.log(foo);
|
||||
console.log(bar?.baz);
|
||||
console.log(bar.qux);
|
||||
};
|
||||
$[3] = foo;
|
||||
$[4] = bar;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
useEffect(t2, [foo, bar, bar.qux]);
|
||||
let t3;
|
||||
if ($[6] !== foo) {
|
||||
t3 = function f() {
|
||||
console.log(foo);
|
||||
};
|
||||
$[6] = foo;
|
||||
$[7] = t3;
|
||||
} else {
|
||||
t3 = $[7];
|
||||
}
|
||||
const f = t3;
|
||||
|
||||
useEffect(f);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// @inferEffectDependencies
|
||||
const nonreactive = 0;
|
||||
|
||||
function Component({foo, bar}) {
|
||||
useEffect(() => {
|
||||
console.log(foo);
|
||||
console.log(bar);
|
||||
console.log(nonreactive);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log(foo);
|
||||
console.log(bar?.baz);
|
||||
console.log(bar.qux);
|
||||
});
|
||||
|
||||
function f() {
|
||||
console.log(foo);
|
||||
}
|
||||
|
||||
// No inferred dep array, the argument is not a lambda
|
||||
useEffect(f);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user