mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Prune scopes wo own outputs
#1507 Ensured that declarations of reactive scopes were propagated to parent reactive scopes as necessary to ensure that those declarations would be available at the appropriate block scope. This meant that some scopes that were previously pruned would no longer be pruned. Specifically, an outer scope wo any declarations, but which contained a nested scope _with_ a propagated declaration, would now end up with non-empty declarations and not be pruned. This PR changes to track the declaring scope of each declaration, so we still prune scopes that don't have any of their own declarations.
This commit is contained in:
@@ -827,10 +827,15 @@ export type ReactiveScope = {
|
||||
id: ScopeId;
|
||||
range: MutableRange;
|
||||
dependencies: Set<ReactiveScopeDependency>;
|
||||
declarations: Map<IdentifierId, Identifier>;
|
||||
declarations: Map<IdentifierId, ReactiveScopeDeclaration>;
|
||||
reassignments: Set<Identifier>;
|
||||
};
|
||||
|
||||
export type ReactiveScopeDeclaration = {
|
||||
identifier: Identifier;
|
||||
scope: ReactiveScope; // the scope in which the variable was originally declared
|
||||
};
|
||||
|
||||
export type ReactiveScopeDependency = {
|
||||
identifier: Identifier;
|
||||
path: Array<string>;
|
||||
|
||||
@@ -191,20 +191,20 @@ function codegenReactiveScope(
|
||||
);
|
||||
}
|
||||
let firstOutputIndex: number | null = null;
|
||||
for (const [, declaration] of scope.declarations) {
|
||||
for (const [, { identifier }] of scope.declarations) {
|
||||
const index = cx.nextCacheIndex;
|
||||
if (firstOutputIndex === null) {
|
||||
firstOutputIndex = index;
|
||||
}
|
||||
|
||||
invariant(
|
||||
declaration.name != null,
|
||||
identifier.name != null,
|
||||
"Expected identifier '@%s' to be named",
|
||||
declaration.id
|
||||
identifier.id
|
||||
);
|
||||
|
||||
const name = convertIdentifier(declaration);
|
||||
if (!cx.hasDeclared(declaration)) {
|
||||
const name = convertIdentifier(identifier);
|
||||
if (!cx.hasDeclared(identifier)) {
|
||||
statements.push(
|
||||
t.variableDeclaration("let", [t.variableDeclarator(name)])
|
||||
);
|
||||
@@ -227,7 +227,7 @@ function codegenReactiveScope(
|
||||
)
|
||||
)
|
||||
);
|
||||
cx.declare(declaration);
|
||||
cx.declare(identifier);
|
||||
}
|
||||
for (const reassignment of scope.reassignments) {
|
||||
const index = cx.nextCacheIndex;
|
||||
|
||||
@@ -47,7 +47,9 @@ export function printReactiveBlock(
|
||||
}] dependencies=[${Array.from(block.scope.dependencies)
|
||||
.map((dep) => printDependency(dep))
|
||||
.join(", ")}] declarations=[${Array.from(block.scope.declarations)
|
||||
.map(([, decl]) => printIdentifier(decl))
|
||||
.map(([, decl]) =>
|
||||
printIdentifier({ ...decl.identifier, scope: decl.scope })
|
||||
)
|
||||
.join(", ")}] reassignments=[${Array.from(block.scope.reassignments).map(
|
||||
(reassign) => printIdentifier(reassign)
|
||||
)}] {`
|
||||
|
||||
@@ -29,9 +29,9 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
|
||||
// value.
|
||||
// Many of our current test fixtures do not return a value, so
|
||||
// it is better for now to promote (and memoize) every output.
|
||||
for (const [, identifier] of block.scope.declarations) {
|
||||
if (identifier.name == null) {
|
||||
identifier.name = `t${state.nextId++}`;
|
||||
for (const [, declaration] of block.scope.declarations) {
|
||||
if (declaration.identifier.name == null) {
|
||||
declaration.identifier.name = `t${state.nextId++}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,13 +317,16 @@ class Context {
|
||||
const originalDeclaration = this.#declarations.get(
|
||||
maybeDependency.identifier.id
|
||||
);
|
||||
if (originalDeclaration !== undefined) {
|
||||
if (
|
||||
originalDeclaration !== undefined &&
|
||||
originalDeclaration.scope.value !== null
|
||||
) {
|
||||
originalDeclaration.scope.each((scope) => {
|
||||
if (!this.#isScopeActive(scope)) {
|
||||
scope.declarations.set(
|
||||
maybeDependency.identifier.id,
|
||||
maybeDependency.identifier
|
||||
);
|
||||
scope.declarations.set(maybeDependency.identifier.id, {
|
||||
identifier: maybeDependency.identifier,
|
||||
scope: originalDeclaration.scope.value!, // checked above
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ class Visitor extends ReactiveFunctionVisitor<State> {
|
||||
if (scope.scope.dependencies.size === 0) {
|
||||
// If a scope has no dependencies, then its declarations are all non-reactive
|
||||
for (const [, declaration] of scope.scope.declarations) {
|
||||
state.delete(declaration.id);
|
||||
state.delete(declaration.identifier.id);
|
||||
}
|
||||
} else {
|
||||
// otherwise, all the scope's declarations are reactive
|
||||
for (const [, declaration] of scope.scope.declarations) {
|
||||
state.add(declaration.id);
|
||||
state.add(declaration.identifier.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,11 @@ class Transform extends ReactiveFunctionTransform<void> {
|
||||
): Transformed<ReactiveStatement> {
|
||||
this.visitScope(scopeBlock, state);
|
||||
if (
|
||||
scopeBlock.scope.declarations.size === 0 &&
|
||||
scopeBlock.scope.reassignments.size === 0
|
||||
scopeBlock.scope.reassignments.size === 0 &&
|
||||
(scopeBlock.scope.declarations.size === 0 ||
|
||||
// Can prune scopes where all declarations bubbled up from inner
|
||||
// scopes
|
||||
!hasOwnDeclaration(scopeBlock))
|
||||
) {
|
||||
return { kind: "replace-many", value: scopeBlock.instructions };
|
||||
} else {
|
||||
@@ -39,3 +42,16 @@ class Transform extends ReactiveFunctionTransform<void> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the scope block declare any values of its own? This can return
|
||||
* false if all the block's declarations are propagated from nested scopes.
|
||||
*/
|
||||
function hasOwnDeclaration(block: ReactiveScopeBlock): boolean {
|
||||
for (const declaration of block.scope.declarations.values()) {
|
||||
if (declaration.scope.id === block.scope.id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -25,36 +25,25 @@ function foo(x, y, z) {
|
||||
```javascript
|
||||
import * as React from "react";
|
||||
function foo(x, y, z) {
|
||||
const $ = React.unstable_useMemoCache(7);
|
||||
const c_0 = $[0] !== z;
|
||||
const c_1 = $[1] !== x;
|
||||
const c_2 = $[2] !== y;
|
||||
const $ = React.unstable_useMemoCache(3);
|
||||
const items = [z];
|
||||
items.push(x);
|
||||
const c_0 = $[0] !== x;
|
||||
const c_1 = $[1] !== y;
|
||||
let items2;
|
||||
if (c_0 || c_1 || c_2) {
|
||||
const items = [z];
|
||||
items.push(x);
|
||||
const c_4 = $[4] !== x;
|
||||
const c_5 = $[5] !== y;
|
||||
if (c_4 || c_5) {
|
||||
items2 = [];
|
||||
if (x) {
|
||||
items2.push(y);
|
||||
}
|
||||
$[4] = x;
|
||||
$[5] = y;
|
||||
$[6] = items2;
|
||||
} else {
|
||||
items2 = $[6];
|
||||
if (c_0 || c_1) {
|
||||
items2 = [];
|
||||
if (x) {
|
||||
items2.push(y);
|
||||
}
|
||||
if (y) {
|
||||
items.push(x);
|
||||
}
|
||||
$[0] = z;
|
||||
$[1] = x;
|
||||
$[2] = y;
|
||||
$[3] = items2;
|
||||
$[0] = x;
|
||||
$[1] = y;
|
||||
$[2] = items2;
|
||||
} else {
|
||||
items2 = $[3];
|
||||
items2 = $[2];
|
||||
}
|
||||
if (y) {
|
||||
items.push(x);
|
||||
}
|
||||
return items2;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user