mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[babel][contextvar] Patch context identifier babel logic; only use referenced
identifiers
---
A few fixes for finding context identifiers:
Previously, we counted every babel identifier as a reference. This is
problematic because babel counts every string symbol as an identifier.
```js
print(x); // x is an identifier as expected
obj.x // x is.. also an identifier here
{x: 2} // x is also an identifier here
```
This PR adds a check for `isReferencedIdentifier`. Note that only non-lval
references pass this check
```js
print(x); // isReferencedIdentifier(x) -> true
obj.x // isReferencedIdentifier(x) -> false
{x: 2} // isReferencedIdentifier(x) -> false
x = 2 // isReferencedIdentifier(x) -> false
```
Which brings us to change #2.
Previously, we counted assignments as references due to the identifier visiting
+ checking logic. The logic was roughly the following (from #1691)
```js
contextVars = intersection(reassigned, referencedByInnerFn);
```
Now that assignments (lvals) and references (rvals) are tracked separately, the
equivalent logic is this. Note that assignment to a context variable does not
need to be modeled as a read (`console.log(x = 5)` always will evaluates and
prints 5, regardless of the previous value of x).
```
contextVars = union(reassignedByInnerFn, intersection(reassigned,
referencedByInnerFn))
```
---
Note that variables that are never read do not need to be modeled as context
variables, but this is unlikely to be a common pattern.
```js
function fn() {
let x = 2;
const inner = () => {
x = 3;
}
}
```
This commit is contained in:
@@ -8,9 +8,20 @@
|
||||
import type { NodePath } from "@babel/traverse";
|
||||
import type * as t from "@babel/types";
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import { Set_union } from "../Utils/utils";
|
||||
import { getOrInsertDefault } from "../Utils/utils";
|
||||
import { GeneratedSource } from "./HIR";
|
||||
|
||||
type IdentifierInfo = {
|
||||
reassigned: boolean;
|
||||
reassignedByInnerFn: boolean;
|
||||
referencedByInnerFn: boolean;
|
||||
};
|
||||
const DEFAULT_IDENTIFIER_INFO: IdentifierInfo = {
|
||||
reassigned: false,
|
||||
reassignedByInnerFn: false,
|
||||
referencedByInnerFn: false,
|
||||
};
|
||||
|
||||
type BabelFunction =
|
||||
| NodePath<t.FunctionDeclaration>
|
||||
| NodePath<t.FunctionExpression>
|
||||
@@ -18,8 +29,7 @@ type BabelFunction =
|
||||
| NodePath<t.ObjectMethod>;
|
||||
type FindContextIdentifierState = {
|
||||
currentFn: Array<BabelFunction>;
|
||||
reassigned: Set<t.Identifier>;
|
||||
referenced: Set<t.Identifier>;
|
||||
identifiers: Map<t.Identifier, IdentifierInfo>;
|
||||
};
|
||||
|
||||
const withFunctionScope = {
|
||||
@@ -39,8 +49,7 @@ export function findContextIdentifiers(
|
||||
): Set<t.Identifier> {
|
||||
const state: FindContextIdentifierState = {
|
||||
currentFn: [],
|
||||
reassigned: new Set(),
|
||||
referenced: new Set(),
|
||||
identifiers: new Map(),
|
||||
};
|
||||
|
||||
func.traverse<FindContextIdentifierState>(
|
||||
@@ -54,38 +63,59 @@ export function findContextIdentifiers(
|
||||
state: FindContextIdentifierState
|
||||
): void {
|
||||
const left = path.get("left");
|
||||
handleAssignment(state.reassigned, left);
|
||||
const currentFn = state.currentFn.at(-1) ?? null;
|
||||
handleAssignment(currentFn, state.identifiers, left);
|
||||
},
|
||||
Identifier(
|
||||
path: NodePath<t.Identifier>,
|
||||
state: FindContextIdentifierState
|
||||
): void {
|
||||
const currentFn = state.currentFn.at(-1);
|
||||
if (currentFn !== undefined)
|
||||
handleIdentifier(currentFn, state.referenced, path);
|
||||
const currentFn = state.currentFn.at(-1) ?? null;
|
||||
if (path.isReferencedIdentifier()) {
|
||||
handleIdentifier(currentFn, state.identifiers, path);
|
||||
}
|
||||
},
|
||||
},
|
||||
state
|
||||
);
|
||||
return Set_union(state.reassigned, state.referenced);
|
||||
|
||||
const result = new Set<t.Identifier>();
|
||||
for (const [id, info] of state.identifiers.entries()) {
|
||||
if (info.reassignedByInnerFn) {
|
||||
result.add(id);
|
||||
} else if (info.reassigned && info.referencedByInnerFn) {
|
||||
result.add(id);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function handleIdentifier(
|
||||
currentFn: BabelFunction,
|
||||
referenced: Set<t.Identifier>,
|
||||
currentFn: BabelFunction | null,
|
||||
identifiers: Map<t.Identifier, IdentifierInfo>,
|
||||
path: NodePath<t.Identifier>
|
||||
): void {
|
||||
const name = path.node.name;
|
||||
const binding = path.scope.getBinding(name);
|
||||
const bindingAboveLambdaScope = currentFn.scope.parent.getBinding(name);
|
||||
if (binding == null) {
|
||||
return;
|
||||
}
|
||||
const identifier = getOrInsertDefault(identifiers, binding.identifier, {
|
||||
...DEFAULT_IDENTIFIER_INFO,
|
||||
});
|
||||
|
||||
if (binding != null && binding === bindingAboveLambdaScope) {
|
||||
referenced.add(binding.identifier);
|
||||
if (currentFn != null) {
|
||||
const bindingAboveLambdaScope = currentFn.scope.parent.getBinding(name);
|
||||
|
||||
if (binding === bindingAboveLambdaScope) {
|
||||
identifier.referencedByInnerFn = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleAssignment(
|
||||
reassigned: Set<t.Identifier>,
|
||||
currentFn: BabelFunction | null,
|
||||
identifiers: Map<t.Identifier, IdentifierInfo>,
|
||||
lvalPath: NodePath<t.LVal>
|
||||
): void {
|
||||
/*
|
||||
@@ -98,8 +128,20 @@ function handleAssignment(
|
||||
const path = lvalPath as NodePath<t.Identifier>;
|
||||
const name = path.node.name;
|
||||
const binding = path.scope.getBinding(name);
|
||||
if (binding != null) {
|
||||
reassigned.add(binding.identifier);
|
||||
if (binding == null) {
|
||||
break;
|
||||
}
|
||||
const state = getOrInsertDefault(identifiers, binding.identifier, {
|
||||
...DEFAULT_IDENTIFIER_INFO,
|
||||
});
|
||||
state.reassigned = true;
|
||||
|
||||
if (currentFn != null) {
|
||||
const bindingAboveLambdaScope = currentFn.scope.parent.getBinding(name);
|
||||
|
||||
if (binding === bindingAboveLambdaScope) {
|
||||
state.reassignedByInnerFn = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -107,7 +149,7 @@ function handleAssignment(
|
||||
const path = lvalPath as NodePath<t.ArrayPattern>;
|
||||
for (const element of path.get("elements")) {
|
||||
if (nonNull(element)) {
|
||||
handleAssignment(reassigned, element);
|
||||
handleAssignment(currentFn, identifiers, element);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -123,7 +165,7 @@ function handleAssignment(
|
||||
loc: valuePath.node.loc ?? GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
handleAssignment(reassigned, valuePath);
|
||||
handleAssignment(currentFn, identifiers, valuePath);
|
||||
} else {
|
||||
CompilerError.invariant(property.isRestElement(), {
|
||||
reason: `[FindContextIdentifiers] Invalid assumptions for babel types.`,
|
||||
@@ -131,7 +173,7 @@ function handleAssignment(
|
||||
loc: property.node.loc ?? GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
handleAssignment(reassigned, property);
|
||||
handleAssignment(currentFn, identifiers, property);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -139,12 +181,12 @@ function handleAssignment(
|
||||
case "AssignmentPattern": {
|
||||
const path = lvalPath as NodePath<t.AssignmentPattern>;
|
||||
const left = path.get("left");
|
||||
handleAssignment(reassigned, left);
|
||||
handleAssignment(currentFn, identifiers, left);
|
||||
break;
|
||||
}
|
||||
case "RestElement": {
|
||||
const path = lvalPath as NodePath<t.RestElement>;
|
||||
handleAssignment(reassigned, path.get("argument"));
|
||||
handleAssignment(currentFn, identifiers, path.get("argument"));
|
||||
break;
|
||||
}
|
||||
case "MemberExpression": {
|
||||
|
||||
+11
-5
@@ -17,8 +17,10 @@ function bar(a, b) {
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: bar,
|
||||
params: ["TodoAdd"],
|
||||
isComponent: "TodoAdd",
|
||||
params: [
|
||||
[1, 2],
|
||||
[2, 3],
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
@@ -52,9 +54,13 @@ function bar(a, b) {
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: bar,
|
||||
params: ["TodoAdd"],
|
||||
isComponent: "TodoAdd",
|
||||
params: [
|
||||
[1, 2],
|
||||
[2, 3],
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Eval output
|
||||
(kind: ok) 2
|
||||
+4
-2
@@ -13,6 +13,8 @@ function bar(a, b) {
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: bar,
|
||||
params: ["TodoAdd"],
|
||||
isComponent: "TodoAdd",
|
||||
params: [
|
||||
[1, 2],
|
||||
[2, 3],
|
||||
],
|
||||
};
|
||||
|
||||
+19
-3
@@ -2,22 +2,31 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { makeArray } from "shared-runtime";
|
||||
|
||||
function Component() {
|
||||
let x,
|
||||
y = (x = {});
|
||||
const foo = () => {
|
||||
x = getObject();
|
||||
x = makeArray();
|
||||
};
|
||||
foo();
|
||||
return [y, x];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
import { makeArray } from "shared-runtime";
|
||||
|
||||
function Component() {
|
||||
const $ = useMemoCache(3);
|
||||
let x;
|
||||
@@ -26,7 +35,7 @@ function Component() {
|
||||
y = x = {};
|
||||
|
||||
const foo = () => {
|
||||
x = getObject();
|
||||
x = makeArray();
|
||||
};
|
||||
|
||||
foo();
|
||||
@@ -46,5 +55,12 @@ function Component() {
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [{},[]]
|
||||
+8
-1
@@ -1,9 +1,16 @@
|
||||
import { makeArray } from "shared-runtime";
|
||||
|
||||
function Component() {
|
||||
let x,
|
||||
y = (x = {});
|
||||
const foo = () => {
|
||||
x = getObject();
|
||||
x = makeArray();
|
||||
};
|
||||
foo();
|
||||
return [y, x];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Component() {
|
||||
let x = 2;
|
||||
const fn = () => {
|
||||
return { x: "value" };
|
||||
};
|
||||
invoke(fn);
|
||||
x = 3;
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Component() {
|
||||
const fn = () => ({ x: "value" });
|
||||
|
||||
invoke(fn);
|
||||
return 3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) 3
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Component() {
|
||||
let x = 2;
|
||||
const fn = () => {
|
||||
return { x: "value" };
|
||||
};
|
||||
invoke(fn);
|
||||
x = 3;
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
};
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { identity, invoke } from "shared-runtime";
|
||||
|
||||
function foo() {
|
||||
let x = 2;
|
||||
const fn1 = () => {
|
||||
const copy1 = (x = 3);
|
||||
return identity(copy1);
|
||||
};
|
||||
const fn2 = () => {
|
||||
const copy2 = (x = 4);
|
||||
return [invoke(fn1), copy2, identity(copy2)];
|
||||
};
|
||||
return invoke(fn2);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
import { identity, invoke } from "shared-runtime";
|
||||
|
||||
function foo() {
|
||||
const $ = useMemoCache(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
let x;
|
||||
x = 2;
|
||||
const fn1 = () => {
|
||||
const copy1 = (x = 3);
|
||||
return identity(copy1);
|
||||
};
|
||||
|
||||
const fn2 = () => {
|
||||
const copy2 = (x = 4);
|
||||
return [invoke(fn1), copy2, identity(copy2)];
|
||||
};
|
||||
|
||||
t0 = invoke(fn2);
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [3,4,4]
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { identity, invoke } from "shared-runtime";
|
||||
|
||||
function foo() {
|
||||
let x = 2;
|
||||
const fn1 = () => {
|
||||
const copy1 = (x = 3);
|
||||
return identity(copy1);
|
||||
};
|
||||
const fn2 = () => {
|
||||
const copy2 = (x = 4);
|
||||
return [invoke(fn1), copy2, identity(copy2)];
|
||||
};
|
||||
return invoke(fn2);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: foo,
|
||||
params: [],
|
||||
};
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Component({ shouldReassign }) {
|
||||
let x = null;
|
||||
const reassign = () => {
|
||||
if (shouldReassign) {
|
||||
x = 2;
|
||||
}
|
||||
};
|
||||
invoke(reassign);
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ shouldReassign: true }],
|
||||
sequentialRenders: [{ shouldReassign: false }, { shouldReassign: true }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Component(t21) {
|
||||
const $ = useMemoCache(2);
|
||||
const { shouldReassign } = t21;
|
||||
let x;
|
||||
if ($[0] !== shouldReassign) {
|
||||
x = null;
|
||||
const reassign = () => {
|
||||
if (shouldReassign) {
|
||||
x = 2;
|
||||
}
|
||||
};
|
||||
|
||||
invoke(reassign);
|
||||
$[0] = shouldReassign;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ shouldReassign: true }],
|
||||
sequentialRenders: [{ shouldReassign: false }, { shouldReassign: true }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) null
|
||||
2
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Component({ shouldReassign }) {
|
||||
let x = null;
|
||||
const reassign = () => {
|
||||
if (shouldReassign) {
|
||||
x = 2;
|
||||
}
|
||||
};
|
||||
invoke(reassign);
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ shouldReassign: true }],
|
||||
sequentialRenders: [{ shouldReassign: false }, { shouldReassign: true }],
|
||||
};
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { conditionalInvoke } from "shared-runtime";
|
||||
|
||||
// same as context-variable-reactive-explicit-control-flow.js, but make
|
||||
// the control flow implicit
|
||||
|
||||
function Component({ shouldReassign }) {
|
||||
let x = null;
|
||||
const reassign = () => {
|
||||
x = 2;
|
||||
};
|
||||
conditionalInvoke(shouldReassign, reassign);
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ shouldReassign: true }],
|
||||
sequentialRenders: [{ shouldReassign: false }, { shouldReassign: true }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
import { conditionalInvoke } from "shared-runtime";
|
||||
|
||||
// same as context-variable-reactive-explicit-control-flow.js, but make
|
||||
// the control flow implicit
|
||||
|
||||
function Component(t20) {
|
||||
const $ = useMemoCache(2);
|
||||
const { shouldReassign } = t20;
|
||||
let x;
|
||||
if ($[0] !== shouldReassign) {
|
||||
x = null;
|
||||
const reassign = () => {
|
||||
x = 2;
|
||||
};
|
||||
|
||||
conditionalInvoke(shouldReassign, reassign);
|
||||
$[0] = shouldReassign;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ shouldReassign: true }],
|
||||
sequentialRenders: [{ shouldReassign: false }, { shouldReassign: true }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) null
|
||||
2
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { conditionalInvoke } from "shared-runtime";
|
||||
|
||||
// same as context-variable-reactive-explicit-control-flow.js, but make
|
||||
// the control flow implicit
|
||||
|
||||
function Component({ shouldReassign }) {
|
||||
let x = null;
|
||||
const reassign = () => {
|
||||
x = 2;
|
||||
};
|
||||
conditionalInvoke(shouldReassign, reassign);
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ shouldReassign: true }],
|
||||
sequentialRenders: [{ shouldReassign: false }, { shouldReassign: true }],
|
||||
};
|
||||
+26
-10
@@ -2,40 +2,48 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @debug
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
let x = null;
|
||||
const onChange = (e) => {
|
||||
const callback = () => {
|
||||
console.log(x);
|
||||
};
|
||||
x = {};
|
||||
return <Foo onChange={onChange} />;
|
||||
return <Stringify callback={callback} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react"; // @debug
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(2);
|
||||
let onChange;
|
||||
let callback;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
let x;
|
||||
x = null;
|
||||
onChange = (e) => {
|
||||
callback = () => {
|
||||
console.log(x);
|
||||
};
|
||||
|
||||
x = {};
|
||||
$[0] = onChange;
|
||||
$[0] = callback;
|
||||
} else {
|
||||
onChange = $[0];
|
||||
callback = $[0];
|
||||
}
|
||||
let t0;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <Foo onChange={onChange} />;
|
||||
t0 = <Stringify callback={callback} shouldInvokeFns={true} />;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
@@ -43,5 +51,13 @@ function Component(props) {
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"callback":{"kind":"Function"},"shouldInvokeFns":true}</div>
|
||||
logs: [{}]
|
||||
+9
-3
@@ -1,9 +1,15 @@
|
||||
// @debug
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
let x = null;
|
||||
const onChange = (e) => {
|
||||
const callback = () => {
|
||||
console.log(x);
|
||||
};
|
||||
x = {};
|
||||
return <Foo onChange={onChange} />;
|
||||
return <Stringify callback={callback} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Component({ value }) {
|
||||
let x = null;
|
||||
const reassign = () => {
|
||||
x = value;
|
||||
};
|
||||
invoke(reassign);
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: 2 }],
|
||||
sequentialRenders: [{ value: 2 }, { value: 4 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Component(t20) {
|
||||
const $ = useMemoCache(2);
|
||||
const { value } = t20;
|
||||
let x;
|
||||
if ($[0] !== value) {
|
||||
x = null;
|
||||
const reassign = () => {
|
||||
x = value;
|
||||
};
|
||||
|
||||
invoke(reassign);
|
||||
$[0] = value;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: 2 }],
|
||||
sequentialRenders: [{ value: 2 }, { value: 4 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) 2
|
||||
4
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { invoke } from "shared-runtime";
|
||||
|
||||
function Component({ value }) {
|
||||
let x = null;
|
||||
const reassign = () => {
|
||||
x = value;
|
||||
};
|
||||
invoke(reassign);
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: 2 }],
|
||||
sequentialRenders: [{ value: 2 }, { value: 4 }],
|
||||
};
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import { conditionalInvoke } from "shared-runtime";
|
||||
|
||||
function Component({ doReassign1, doReassign2 }) {
|
||||
let x = {};
|
||||
const reassign1 = () => {
|
||||
x = 2;
|
||||
};
|
||||
const reassign2 = () => {
|
||||
x = 3;
|
||||
};
|
||||
conditionalInvoke(doReassign1, reassign1);
|
||||
conditionalInvoke(doReassign2, reassign2);
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ doReassign1: true, doReassign2: true }],
|
||||
sequentialRenders: [
|
||||
{ doReassign1: true, doReassign2: true },
|
||||
{ doReassign1: true, doReassign2: false },
|
||||
{ doReassign1: false, doReassign2: false },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
import { conditionalInvoke } from "shared-runtime";
|
||||
|
||||
function Component(t32) {
|
||||
const $ = useMemoCache(3);
|
||||
const { doReassign1, doReassign2 } = t32;
|
||||
let x;
|
||||
if ($[0] !== doReassign1 || $[1] !== doReassign2) {
|
||||
x = {};
|
||||
const reassign1 = () => {
|
||||
x = 2;
|
||||
};
|
||||
|
||||
const reassign2 = () => {
|
||||
x = 3;
|
||||
};
|
||||
|
||||
conditionalInvoke(doReassign1, reassign1);
|
||||
conditionalInvoke(doReassign2, reassign2);
|
||||
$[0] = doReassign1;
|
||||
$[1] = doReassign2;
|
||||
$[2] = x;
|
||||
} else {
|
||||
x = $[2];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ doReassign1: true, doReassign2: true }],
|
||||
sequentialRenders: [
|
||||
{ doReassign1: true, doReassign2: true },
|
||||
{ doReassign1: true, doReassign2: false },
|
||||
{ doReassign1: false, doReassign2: false },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) 3
|
||||
2
|
||||
{}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { conditionalInvoke } from "shared-runtime";
|
||||
|
||||
function Component({ doReassign1, doReassign2 }) {
|
||||
let x = {};
|
||||
const reassign1 = () => {
|
||||
x = 2;
|
||||
};
|
||||
const reassign2 = () => {
|
||||
x = 3;
|
||||
};
|
||||
conditionalInvoke(doReassign1, reassign1);
|
||||
conditionalInvoke(doReassign2, reassign2);
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ doReassign1: true, doReassign2: true }],
|
||||
sequentialRenders: [
|
||||
{ doReassign1: true, doReassign2: true },
|
||||
{ doReassign1: true, doReassign2: false },
|
||||
{ doReassign1: false, doReassign2: false },
|
||||
],
|
||||
};
|
||||
+5
-7
@@ -25,16 +25,14 @@ import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function component() {
|
||||
const $ = useMemoCache(1);
|
||||
let x;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
x = function x(a) {
|
||||
a.foo();
|
||||
};
|
||||
|
||||
x = {};
|
||||
$[0] = x;
|
||||
t0 = {};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
x = $[0];
|
||||
t0 = $[0];
|
||||
}
|
||||
x = t0;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -13,8 +13,7 @@ function Component(props) {
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: ["TodoAdd"],
|
||||
isComponent: "TodoAdd",
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
@@ -42,8 +41,7 @@ function Component(props) {
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: ["TodoAdd"],
|
||||
isComponent: "TodoAdd",
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
+1
-2
@@ -9,6 +9,5 @@ function Component(props) {
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: ["TodoAdd"],
|
||||
isComponent: "TodoAdd",
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
+5
-5
@@ -13,8 +13,7 @@ function Component(props) {
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: ["TodoAdd"],
|
||||
isComponent: "TodoAdd",
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
@@ -42,9 +41,10 @@ function Component(props) {
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: ["TodoAdd"],
|
||||
isComponent: "TodoAdd",
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Eval output
|
||||
(kind: ok) {}
|
||||
+1
-2
@@ -9,6 +9,5 @@ function Component(props) {
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: ["TodoAdd"],
|
||||
isComponent: "TodoAdd",
|
||||
params: [{}],
|
||||
};
|
||||
|
||||
@@ -32,7 +32,6 @@ const skipFilter = new Set([
|
||||
"capturing-func-mutate-nested",
|
||||
"capturing-func-mutate",
|
||||
"capturing-function-1",
|
||||
"capturing-function-alias-computed-load-3",
|
||||
"capturing-function-alias-computed-load",
|
||||
"capturing-function-decl",
|
||||
"capturing-function-skip-computed-path",
|
||||
@@ -123,7 +122,6 @@ const skipFilter = new Set([
|
||||
"reactive-scopes",
|
||||
"reactivity-analysis-interleaved-reactivity",
|
||||
"reassign-object-in-context",
|
||||
"reassign-primitive-in-context",
|
||||
"reassignment-separate-scopes",
|
||||
"reduce-reactive-cond-memberexpr-join",
|
||||
"reduce-reactive-uncond-deps-nonoverlap-descendant",
|
||||
@@ -191,7 +189,6 @@ const skipFilter = new Set([
|
||||
*/
|
||||
"alias-capture-in-method-receiver",
|
||||
"alias-nested-member-path-mutate",
|
||||
"chained-assignment-context-variable",
|
||||
"concise-arrow-expr",
|
||||
"const-propagation-into-function-expression-global",
|
||||
"declare-reassign-variable-in-function-declaration",
|
||||
@@ -268,7 +265,6 @@ const skipFilter = new Set([
|
||||
"computed-load-primitive-as-dependency",
|
||||
"computed-store-alias",
|
||||
"constant-propagation-into-function-expressions",
|
||||
"context-variable-reassigned-outside-of-lambda",
|
||||
"destructuring-mixed-scope-declarations-and-locals",
|
||||
"destructuring-property-inference",
|
||||
"do-while-conditional-break",
|
||||
|
||||
@@ -127,8 +127,8 @@ export function makeObject_Primitives(): StringKeyedObject {
|
||||
return { a: 0, b: "value1", c: true };
|
||||
}
|
||||
|
||||
export function makeArray<T>(value: T): Array<T> {
|
||||
return [value];
|
||||
export function makeArray<T>(...values: Array<T>): Array<T> {
|
||||
return [...values];
|
||||
}
|
||||
|
||||
export function addOne(value: number): number {
|
||||
@@ -173,6 +173,18 @@ export function invoke<T extends Array<any>, ReturnType>(
|
||||
return fn(...params);
|
||||
}
|
||||
|
||||
export function conditionalInvoke<T extends Array<any>, ReturnType>(
|
||||
shouldInvoke: boolean,
|
||||
fn: (...input: T) => ReturnType,
|
||||
...params: T
|
||||
) {
|
||||
if (shouldInvoke) {
|
||||
return fn(...params);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* React Components
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user