mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[bugfix] do not hoist computed memberpaths in lambdas
This commit is contained in:
@@ -3530,6 +3530,14 @@ 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) {
|
||||
@@ -3579,7 +3587,7 @@ function gatherCapturedDeps(
|
||||
| NodePath<t.JSXOpeningElement>
|
||||
): void {
|
||||
// Base context variable to depend on
|
||||
let baseIdentifier: NodePath<t.Identifier | t.JSXIdentifier>;
|
||||
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:
|
||||
@@ -3604,31 +3612,36 @@ function gatherCapturedDeps(
|
||||
dependency = current;
|
||||
} else if (path.isMemberExpression()) {
|
||||
// Calculate baseIdentifier
|
||||
let current: NodePath<Expression> = path;
|
||||
while (current.isMemberExpression()) {
|
||||
current = current.get("object");
|
||||
let currentId: NodePath<Expression> = path;
|
||||
while (currentId.isMemberExpression()) {
|
||||
currentId = currentId.get("object");
|
||||
}
|
||||
if (!current.isIdentifier()) {
|
||||
if (!currentId.isIdentifier()) {
|
||||
return;
|
||||
}
|
||||
baseIdentifier = current;
|
||||
baseIdentifier = currentId;
|
||||
|
||||
// Get the expression to depend on, which may involve PropertyLoads
|
||||
// for member expressions
|
||||
current =
|
||||
path.parent.type === "CallExpression" &&
|
||||
path.parent.callee === path.node
|
||||
? path.get("object")
|
||||
: path;
|
||||
while (current.isMemberExpression() && current.node.computed) {
|
||||
// computed nodes may contain side-effectful subexpressions
|
||||
current = current.get("object");
|
||||
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;
|
||||
}
|
||||
}
|
||||
invariant(
|
||||
current.isMemberExpression() || current.isIdentifier(),
|
||||
"Internal invariant broken in BuildHIR, unexpected type for capturedDep"
|
||||
);
|
||||
dependency = current;
|
||||
|
||||
dependency = currentDep;
|
||||
} else {
|
||||
baseIdentifier = path;
|
||||
dependency = path;
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Foo() {
|
||||
const x = [{ value: 0 }, { value: 1 }, { value: 2 }];
|
||||
const foo = (param: number) => {
|
||||
return x[param].value;
|
||||
};
|
||||
|
||||
return invoke(foo, 1);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPONT = {
|
||||
fn: Foo,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] Todo: EnterSSA: Expected identifier to be defined before being used. Identifier param$10 is undefined (5:5)
|
||||
```
|
||||
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { CONST_NUMBER0, invoke } from "shared-runtime";
|
||||
|
||||
function Foo() {
|
||||
const x = [{ value: 0 }, { value: 1 }, { value: 2 }];
|
||||
const param = CONST_NUMBER0;
|
||||
const foo = () => {
|
||||
return x[param].value;
|
||||
};
|
||||
|
||||
return invoke(foo);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
import { CONST_NUMBER0, invoke } from "shared-runtime";
|
||||
|
||||
function Foo() {
|
||||
const $ = useMemoCache(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const x = [{ value: 0 }, { value: 1 }, { value: 2 }];
|
||||
|
||||
const foo = () => x[CONST_NUMBER0].value;
|
||||
|
||||
t0 = invoke(foo);
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { CONST_NUMBER0, invoke } from "shared-runtime";
|
||||
|
||||
function Foo() {
|
||||
const x = [{ value: 0 }, { value: 1 }, { value: 2 }];
|
||||
const param = CONST_NUMBER0;
|
||||
const foo = () => {
|
||||
return x[param].value;
|
||||
};
|
||||
|
||||
return invoke(foo);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}],
|
||||
};
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Foo() {
|
||||
const x = [{ value: 0 }, { value: 1 }, { value: 2 }];
|
||||
const foo = (param: number) => {
|
||||
return x[param].value;
|
||||
};
|
||||
|
||||
return invoke(foo, 1);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Foo() {
|
||||
const $ = useMemoCache(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const x = [{ value: 0 }, { value: 1 }, { value: 2 }];
|
||||
const foo = (param) => x[param].value;
|
||||
|
||||
t0 = invoke(foo, 1);
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ function Foo() {
|
||||
return invoke(foo, 1);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPONT = {
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}],
|
||||
};
|
||||
@@ -99,7 +99,9 @@ export function doEval(source: string): EvaluatorResult {
|
||||
) {
|
||||
return {
|
||||
kind: "UnexpectedError",
|
||||
value: 'FIXTURE_ENTRYPOINT not exported!',
|
||||
value: 'FIXTURE_ENTRYPOINT not exported! Found {'
|
||||
+ Object.keys(exports).filter(e => e !== 'FIXTURE_ENTRYPOINT').toString()
|
||||
+ '}',
|
||||
};
|
||||
}
|
||||
const validationError = validateEntrypoint(exports.FIXTURE_ENTRYPOINT);
|
||||
|
||||
Reference in New Issue
Block a user