mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[compiler] (needs cleanup) Hoist dependencies from functions more conservatively
Summary: Test Plan: Reviewers: Subscribers: Tasks: Tags:
This commit is contained in:
+168
-26
@@ -13,11 +13,14 @@ import {
|
||||
BlockId,
|
||||
DependencyPathEntry,
|
||||
GeneratedSource,
|
||||
getHookKind,
|
||||
HIRFunction,
|
||||
Identifier,
|
||||
IdentifierId,
|
||||
InstructionId,
|
||||
InstructionValue,
|
||||
LoweredFunction,
|
||||
Place,
|
||||
PropertyLiteral,
|
||||
ReactiveScopeDependency,
|
||||
ScopeId,
|
||||
@@ -112,6 +115,9 @@ export function collectHoistablePropertyLoads(
|
||||
hoistableFromOptionals,
|
||||
registry,
|
||||
nestedFnImmutableContext: null,
|
||||
likelyInvokedFns: fn.env.config.enableTreatFunctionDepsAsConditional
|
||||
? new Set()
|
||||
: getLikelyInvokedFunctions(fn),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -127,6 +133,11 @@ type CollectHoistablePropertyLoadsContext = {
|
||||
* but are currently kept separate for readability.
|
||||
*/
|
||||
nestedFnImmutableContext: ReadonlySet<IdentifierId> | null;
|
||||
/**
|
||||
* Functions which are assumed to be eventually called (as opposed to ones which might
|
||||
* not be called, e.g. the 0th argument of Array.map)
|
||||
*/
|
||||
likelyInvokedFns: ReadonlySet<LoweredFunction>;
|
||||
};
|
||||
function collectHoistablePropertyLoadsImpl(
|
||||
fn: HIRFunction,
|
||||
@@ -338,7 +349,13 @@ function collectNonNullsInBlocks(
|
||||
context.registry.getOrCreateIdentifier(identifier),
|
||||
);
|
||||
}
|
||||
const nodes = new Map<BlockId, BlockInfo>();
|
||||
const nodes = new Map<
|
||||
BlockId,
|
||||
{
|
||||
block: BasicBlock;
|
||||
assumedNonNullObjects: Set<PropertyPathNode>;
|
||||
}
|
||||
>();
|
||||
for (const [_, block] of fn.body.blocks) {
|
||||
const assumedNonNullObjects = new Set<PropertyPathNode>(
|
||||
knownNonNullIdentifiers,
|
||||
@@ -358,32 +375,30 @@ function collectNonNullsInBlocks(
|
||||
) {
|
||||
assumedNonNullObjects.add(maybeNonNull);
|
||||
}
|
||||
if (
|
||||
(instr.value.kind === 'FunctionExpression' ||
|
||||
instr.value.kind === 'ObjectMethod') &&
|
||||
!fn.env.config.enableTreatFunctionDepsAsConditional
|
||||
) {
|
||||
if (instr.value.kind === 'FunctionExpression') {
|
||||
const innerFn = instr.value.loweredFunc;
|
||||
const innerHoistableMap = collectHoistablePropertyLoadsImpl(
|
||||
innerFn.func,
|
||||
{
|
||||
...context,
|
||||
nestedFnImmutableContext:
|
||||
context.nestedFnImmutableContext ??
|
||||
new Set(
|
||||
innerFn.func.context
|
||||
.filter(place =>
|
||||
isImmutableAtInstr(place.identifier, instr.id, context),
|
||||
)
|
||||
.map(place => place.identifier.id),
|
||||
),
|
||||
},
|
||||
);
|
||||
const innerHoistables = assertNonNull(
|
||||
innerHoistableMap.get(innerFn.func.body.entry),
|
||||
);
|
||||
for (const entry of innerHoistables.assumedNonNullObjects) {
|
||||
assumedNonNullObjects.add(entry);
|
||||
if (context.likelyInvokedFns.has(innerFn)) {
|
||||
const innerHoistableMap = collectHoistablePropertyLoadsImpl(
|
||||
innerFn.func,
|
||||
{
|
||||
...context,
|
||||
nestedFnImmutableContext:
|
||||
context.nestedFnImmutableContext ??
|
||||
new Set(
|
||||
innerFn.func.context
|
||||
.filter(place =>
|
||||
isImmutableAtInstr(place.identifier, instr.id, context),
|
||||
)
|
||||
.map(place => place.identifier.id),
|
||||
),
|
||||
},
|
||||
);
|
||||
const innerHoistables = assertNonNull(
|
||||
innerHoistableMap.get(innerFn.func.body.entry),
|
||||
);
|
||||
for (const entry of innerHoistables.assumedNonNullObjects) {
|
||||
assumedNonNullObjects.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -591,3 +606,130 @@ function reduceMaybeOptionalChains(
|
||||
}
|
||||
} while (changed);
|
||||
}
|
||||
|
||||
function getLikelyInvokedFunctions(
|
||||
fn: HIRFunction,
|
||||
temporaries: Map<
|
||||
IdentifierId,
|
||||
{fn: LoweredFunction; mayInvoke: Set<LoweredFunction>}
|
||||
> = new Map(),
|
||||
): ReadonlySet<LoweredFunction> {
|
||||
const hoistableFunctions = new Set<LoweredFunction>();
|
||||
/**
|
||||
* Step 1: Conservatively collect identifier to function expression mappings
|
||||
*/
|
||||
for (const block of fn.body.blocks.values()) {
|
||||
for (const {lvalue, value} of block.instructions) {
|
||||
/**
|
||||
* Conservatively only match function expressions which can have guaranteed ssa.
|
||||
* ObjectMethods and ObjectProperties do not.
|
||||
*/
|
||||
if (value.kind === 'FunctionExpression') {
|
||||
temporaries.set(lvalue.identifier.id, {
|
||||
fn: value.loweredFunc,
|
||||
mayInvoke: new Set(),
|
||||
});
|
||||
} else if (value.kind === 'StoreLocal') {
|
||||
const lvalue = value.lvalue.place.identifier;
|
||||
const maybeLoweredFunc = temporaries.get(value.value.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
temporaries.set(lvalue.id, maybeLoweredFunc);
|
||||
}
|
||||
} else if (value.kind === 'LoadLocal') {
|
||||
const maybeLoweredFunc = temporaries.get(value.place.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
temporaries.set(lvalue.identifier.id, maybeLoweredFunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Step 2: Forward pass to do analysis of assumed function calls. Note that
|
||||
* this is conservative and does not count indirect references through
|
||||
* containers (e.g. `return {cb: () => {...}})`).
|
||||
*/
|
||||
for (const block of fn.body.blocks.values()) {
|
||||
for (const {lvalue, value} of block.instructions) {
|
||||
if (value.kind === 'CallExpression') {
|
||||
const callee = value.callee;
|
||||
const maybeHook = getHookKind(fn.env, callee.identifier);
|
||||
const maybeLoweredFunc = temporaries.get(callee.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
// Direct calls
|
||||
hoistableFunctions.add(maybeLoweredFunc.fn);
|
||||
} else if (maybeHook != null) {
|
||||
/**
|
||||
* Assume arguments to all hooks are safe to invoke
|
||||
*/
|
||||
for (const arg of value.args.filter(
|
||||
arg => arg.kind === 'Identifier',
|
||||
) as Array<Place>) {
|
||||
const maybeLoweredFunc = temporaries.get(arg.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
hoistableFunctions.add(maybeLoweredFunc.fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (value.kind === 'JsxExpression') {
|
||||
/**
|
||||
* Assume JSX attributes and children are safe to invoke
|
||||
*/
|
||||
for (const attr of value.props) {
|
||||
if (attr.kind === 'JsxSpreadAttribute') {
|
||||
continue;
|
||||
}
|
||||
const maybeLoweredFunc = temporaries.get(attr.place.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
hoistableFunctions.add(maybeLoweredFunc.fn);
|
||||
}
|
||||
}
|
||||
for (const child of value.children ?? []) {
|
||||
const maybeLoweredFunc = temporaries.get(child.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
hoistableFunctions.add(maybeLoweredFunc.fn);
|
||||
}
|
||||
}
|
||||
} else if (value.kind === 'FunctionExpression') {
|
||||
/**
|
||||
* Recursively traverse into other function expressions which may invoke
|
||||
* or pass already declared functions to react (e.g. as JSXAttributes).
|
||||
*
|
||||
* If lambda A calls lambda B, we assume lambda B is safe to invoke if
|
||||
* lambda A is -- even if lambda B is conditionally called. (see
|
||||
* `conditional-call-chain` fixture for example).
|
||||
*/
|
||||
const loweredFunc = value.loweredFunc.func;
|
||||
const lambdasCalled = getLikelyInvokedFunctions(
|
||||
loweredFunc,
|
||||
temporaries,
|
||||
);
|
||||
const maybeLoweredFunc = temporaries.get(lvalue.identifier.id);
|
||||
if (maybeLoweredFunc != null) {
|
||||
for (const called of lambdasCalled) {
|
||||
maybeLoweredFunc.mayInvoke.add(called);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (block.terminal.kind === 'return') {
|
||||
/**
|
||||
* Assume directly returned functions are safe to call
|
||||
*/
|
||||
const maybeLoweredFunc = temporaries.get(
|
||||
block.terminal.value.identifier.id,
|
||||
);
|
||||
if (maybeLoweredFunc != null) {
|
||||
hoistableFunctions.add(maybeLoweredFunc.fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [_, {fn, mayInvoke}] of temporaries) {
|
||||
if (hoistableFunctions.has(fn)) {
|
||||
for (const called of mayInvoke) {
|
||||
hoistableFunctions.add(called);
|
||||
}
|
||||
}
|
||||
}
|
||||
return hoistableFunctions;
|
||||
}
|
||||
|
||||
+2
-2
@@ -23,11 +23,11 @@ import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
let t0;
|
||||
if ($[0] !== props.name) {
|
||||
if ($[0] !== props) {
|
||||
t0 = function () {
|
||||
return <div>{props.name}</div>;
|
||||
};
|
||||
$[0] = props.name;
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Named lambdas (e.g. cb1) may be defined in the top scope of a function and
|
||||
* used in a different lambda (getArrMap1).
|
||||
*
|
||||
* Here, we should try to determine if cb1 is actually called. In this case:
|
||||
* - getArrMap1 is assumed to be called as it's passed to JSX
|
||||
* - cb1 is not assumed to be called since it's only used as a call operand
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const cb1 = e => arr1[0].value + e.value;
|
||||
const getArrMap1 = () => arr1.map(cb1);
|
||||
const cb2 = e => arr2[0].value + e.value;
|
||||
const getArrMap2 = () => arr1.map(cb2);
|
||||
return (
|
||||
<Stringify
|
||||
getArrMap1={getArrMap1}
|
||||
getArrMap2={getArrMap2}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Named lambdas (e.g. cb1) may be defined in the top scope of a function and
|
||||
* used in a different lambda (getArrMap1).
|
||||
*
|
||||
* Here, we should try to determine if cb1 is actually called. In this case:
|
||||
* - getArrMap1 is assumed to be called as it's passed to JSX
|
||||
* - cb1 is not assumed to be called since it's only used as a call operand
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(13);
|
||||
const { arr1, arr2 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr1[0]) {
|
||||
t1 = (e) => arr1[0].value + e.value;
|
||||
$[0] = arr1[0];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb1 = t1;
|
||||
let t2;
|
||||
if ($[2] !== arr1 || $[3] !== cb1) {
|
||||
t2 = () => arr1.map(cb1);
|
||||
$[2] = arr1;
|
||||
$[3] = cb1;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
const getArrMap1 = t2;
|
||||
let t3;
|
||||
if ($[5] !== arr2) {
|
||||
t3 = (e_0) => arr2[0].value + e_0.value;
|
||||
$[5] = arr2;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
const cb2 = t3;
|
||||
let t4;
|
||||
if ($[7] !== arr1 || $[8] !== cb2) {
|
||||
t4 = () => arr1.map(cb2);
|
||||
$[7] = arr1;
|
||||
$[8] = cb2;
|
||||
$[9] = t4;
|
||||
} else {
|
||||
t4 = $[9];
|
||||
}
|
||||
const getArrMap2 = t4;
|
||||
let t5;
|
||||
if ($[10] !== getArrMap1 || $[11] !== getArrMap2) {
|
||||
t5 = (
|
||||
<Stringify
|
||||
getArrMap1={getArrMap1}
|
||||
getArrMap2={getArrMap2}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
$[10] = getArrMap1;
|
||||
$[11] = getArrMap2;
|
||||
$[12] = t5;
|
||||
} else {
|
||||
t5 = $[12];
|
||||
}
|
||||
return t5;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ arr1: [], arr2: [] }],
|
||||
sequentialRenders: [
|
||||
{ arr1: [], arr2: [] },
|
||||
{ arr1: [], arr2: null },
|
||||
{ arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"getArrMap1":{"kind":"Function","result":[]},"getArrMap2":{"kind":"Function","result":[]},"shouldInvokeFns":true}</div>
|
||||
<div>{"getArrMap1":{"kind":"Function","result":[]},"getArrMap2":{"kind":"Function","result":[]},"shouldInvokeFns":true}</div>
|
||||
<div>{"getArrMap1":{"kind":"Function","result":[2,3]},"getArrMap2":{"kind":"Function","result":[0,1]},"shouldInvokeFns":true}</div>
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Named lambdas (e.g. cb1) may be defined in the top scope of a function and
|
||||
* used in a different lambda (getArrMap1).
|
||||
*
|
||||
* Here, we should try to determine if cb1 is actually called. In this case:
|
||||
* - getArrMap1 is assumed to be called as it's passed to JSX
|
||||
* - cb1 is not assumed to be called since it's only used as a call operand
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const cb1 = e => arr1[0].value + e.value;
|
||||
const getArrMap1 = () => arr1.map(cb1);
|
||||
const cb2 = e => arr2[0].value + e.value;
|
||||
const getArrMap2 = () => arr1.map(cb2);
|
||||
return (
|
||||
<Stringify
|
||||
getArrMap1={getArrMap1}
|
||||
getArrMap2={getArrMap2}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Whether lambdas are named or passed inline shouldn't affect whether we expect
|
||||
* it to be called.
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const cb1 = e => arr1[0].value + e.value;
|
||||
const x = arr1.map(cb1);
|
||||
const cb2 = e => arr2[0].value + e.value;
|
||||
const y = arr1.map(cb2);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; /**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Whether lambdas are named or passed inline shouldn't affect whether we expect
|
||||
* it to be called.
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(13);
|
||||
const { arr1, arr2 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr1[0]) {
|
||||
t1 = (e) => arr1[0].value + e.value;
|
||||
$[0] = arr1[0];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb1 = t1;
|
||||
let t2;
|
||||
if ($[2] !== arr1 || $[3] !== cb1) {
|
||||
t2 = arr1.map(cb1);
|
||||
$[2] = arr1;
|
||||
$[3] = cb1;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
const x = t2;
|
||||
let t3;
|
||||
if ($[5] !== arr2) {
|
||||
t3 = (e_0) => arr2[0].value + e_0.value;
|
||||
$[5] = arr2;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
const cb2 = t3;
|
||||
let t4;
|
||||
if ($[7] !== arr1 || $[8] !== cb2) {
|
||||
t4 = arr1.map(cb2);
|
||||
$[7] = arr1;
|
||||
$[8] = cb2;
|
||||
$[9] = t4;
|
||||
} else {
|
||||
t4 = $[9];
|
||||
}
|
||||
const y = t4;
|
||||
let t5;
|
||||
if ($[10] !== x || $[11] !== y) {
|
||||
t5 = [x, y];
|
||||
$[10] = x;
|
||||
$[11] = y;
|
||||
$[12] = t5;
|
||||
} else {
|
||||
t5 = $[12];
|
||||
}
|
||||
return t5;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ arr1: [], arr2: [] }],
|
||||
sequentialRenders: [
|
||||
{ arr1: [], arr2: [] },
|
||||
{ arr1: [], arr2: null },
|
||||
{ arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[],[]]
|
||||
[[],[]]
|
||||
[[2,3],[0,1]]
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Whether lambdas are named or passed inline shouldn't affect whether we expect
|
||||
* it to be called.
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const cb1 = e => arr1[0].value + e.value;
|
||||
const x = arr1.map(cb1);
|
||||
const cb2 = e => arr2[0].value + e.value;
|
||||
const y = arr1.map(cb2);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Here, getVal1 has a known callsite in `cb1`, but `cb1` isn't known to be
|
||||
* called (it's only passed to array.map). In this case, we should be
|
||||
* conservative and assume that all named lambdas are conditionally called.
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const getVal1 = () => arr1[0].value;
|
||||
const cb1 = e => getVal1() + e.value;
|
||||
const x = arr1.map(cb1);
|
||||
const getVal2 = () => arr2[0].value;
|
||||
const cb2 = e => getVal2() + e.value;
|
||||
const y = arr1.map(cb2);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; /**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Here, getVal1 has a known callsite in `cb1`, but `cb1` isn't known to be
|
||||
* called (it's only passed to array.map). In this case, we should be
|
||||
* conservative and assume that all named lambdas are conditionally called.
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(17);
|
||||
const { arr1, arr2 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr1[0]) {
|
||||
t1 = () => arr1[0].value;
|
||||
$[0] = arr1[0];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const getVal1 = t1;
|
||||
let t2;
|
||||
if ($[2] !== getVal1) {
|
||||
t2 = (e) => getVal1() + e.value;
|
||||
$[2] = getVal1;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
const cb1 = t2;
|
||||
let t3;
|
||||
if ($[4] !== arr1 || $[5] !== cb1) {
|
||||
t3 = arr1.map(cb1);
|
||||
$[4] = arr1;
|
||||
$[5] = cb1;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
const x = t3;
|
||||
let t4;
|
||||
if ($[7] !== arr2) {
|
||||
t4 = () => arr2[0].value;
|
||||
$[7] = arr2;
|
||||
$[8] = t4;
|
||||
} else {
|
||||
t4 = $[8];
|
||||
}
|
||||
const getVal2 = t4;
|
||||
let t5;
|
||||
if ($[9] !== getVal2) {
|
||||
t5 = (e_0) => getVal2() + e_0.value;
|
||||
$[9] = getVal2;
|
||||
$[10] = t5;
|
||||
} else {
|
||||
t5 = $[10];
|
||||
}
|
||||
const cb2 = t5;
|
||||
let t6;
|
||||
if ($[11] !== arr1 || $[12] !== cb2) {
|
||||
t6 = arr1.map(cb2);
|
||||
$[11] = arr1;
|
||||
$[12] = cb2;
|
||||
$[13] = t6;
|
||||
} else {
|
||||
t6 = $[13];
|
||||
}
|
||||
const y = t6;
|
||||
let t7;
|
||||
if ($[14] !== x || $[15] !== y) {
|
||||
t7 = [x, y];
|
||||
$[14] = x;
|
||||
$[15] = y;
|
||||
$[16] = t7;
|
||||
} else {
|
||||
t7 = $[16];
|
||||
}
|
||||
return t7;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ arr1: [], arr2: [] }],
|
||||
sequentialRenders: [
|
||||
{ arr1: [], arr2: [] },
|
||||
{ arr1: [], arr2: null },
|
||||
{ arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[],[]]
|
||||
[[],[]]
|
||||
[[2,3],[0,1]]
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Forked from array-map-simple.js
|
||||
*
|
||||
* Here, getVal1 has a known callsite in `cb1`, but `cb1` isn't known to be
|
||||
* called (it's only passed to array.map). In this case, we should be
|
||||
* conservative and assume that all named lambdas are conditionally called.
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const getVal1 = () => arr1[0].value;
|
||||
const cb1 = e => getVal1() + e.value;
|
||||
const x = arr1.map(cb1);
|
||||
const getVal2 = () => arr2[0].value;
|
||||
const cb2 = e => getVal2() + e.value;
|
||||
const y = arr1.map(cb2);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Test that we're not hoisting property reads from lambdas that are created to
|
||||
* pass to opaque functions, which often have maybe-invoke semantics.
|
||||
*
|
||||
* In this example, we shouldn't hoist `arr[0].value` out of the lambda.
|
||||
* ```js
|
||||
* e => arr[0].value + e.value <-- created to pass to map
|
||||
* arr.map(<cb>) <-- argument only invoked if array is non-empty
|
||||
* ```
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const x = arr1.map(e => arr1[0].value + e.value);
|
||||
const y = arr1.map(e => arr2[0].value + e.value);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; /**
|
||||
* Test that we're not hoisting property reads from lambdas that are created to
|
||||
* pass to opaque functions, which often have maybe-invoke semantics.
|
||||
*
|
||||
* In this example, we shouldn't hoist `arr[0].value` out of the lambda.
|
||||
* ```js
|
||||
* e => arr[0].value + e.value <-- created to pass to map
|
||||
* arr.map(<cb>) <-- argument only invoked if array is non-empty
|
||||
* ```
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(12);
|
||||
const { arr1, arr2 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr1) {
|
||||
let t2;
|
||||
if ($[2] !== arr1[0]) {
|
||||
t2 = (e) => arr1[0].value + e.value;
|
||||
$[2] = arr1[0];
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
t1 = arr1.map(t2);
|
||||
$[0] = arr1;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const x = t1;
|
||||
let t2;
|
||||
if ($[4] !== arr1 || $[5] !== arr2) {
|
||||
let t3;
|
||||
if ($[7] !== arr2) {
|
||||
t3 = (e_0) => arr2[0].value + e_0.value;
|
||||
$[7] = arr2;
|
||||
$[8] = t3;
|
||||
} else {
|
||||
t3 = $[8];
|
||||
}
|
||||
t2 = arr1.map(t3);
|
||||
$[4] = arr1;
|
||||
$[5] = arr2;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
const y = t2;
|
||||
let t3;
|
||||
if ($[9] !== x || $[10] !== y) {
|
||||
t3 = [x, y];
|
||||
$[9] = x;
|
||||
$[10] = y;
|
||||
$[11] = t3;
|
||||
} else {
|
||||
t3 = $[11];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ arr1: [], arr2: [] }],
|
||||
sequentialRenders: [
|
||||
{ arr1: [], arr2: [] },
|
||||
{ arr1: [], arr2: null },
|
||||
{ arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[],[]]
|
||||
[[],[]]
|
||||
[[2,3],[0,1]]
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Test that we're not hoisting property reads from lambdas that are created to
|
||||
* pass to opaque functions, which often have maybe-invoke semantics.
|
||||
*
|
||||
* In this example, we shouldn't hoist `arr[0].value` out of the lambda.
|
||||
* ```js
|
||||
* e => arr[0].value + e.value <-- created to pass to map
|
||||
* arr.map(<cb>) <-- argument only invoked if array is non-empty
|
||||
* ```
|
||||
*/
|
||||
function useFoo({arr1, arr2}) {
|
||||
const x = arr1.map(e => arr1[0].value + e.value);
|
||||
const y = arr1.map(e => arr2[0].value + e.value);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useRef} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Component({a, b}) {
|
||||
const logA = () => {
|
||||
console.log(a.value);
|
||||
};
|
||||
const logB = () => {
|
||||
console.log(b.value);
|
||||
};
|
||||
const hasLogged = useRef(false);
|
||||
const log = () => {
|
||||
if (!hasLogged.current) {
|
||||
logA();
|
||||
logB();
|
||||
hasLogged.current = true;
|
||||
}
|
||||
};
|
||||
return <Stringify log={log} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: {value: 1}, b: {value: 2}}],
|
||||
sequentialRenders: [
|
||||
{a: {value: 1}, b: {value: 2}},
|
||||
{a: {value: 3}, b: {value: 4}},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useRef } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(9);
|
||||
const { a, b } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a.value) {
|
||||
t1 = () => {
|
||||
console.log(a.value);
|
||||
};
|
||||
$[0] = a.value;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const logA = t1;
|
||||
let t2;
|
||||
if ($[2] !== b.value) {
|
||||
t2 = () => {
|
||||
console.log(b.value);
|
||||
};
|
||||
$[2] = b.value;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
const logB = t2;
|
||||
|
||||
const hasLogged = useRef(false);
|
||||
let t3;
|
||||
if ($[4] !== logA || $[5] !== logB) {
|
||||
t3 = () => {
|
||||
if (!hasLogged.current) {
|
||||
logA();
|
||||
logB();
|
||||
hasLogged.current = true;
|
||||
}
|
||||
};
|
||||
$[4] = logA;
|
||||
$[5] = logB;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
const log = t3;
|
||||
let t4;
|
||||
if ($[7] !== log) {
|
||||
t4 = <Stringify log={log} shouldInvokeFns={true} />;
|
||||
$[7] = log;
|
||||
$[8] = t4;
|
||||
} else {
|
||||
t4 = $[8];
|
||||
}
|
||||
return t4;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ a: { value: 1 }, b: { value: 2 } }],
|
||||
sequentialRenders: [
|
||||
{ a: { value: 1 }, b: { value: 2 } },
|
||||
{ a: { value: 3 }, b: { value: 4 } },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"log":{"kind":"Function"},"shouldInvokeFns":true}</div>
|
||||
<div>{"log":{"kind":"Function"},"shouldInvokeFns":true}</div>
|
||||
logs: [1,2]
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import {useRef} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Component({a, b}) {
|
||||
const logA = () => {
|
||||
console.log(a.value);
|
||||
};
|
||||
const logB = () => {
|
||||
console.log(b.value);
|
||||
};
|
||||
const hasLogged = useRef(false);
|
||||
const log = () => {
|
||||
if (!hasLogged.current) {
|
||||
logA();
|
||||
logB();
|
||||
hasLogged.current = true;
|
||||
}
|
||||
};
|
||||
return <Stringify log={log} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: {value: 1}, b: {value: 2}}],
|
||||
sequentialRenders: [
|
||||
{a: {value: 1}, b: {value: 2}},
|
||||
{a: {value: 3}, b: {value: 4}},
|
||||
],
|
||||
};
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useState} from 'react';
|
||||
import {useIdentity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that conditionally called functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback({obj}: {obj: {value: number}}) {
|
||||
const [state, setState] = useState(0);
|
||||
const cb = () => {
|
||||
if (obj.value !== 0) setState(obj.value);
|
||||
};
|
||||
useIdentity(null);
|
||||
if (state === 0) {
|
||||
cb();
|
||||
}
|
||||
return {cb};
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}}],
|
||||
sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useState } from "react";
|
||||
import { useIdentity } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Assume that conditionally called functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(4);
|
||||
const { obj } = t0;
|
||||
const [state, setState] = useState(0);
|
||||
let t1;
|
||||
if ($[0] !== obj.value) {
|
||||
t1 = () => {
|
||||
if (obj.value !== 0) {
|
||||
setState(obj.value);
|
||||
}
|
||||
};
|
||||
$[0] = obj.value;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb = t1;
|
||||
|
||||
useIdentity(null);
|
||||
if (state === 0) {
|
||||
cb();
|
||||
}
|
||||
let t2;
|
||||
if ($[2] !== cb) {
|
||||
t2 = { cb };
|
||||
$[2] = cb;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{ obj: { value: 1 } }],
|
||||
sequentialRenders: [{ obj: { value: 1 } }, { obj: { value: 2 } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) {"cb":"[[ function params=0 ]]"}
|
||||
{"cb":"[[ function params=0 ]]"}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import {useState} from 'react';
|
||||
import {useIdentity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that conditionally called functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback({obj}: {obj: {value: number}}) {
|
||||
const [state, setState] = useState(0);
|
||||
const cb = () => {
|
||||
if (obj.value !== 0) setState(obj.value);
|
||||
};
|
||||
useIdentity(null);
|
||||
if (state === 0) {
|
||||
cb();
|
||||
}
|
||||
return {cb};
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}}],
|
||||
sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
|
||||
};
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that conditionally returned functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
shouldMakeCb,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
shouldMakeCb: boolean;
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
const cb = () => setState(obj.value);
|
||||
if (shouldMakeCb) return cb;
|
||||
else return null;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, shouldMakeCb: true, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, shouldMakeCb: true, setState},
|
||||
{obj: {value: 2}, shouldMakeCb: true, setState},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { createHookWrapper } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Assume that conditionally returned functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(3);
|
||||
const { obj, shouldMakeCb, setState } = t0;
|
||||
let t1;
|
||||
if ($[0] !== obj.value || $[1] !== setState) {
|
||||
t1 = () => setState(obj.value);
|
||||
$[0] = obj.value;
|
||||
$[1] = setState;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
const cb = t1;
|
||||
if (shouldMakeCb) {
|
||||
return cb;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
"use no memo";
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{ obj: { value: 1 }, shouldMakeCb: true, setState }],
|
||||
sequentialRenders: [
|
||||
{ obj: { value: 1 }, shouldMakeCb: true, setState },
|
||||
{ obj: { value: 2 }, shouldMakeCb: true, setState },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"result":{"kind":"Function","result":1},"shouldInvokeFns":true}</div>
|
||||
<div>{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}</div>
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that conditionally returned functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
shouldMakeCb,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
shouldMakeCb: boolean;
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
const cb = () => setState(obj.value);
|
||||
if (shouldMakeCb) return cb;
|
||||
else return null;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, shouldMakeCb: true, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, shouldMakeCb: true, setState},
|
||||
{obj: {value: 2}, shouldMakeCb: true, setState},
|
||||
],
|
||||
};
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useState} from 'react';
|
||||
import {useIdentity} from 'shared-runtime';
|
||||
|
||||
function useMakeCallback({obj}: {obj: {value: number}}) {
|
||||
const [state, setState] = useState(0);
|
||||
const cb = () => {
|
||||
if (obj.value !== state) setState(obj.value);
|
||||
};
|
||||
useIdentity();
|
||||
cb();
|
||||
return [cb];
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}}],
|
||||
sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useState } from "react";
|
||||
import { useIdentity } from "shared-runtime";
|
||||
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(5);
|
||||
const { obj } = t0;
|
||||
const [state, setState] = useState(0);
|
||||
let t1;
|
||||
if ($[0] !== obj.value || $[1] !== state) {
|
||||
t1 = () => {
|
||||
if (obj.value !== state) {
|
||||
setState(obj.value);
|
||||
}
|
||||
};
|
||||
$[0] = obj.value;
|
||||
$[1] = state;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
const cb = t1;
|
||||
|
||||
useIdentity();
|
||||
cb();
|
||||
let t2;
|
||||
if ($[3] !== cb) {
|
||||
t2 = [cb];
|
||||
$[3] = cb;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{ obj: { value: 1 } }],
|
||||
sequentialRenders: [{ obj: { value: 1 } }, { obj: { value: 2 } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) ["[[ function params=0 ]]"]
|
||||
["[[ function params=0 ]]"]
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import {useState} from 'react';
|
||||
import {useIdentity} from 'shared-runtime';
|
||||
|
||||
function useMakeCallback({obj}: {obj: {value: number}}) {
|
||||
const [state, setState] = useState(0);
|
||||
const cb = () => {
|
||||
if (obj.value !== state) setState(obj.value);
|
||||
};
|
||||
useIdentity();
|
||||
cb();
|
||||
return [cb];
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}}],
|
||||
sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
|
||||
};
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* (Given that the returned lambda is assumed to be invoked, see
|
||||
* return-function)
|
||||
*
|
||||
* If lambda A conditionally calls lambda B, optimistically assume that property
|
||||
* loads from lambda B has the same hoistability of ones from lambda A. This
|
||||
* helps optimize components / hooks that create and chain many helper
|
||||
* functions.
|
||||
*
|
||||
* Type systems and code readability encourage developers to colocate length and
|
||||
* null checks values in the same function as where values are used. i.e.
|
||||
* developers are unlikely to write the following code.
|
||||
* ```js
|
||||
* function useFoo(obj, objNotNullAndHasElements) {
|
||||
* // ...
|
||||
* const get0th = () => obj.arr[0].value;
|
||||
* return () => objNotNullAndHasElements ? get0th : undefined;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In Meta code, this assumption helps reduce the number of memo dependency
|
||||
* deopts.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
cond,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
cond: boolean;
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
const cb = () => setState(obj.value);
|
||||
// cb's property loads are assumed to be hoistable to the start of this lambda
|
||||
return () => (cond ? cb() : undefined);
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, cond: true, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, cond: true, setState},
|
||||
{obj: {value: 2}, cond: true, setState},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { createHookWrapper } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* (Given that the returned lambda is assumed to be invoked, see
|
||||
* return-function)
|
||||
*
|
||||
* If lambda A conditionally calls lambda B, optimistically assume that property
|
||||
* loads from lambda B has the same hoistability of ones from lambda A. This
|
||||
* helps optimize components / hooks that create and chain many helper
|
||||
* functions.
|
||||
*
|
||||
* Type systems and code readability encourage developers to colocate length and
|
||||
* null checks values in the same function as where values are used. i.e.
|
||||
* developers are unlikely to write the following code.
|
||||
* ```js
|
||||
* function useFoo(obj, objNotNullAndHasElements) {
|
||||
* // ...
|
||||
* const get0th = () => obj.arr[0].value;
|
||||
* return () => objNotNullAndHasElements ? get0th : undefined;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In Meta code, this assumption helps reduce the number of memo dependency
|
||||
* deopts.
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(6);
|
||||
const { obj, cond, setState } = t0;
|
||||
let t1;
|
||||
if ($[0] !== obj.value || $[1] !== setState) {
|
||||
t1 = () => setState(obj.value);
|
||||
$[0] = obj.value;
|
||||
$[1] = setState;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
const cb = t1;
|
||||
let t2;
|
||||
if ($[3] !== cb || $[4] !== cond) {
|
||||
t2 = () => (cond ? cb() : undefined);
|
||||
$[3] = cb;
|
||||
$[4] = cond;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
"use no memo";
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{ obj: { value: 1 }, cond: true, setState }],
|
||||
sequentialRenders: [
|
||||
{ obj: { value: 1 }, cond: true, setState },
|
||||
{ obj: { value: 2 }, cond: true, setState },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"result":{"kind":"Function","result":1},"shouldInvokeFns":true}</div>
|
||||
<div>{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}</div>
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* (Given that the returned lambda is assumed to be invoked, see
|
||||
* return-function)
|
||||
*
|
||||
* If lambda A conditionally calls lambda B, optimistically assume that property
|
||||
* loads from lambda B has the same hoistability of ones from lambda A. This
|
||||
* helps optimize components / hooks that create and chain many helper
|
||||
* functions.
|
||||
*
|
||||
* Type systems and code readability encourage developers to colocate length and
|
||||
* null checks values in the same function as where values are used. i.e.
|
||||
* developers are unlikely to write the following code.
|
||||
* ```js
|
||||
* function useFoo(obj, objNotNullAndHasElements) {
|
||||
* // ...
|
||||
* const get0th = () => obj.arr[0].value;
|
||||
* return () => objNotNullAndHasElements ? get0th : undefined;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In Meta code, this assumption helps reduce the number of memo dependency
|
||||
* deopts.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
cond,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
cond: boolean;
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
const cb = () => setState(obj.value);
|
||||
// cb's property loads are assumed to be hoistable to the start of this lambda
|
||||
return () => (cond ? cb() : undefined);
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, cond: true, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, cond: true, setState},
|
||||
{obj: {value: 2}, cond: true, setState},
|
||||
],
|
||||
};
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {createHookWrapper, useIdentity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that functions passed hook arguments are invoked and that their
|
||||
* property loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
const cb = useIdentity(() => setState(obj.value));
|
||||
return cb;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, setState},
|
||||
{obj: {value: 2}, setState},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { createHookWrapper, useIdentity } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Assume that functions passed hook arguments are invoked and that their
|
||||
* property loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(3);
|
||||
const { obj, setState } = t0;
|
||||
let t1;
|
||||
if ($[0] !== obj.value || $[1] !== setState) {
|
||||
t1 = () => setState(obj.value);
|
||||
$[0] = obj.value;
|
||||
$[1] = setState;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
const cb = useIdentity(t1);
|
||||
return cb;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
"use no memo";
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{ obj: { value: 1 }, setState }],
|
||||
sequentialRenders: [
|
||||
{ obj: { value: 1 }, setState },
|
||||
{ obj: { value: 2 }, setState },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"result":{"kind":"Function","result":1},"shouldInvokeFns":true}</div>
|
||||
<div>{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}</div>
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import {createHookWrapper, useIdentity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that functions passed hook arguments are invoked and that their
|
||||
* property loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
const cb = useIdentity(() => setState(obj.value));
|
||||
return cb;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, setState},
|
||||
{obj: {value: 2}, setState},
|
||||
],
|
||||
};
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
function useFoo({arr1}) {
|
||||
const cb1 = e => arr1[0].value + e.value;
|
||||
const x = arr1.map(cb1);
|
||||
return [x, cb1];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useFoo),
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { createHookWrapper } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(8);
|
||||
const { arr1 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr1[0]) {
|
||||
t1 = (e) => arr1[0].value + e.value;
|
||||
$[0] = arr1[0];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb1 = t1;
|
||||
let t2;
|
||||
if ($[2] !== arr1 || $[3] !== cb1) {
|
||||
t2 = arr1.map(cb1);
|
||||
$[2] = arr1;
|
||||
$[3] = cb1;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
const x = t2;
|
||||
let t3;
|
||||
if ($[5] !== cb1 || $[6] !== x) {
|
||||
t3 = [x, cb1];
|
||||
$[5] = cb1;
|
||||
$[6] = x;
|
||||
$[7] = t3;
|
||||
} else {
|
||||
t3 = $[7];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useFoo),
|
||||
params: [{ arr1: [], arr2: [] }],
|
||||
sequentialRenders: [
|
||||
{ arr1: [], arr2: [] },
|
||||
{ arr1: [], arr2: null },
|
||||
{ arr1: [{ value: 1 }, { value: 2 }], arr2: [{ value: -1 }] },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"result":[[],"[[ function params=1 ]]"],"shouldInvokeFns":true}</div>
|
||||
<div>{"result":[[],"[[ function params=1 ]]"],"shouldInvokeFns":true}</div>
|
||||
<div>{"result":[[2,3],"[[ function params=1 ]]"],"shouldInvokeFns":true}</div>
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
function useFoo({arr1}) {
|
||||
const cb1 = e => arr1[0].value + e.value;
|
||||
const x = arr1.map(cb1);
|
||||
return [x, cb1];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useFoo),
|
||||
params: [{arr1: [], arr2: []}],
|
||||
sequentialRenders: [
|
||||
{arr1: [], arr2: []},
|
||||
{arr1: [], arr2: null},
|
||||
{arr1: [{value: 1}, {value: 2}], arr2: [{value: -1}]},
|
||||
],
|
||||
};
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @flow
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that functions captured directly as jsx attributes are invoked and
|
||||
* that their property loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number},
|
||||
setState: (newState: number) => void,
|
||||
}) {
|
||||
return <Stringify cb={() => setState(obj.value)} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, setState},
|
||||
{obj: {value: 2}, setState},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(3);
|
||||
const { obj, setState } = t0;
|
||||
let t1;
|
||||
if ($[0] !== obj.value || $[1] !== setState) {
|
||||
t1 = <Stringify cb={() => setState(obj.value)} shouldInvokeFns={true} />;
|
||||
$[0] = obj.value;
|
||||
$[1] = setState;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
"use no memo";
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{ obj: { value: 1 }, setState }],
|
||||
sequentialRenders: [
|
||||
{ obj: { value: 1 }, setState },
|
||||
{ obj: { value: 2 }, setState },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"cb":{"kind":"Function","result":1},"shouldInvokeFns":true}</div>
|
||||
<div>{"cb":{"kind":"Function","result":2},"shouldInvokeFns":true}</div>
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// @flow
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that functions captured directly as jsx attributes are invoked and
|
||||
* that their property loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
return <Stringify cb={() => setState(obj.value)} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, setState},
|
||||
{obj: {value: 2}, setState},
|
||||
],
|
||||
};
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that directly returned functions are invoked and that their property
|
||||
* loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
return () => setState(obj.value);
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, setState},
|
||||
{obj: {value: 2}, setState},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { createHookWrapper } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Assume that directly returned functions are invoked and that their property
|
||||
* loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(3);
|
||||
const { obj, setState } = t0;
|
||||
let t1;
|
||||
if ($[0] !== obj.value || $[1] !== setState) {
|
||||
t1 = () => setState(obj.value);
|
||||
$[0] = obj.value;
|
||||
$[1] = setState;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
"use no memo";
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{ obj: { value: 1 }, setState }],
|
||||
sequentialRenders: [
|
||||
{ obj: { value: 1 }, setState },
|
||||
{ obj: { value: 2 }, setState },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"result":{"kind":"Function","result":1},"shouldInvokeFns":true}</div>
|
||||
<div>{"result":{"kind":"Function","result":2},"shouldInvokeFns":true}</div>
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import {createHookWrapper} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that directly returned functions are invoked and that their property
|
||||
* loads are hoistable.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
setState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
setState: (newState: number) => void;
|
||||
}) {
|
||||
return () => setState(obj.value);
|
||||
}
|
||||
|
||||
const setState = (arg: number) => {
|
||||
'use no memo';
|
||||
return arg;
|
||||
};
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: createHookWrapper(useMakeCallback),
|
||||
params: [{obj: {value: 1}, setState}],
|
||||
sequentialRenders: [
|
||||
{obj: {value: 1}, setState},
|
||||
{obj: {value: 2}, setState},
|
||||
],
|
||||
};
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useState, useMemo} from 'react';
|
||||
import {useIdentity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that conditionally called functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
shouldSynchronizeState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
shouldSynchronizeState: boolean;
|
||||
}) {
|
||||
const [state, setState] = useState(0);
|
||||
const cb = useMemo(() => {
|
||||
return () => {
|
||||
if (obj.value !== 0) setState(obj.value);
|
||||
};
|
||||
}, [obj.value, shouldSynchronizeState]);
|
||||
useIdentity(null);
|
||||
return cb;
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}}],
|
||||
sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useState, useMemo } from "react";
|
||||
import { useIdentity } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Assume that conditionally called functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(2);
|
||||
const { obj, shouldSynchronizeState } = t0;
|
||||
|
||||
const [, setState] = useState(0);
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== obj.value) {
|
||||
t2 = () => {
|
||||
if (obj.value !== 0) {
|
||||
setState(obj.value);
|
||||
}
|
||||
};
|
||||
$[0] = obj.value;
|
||||
$[1] = t2;
|
||||
} else {
|
||||
t2 = $[1];
|
||||
}
|
||||
t1 = t2;
|
||||
const cb = t1;
|
||||
|
||||
useIdentity(null);
|
||||
return cb;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{ obj: { value: 1 } }],
|
||||
sequentialRenders: [{ obj: { value: 1 } }, { obj: { value: 2 } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "[[ function params=0 ]]"
|
||||
"[[ function params=0 ]]"
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import {useState, useMemo} from 'react';
|
||||
import {useIdentity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Assume that conditionally called functions can be invoked and that their
|
||||
* property loads are hoistable to the function declaration site.
|
||||
*/
|
||||
function useMakeCallback({
|
||||
obj,
|
||||
shouldSynchronizeState,
|
||||
}: {
|
||||
obj: {value: number};
|
||||
shouldSynchronizeState: boolean;
|
||||
}) {
|
||||
const [state, setState] = useState(0);
|
||||
const cb = useMemo(() => {
|
||||
return () => {
|
||||
if (obj.value !== 0) setState(obj.value);
|
||||
};
|
||||
}, [obj.value, shouldSynchronizeState]);
|
||||
useIdentity(null);
|
||||
return cb;
|
||||
}
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{obj: {value: 1}}],
|
||||
sequentialRenders: [{obj: {value: 1}}, {obj: {value: 2}}],
|
||||
};
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Assume that only directly returned functions or JSX attributes are invoked.
|
||||
* Conservatively estimate that functions wrapped in objects or other containers
|
||||
* might never be called (and therefore their property loads are not hoistable).
|
||||
*/
|
||||
function useMakeCallback({arr}) {
|
||||
return {
|
||||
getElement0: () => arr[0].value,
|
||||
getElement1: () => arr[1].value,
|
||||
};
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{arr: [1, 2]}],
|
||||
sequentialRenders: [{arr: [1, 2]}, {arr: []}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; /**
|
||||
* Assume that only directly returned functions or JSX attributes are invoked.
|
||||
* Conservatively estimate that functions wrapped in objects or other containers
|
||||
* might never be called (and therefore their property loads are not hoistable).
|
||||
*/
|
||||
function useMakeCallback(t0) {
|
||||
const $ = _c(2);
|
||||
const { arr } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr) {
|
||||
t1 = { getElement0: () => arr[0].value, getElement1: () => arr[1].value };
|
||||
$[0] = arr;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{ arr: [1, 2] }],
|
||||
sequentialRenders: [{ arr: [1, 2] }, { arr: [] }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) {"getElement0":"[[ function params=0 ]]","getElement1":"[[ function params=0 ]]"}
|
||||
{"getElement0":"[[ function params=0 ]]","getElement1":"[[ function params=0 ]]"}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Assume that only directly returned functions or JSX attributes are invoked.
|
||||
* Conservatively estimate that functions wrapped in objects or other containers
|
||||
* might never be called (and therefore their property loads are not hoistable).
|
||||
*/
|
||||
function useMakeCallback({arr}) {
|
||||
return {
|
||||
getElement0: () => arr[0].value,
|
||||
getElement1: () => arr[1].value,
|
||||
};
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useMakeCallback,
|
||||
params: [{arr: [1, 2]}],
|
||||
sequentialRenders: [{arr: [1, 2]}, {arr: []}],
|
||||
};
|
||||
+2
-2
@@ -41,9 +41,9 @@ function useFoo(t0) {
|
||||
local = $[1];
|
||||
}
|
||||
let t1;
|
||||
if ($[2] !== local.b.c) {
|
||||
if ($[2] !== local) {
|
||||
t1 = () => [() => local.b.c];
|
||||
$[2] = local.b.c;
|
||||
$[2] = local;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
|
||||
+2
-2
@@ -34,13 +34,13 @@ function useFoo(t0) {
|
||||
const $ = _c(4);
|
||||
const { a } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a.b.c) {
|
||||
if ($[0] !== a) {
|
||||
t1 = {
|
||||
fn() {
|
||||
return identity(a.b.c);
|
||||
},
|
||||
};
|
||||
$[0] = a.b.c;
|
||||
$[0] = a;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
|
||||
+2
-2
@@ -51,7 +51,7 @@ import { identity } from "shared-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
let x;
|
||||
if ($[0] !== props.cond) {
|
||||
if ($[0] !== props) {
|
||||
const f = () => {
|
||||
if (props.cond) {
|
||||
x = 1;
|
||||
@@ -62,7 +62,7 @@ function Component(props) {
|
||||
|
||||
const f2 = identity(f);
|
||||
f2();
|
||||
$[0] = props.cond;
|
||||
$[0] = props;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
|
||||
+2
-2
@@ -82,9 +82,9 @@ function Component(t0) {
|
||||
contextVar = $[2];
|
||||
}
|
||||
let t1;
|
||||
if ($[3] !== contextVar.val) {
|
||||
if ($[3] !== contextVar) {
|
||||
t1 = { cb: () => contextVar.val * 4 };
|
||||
$[3] = contextVar.val;
|
||||
$[3] = contextVar;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
|
||||
+2
-2
@@ -43,7 +43,7 @@ const t0 = "module_t0";
|
||||
const c_0 = "module_c_0";
|
||||
function useFoo(props) {
|
||||
const $0 = _c(2);
|
||||
const c_00 = $0[0] !== props.value;
|
||||
const c_00 = $0[0] !== props;
|
||||
let t1;
|
||||
if (c_00) {
|
||||
const a = {
|
||||
@@ -61,7 +61,7 @@ function useFoo(props) {
|
||||
};
|
||||
|
||||
t1 = a.foo().bar();
|
||||
$0[0] = props.value;
|
||||
$0[0] = props;
|
||||
$0[1] = t1;
|
||||
} else {
|
||||
t1 = $0[1];
|
||||
|
||||
+10
-15
@@ -35,7 +35,7 @@ function Component(props) {
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableTransitivelyFreezeFunctionExpressions:false
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(9);
|
||||
const $ = _c(7);
|
||||
const item = useMutable(props.itemId);
|
||||
const dispatch = useDispatch();
|
||||
useFreeze(dispatch);
|
||||
@@ -51,7 +51,8 @@ function Component(props) {
|
||||
}
|
||||
const exit = t0;
|
||||
let t1;
|
||||
if ($[2] !== exit || $[3] !== item.value) {
|
||||
let t2;
|
||||
if ($[2] !== exit || $[3] !== item) {
|
||||
t1 = () => {
|
||||
const cleanup = GlobalEventEmitter.addListener("onInput", () => {
|
||||
if (item.value) {
|
||||
@@ -60,30 +61,24 @@ function Component(props) {
|
||||
});
|
||||
return () => cleanup.remove();
|
||||
};
|
||||
t2 = [exit, item];
|
||||
$[2] = exit;
|
||||
$[3] = item.value;
|
||||
$[3] = item;
|
||||
$[4] = t1;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
let t2;
|
||||
if ($[5] !== exit || $[6] !== item) {
|
||||
t2 = [exit, item];
|
||||
$[5] = exit;
|
||||
$[6] = item;
|
||||
$[7] = t2;
|
||||
} else {
|
||||
t2 = $[7];
|
||||
t2 = $[5];
|
||||
}
|
||||
useEffect(t1, t2);
|
||||
|
||||
maybeMutate(item);
|
||||
let t3;
|
||||
if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = <div />;
|
||||
$[8] = t3;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[8];
|
||||
t3 = $[6];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user