mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[λ] Run analyseFunctions on inner func exprs
Previously we would skip out on calling analyseFunctions on inner function exprs so we missed out on catching aliased mutation in a different scope.
This commit is contained in:
@@ -440,17 +440,7 @@ export type InstructionData =
|
||||
| { kind: "ComputedStore"; object: Place; property: Place; value: Place }
|
||||
// load `object[index]` - like PropertyLoad but with a dynamic property
|
||||
| { kind: "ComputedLoad"; object: Place; property: Place }
|
||||
| {
|
||||
kind: "FunctionExpression";
|
||||
name: string | null;
|
||||
params: Array<string>;
|
||||
dependencies: Array<Place>;
|
||||
// TODO(gsn): Remove this mutatedDeps array and use dependencies as single
|
||||
// source of truth.
|
||||
mutatedDeps: Array<Place>;
|
||||
loweredFunc: HIRFunction;
|
||||
expr: t.ArrowFunctionExpression | t.FunctionExpression;
|
||||
}
|
||||
| FunctionExpression
|
||||
| {
|
||||
kind: "TaggedTemplateExpression";
|
||||
tag: Place;
|
||||
@@ -471,6 +461,17 @@ export type JsxAttribute =
|
||||
| { kind: "JsxSpreadAttribute"; argument: Place }
|
||||
| { kind: "JsxAttribute"; name: string; place: Place };
|
||||
|
||||
export type FunctionExpression = {
|
||||
kind: "FunctionExpression";
|
||||
name: string | null;
|
||||
params: Array<string>;
|
||||
dependencies: Array<Place>;
|
||||
// TODO(gsn): Remove this mutatedDeps array and use dependencies as single
|
||||
// source of truth.
|
||||
mutatedDeps: Array<Place>;
|
||||
loweredFunc: HIRFunction;
|
||||
expr: t.ArrowFunctionExpression | t.FunctionExpression;
|
||||
};
|
||||
/**
|
||||
* A place where data may be read from / written to:
|
||||
* - a variable (identifier)
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { HIRFunction, Identifier, mergeConsecutiveBlocks, Place } from "../HIR";
|
||||
import {
|
||||
HIRFunction,
|
||||
FunctionExpression,
|
||||
Identifier,
|
||||
mergeConsecutiveBlocks,
|
||||
Place,
|
||||
} from "../HIR";
|
||||
import { constantPropagation } from "../Optimization";
|
||||
import { eliminateRedundantPhi, enterSSA } from "../SSA";
|
||||
import { inferTypes } from "../TypeInference";
|
||||
@@ -30,18 +36,15 @@ function declareProperty(
|
||||
properties.set(lvalue.identifier, nextDependency);
|
||||
}
|
||||
|
||||
export default function (func: HIRFunction) {
|
||||
export default function analyseFunctions(func: HIRFunction) {
|
||||
const properties: Map<Identifier, Dependency> = new Map();
|
||||
|
||||
for (const [_, block] of func.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
switch (instr.value.kind) {
|
||||
case "FunctionExpression": {
|
||||
instr.value.mutatedDeps = buildMutatedDeps(
|
||||
analyzeMutatedPlaces(instr.value.loweredFunc),
|
||||
instr.value.dependencies,
|
||||
properties
|
||||
);
|
||||
lower(instr.value.loweredFunc);
|
||||
infer(instr.value, properties);
|
||||
break;
|
||||
}
|
||||
case "PropertyLoad": {
|
||||
@@ -57,6 +60,37 @@ export default function (func: HIRFunction) {
|
||||
}
|
||||
}
|
||||
|
||||
function lower(func: HIRFunction) {
|
||||
mergeConsecutiveBlocks(func);
|
||||
enterSSA(func);
|
||||
eliminateRedundantPhi(func);
|
||||
constantPropagation(func);
|
||||
inferTypes(func);
|
||||
analyseFunctions(func);
|
||||
inferReferenceEffects(func);
|
||||
inferMutableRanges(func);
|
||||
logHIRFunction("AnalyseFunction (inner)", func);
|
||||
}
|
||||
|
||||
function infer(
|
||||
value: FunctionExpression,
|
||||
properties: Map<Identifier, Dependency>
|
||||
) {
|
||||
const func = value.loweredFunc;
|
||||
const mutations: Array<Place> = func.context.filter((dep) =>
|
||||
isMutated(dep.identifier)
|
||||
);
|
||||
value.mutatedDeps = buildMutatedDeps(
|
||||
mutations,
|
||||
value.dependencies,
|
||||
properties
|
||||
);
|
||||
}
|
||||
|
||||
function isMutated(id: Identifier) {
|
||||
return id.mutableRange.end - id.mutableRange.start > 1;
|
||||
}
|
||||
|
||||
function buildMutatedDeps(
|
||||
mutations: Place[],
|
||||
capturedDeps: Place[],
|
||||
@@ -89,33 +123,3 @@ function buildMutatedDeps(
|
||||
|
||||
return mutatedDeps;
|
||||
}
|
||||
|
||||
function analyzeMutatedPlaces(func: HIRFunction): Array<Place> {
|
||||
mergeConsecutiveBlocks(func);
|
||||
enterSSA(func);
|
||||
eliminateRedundantPhi(func);
|
||||
constantPropagation(func);
|
||||
inferTypes(func);
|
||||
inferReferenceEffects(func);
|
||||
inferMutableRanges(func);
|
||||
logHIRFunction("AnalyseFunction (inner)", func);
|
||||
|
||||
const mutations: Array<Place> = [];
|
||||
for (const [_, block] of func.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
if (
|
||||
instr.value.kind === "FunctionExpression" &&
|
||||
instr.value.loweredFunc !== null
|
||||
) {
|
||||
mutations.push(...analyzeMutatedPlaces(instr.value.loweredFunc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutations.push(...func.context.filter((dep) => isMutated(dep.identifier)));
|
||||
return mutations;
|
||||
}
|
||||
|
||||
function isMutated(id: Identifier) {
|
||||
return id.mutableRange.end - id.mutableRange.start > 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function component(a) {
|
||||
let z = { a };
|
||||
(function () {
|
||||
(function () {
|
||||
z.b = 1;
|
||||
})();
|
||||
})();
|
||||
return z;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function component(a) {
|
||||
const $ = React.useMemoCache();
|
||||
const c_0 = $[0] !== a;
|
||||
let z;
|
||||
if (c_0) {
|
||||
z = { a: a };
|
||||
$[0] = a;
|
||||
$[1] = z;
|
||||
} else {
|
||||
z = $[1];
|
||||
}
|
||||
(function () {
|
||||
(function () {
|
||||
z.b = 1;
|
||||
})();
|
||||
})();
|
||||
return z;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
function component(a) {
|
||||
let z = { a };
|
||||
(function () {
|
||||
(function () {
|
||||
z.b = 1;
|
||||
})();
|
||||
})();
|
||||
return z;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function component(a) {
|
||||
let x = { a };
|
||||
(function () {
|
||||
let q = x;
|
||||
(function () {
|
||||
q.b = 1;
|
||||
})();
|
||||
})();
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function component(a) {
|
||||
const $ = React.useMemoCache();
|
||||
const c_0 = $[0] !== a;
|
||||
let x;
|
||||
if (c_0) {
|
||||
x = { a: a };
|
||||
(function () {
|
||||
let q = x;
|
||||
(function () {
|
||||
q.b = 1;
|
||||
})();
|
||||
})();
|
||||
$[0] = a;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
function component(a) {
|
||||
let x = { a };
|
||||
(function () {
|
||||
let q = x;
|
||||
(function () {
|
||||
q.b = 1;
|
||||
})();
|
||||
})();
|
||||
|
||||
return x;
|
||||
}
|
||||
Reference in New Issue
Block a user