mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[compiler][hir] Only hoist always-accessed PropertyLoads from function decls
Prior to this PR, we consider all of a nested function's accessed paths as 'hoistable' (to the basic block in which the function was defined). Now, we traverse nested functions and find all paths hoistable to their *entry block*.
Note that this only replaces the *hoisting* part of function declarations, not dependencies. This realistically only affects optional chains within functions, which always get truncated to its inner non-optional path (see [todo-infer-function-uncond-optionals-hoisted.tsx](https://github.com/facebook/react/blob/576f3c0aa898cb99da1b7bf15317756e25c13708/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/reduce-reactive-deps/todo-infer-function-uncond-optionals-hoisted.tsx))
See newly added test fixtures for details
Update: Note that toggling `enableTreatFunctionDepsAsConditional` makes a non-trivial impact on granularity of inferred deps (i.e. we find that function declarations uniquely identify some paths as hoistable). Snapshot comparison of internal code shows ~2.5% of files get worse dependencies ([internal link](https://www.internalfb.com/phabricator/paste/view/P1625792186))
ghstack-source-id: 778ab5494a
Pull Request resolved: https://github.com/facebook/react/pull/31066
This commit is contained in:
+161
-70
@@ -7,6 +7,7 @@ import {
|
||||
Set_union,
|
||||
getOrInsertDefault,
|
||||
} from '../Utils/utils';
|
||||
import {collectOptionalChainSidemap} from './CollectOptionalChainDependencies';
|
||||
import {
|
||||
BasicBlock,
|
||||
BlockId,
|
||||
@@ -15,10 +16,12 @@ import {
|
||||
HIRFunction,
|
||||
Identifier,
|
||||
IdentifierId,
|
||||
InstructionId,
|
||||
InstructionValue,
|
||||
ReactiveScopeDependency,
|
||||
ScopeId,
|
||||
} from './HIR';
|
||||
import {collectTemporariesSidemap} from './PropagateScopeDependenciesHIR';
|
||||
|
||||
/**
|
||||
* Helper function for `PropagateScopeDependencies`. Uses control flow graph
|
||||
@@ -83,28 +86,57 @@ export function collectHoistablePropertyLoads(
|
||||
fn: HIRFunction,
|
||||
temporaries: ReadonlyMap<IdentifierId, ReactiveScopeDependency>,
|
||||
hoistableFromOptionals: ReadonlyMap<BlockId, ReactiveScopeDependency>,
|
||||
): ReadonlyMap<ScopeId, BlockInfo> {
|
||||
nestedFnImmutableContext: ReadonlySet<IdentifierId> | null,
|
||||
): ReadonlyMap<BlockId, BlockInfo> {
|
||||
const registry = new PropertyPathRegistry();
|
||||
|
||||
const nodes = collectNonNullsInBlocks(
|
||||
fn,
|
||||
temporaries,
|
||||
const functionExpressionLoads = collectFunctionExpressionFakeLoads(fn);
|
||||
const actuallyEvaluatedTemporaries = new Map(
|
||||
[...temporaries].filter(([id]) => !functionExpressionLoads.has(id)),
|
||||
);
|
||||
|
||||
/**
|
||||
* Due to current limitations of mutable range inference, there are edge cases in
|
||||
* which we infer known-immutable values (e.g. props or hook params) to have a
|
||||
* mutable range and scope.
|
||||
* (see `destructure-array-declaration-to-context-var` fixture)
|
||||
* We track known immutable identifiers to reduce regressions (as PropagateScopeDeps
|
||||
* is being rewritten to HIR).
|
||||
*/
|
||||
const knownImmutableIdentifiers = new Set<IdentifierId>();
|
||||
if (fn.fnType === 'Component' || fn.fnType === 'Hook') {
|
||||
for (const p of fn.params) {
|
||||
if (p.kind === 'Identifier') {
|
||||
knownImmutableIdentifiers.add(p.identifier.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
const nodes = collectNonNullsInBlocks(fn, {
|
||||
temporaries: actuallyEvaluatedTemporaries,
|
||||
knownImmutableIdentifiers,
|
||||
hoistableFromOptionals,
|
||||
registry,
|
||||
);
|
||||
nestedFnImmutableContext,
|
||||
});
|
||||
propagateNonNull(fn, nodes, registry);
|
||||
|
||||
const nodesKeyedByScopeId = new Map<ScopeId, BlockInfo>();
|
||||
return nodes;
|
||||
}
|
||||
|
||||
export function keyByScopeId<T>(
|
||||
fn: HIRFunction,
|
||||
source: ReadonlyMap<BlockId, T>,
|
||||
): ReadonlyMap<ScopeId, T> {
|
||||
const keyedByScopeId = new Map<ScopeId, T>();
|
||||
for (const [_, block] of fn.body.blocks) {
|
||||
if (block.terminal.kind === 'scope') {
|
||||
nodesKeyedByScopeId.set(
|
||||
keyedByScopeId.set(
|
||||
block.terminal.scope.id,
|
||||
nodes.get(block.terminal.block)!,
|
||||
source.get(block.terminal.block)!,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return nodesKeyedByScopeId;
|
||||
return keyedByScopeId;
|
||||
}
|
||||
|
||||
export type BlockInfo = {
|
||||
@@ -211,45 +243,75 @@ class PropertyPathRegistry {
|
||||
|
||||
function getMaybeNonNullInInstruction(
|
||||
instr: InstructionValue,
|
||||
temporaries: ReadonlyMap<IdentifierId, ReactiveScopeDependency>,
|
||||
registry: PropertyPathRegistry,
|
||||
context: CollectNonNullsInBlocksContext,
|
||||
): PropertyPathNode | null {
|
||||
let path = null;
|
||||
if (instr.kind === 'PropertyLoad') {
|
||||
path = temporaries.get(instr.object.identifier.id) ?? {
|
||||
path = context.temporaries.get(instr.object.identifier.id) ?? {
|
||||
identifier: instr.object.identifier,
|
||||
path: [],
|
||||
};
|
||||
} else if (instr.kind === 'Destructure') {
|
||||
path = temporaries.get(instr.value.identifier.id) ?? null;
|
||||
path = context.temporaries.get(instr.value.identifier.id) ?? null;
|
||||
} else if (instr.kind === 'ComputedLoad') {
|
||||
path = temporaries.get(instr.object.identifier.id) ?? null;
|
||||
path = context.temporaries.get(instr.object.identifier.id) ?? null;
|
||||
}
|
||||
return path != null ? registry.getOrCreateProperty(path) : null;
|
||||
return path != null ? context.registry.getOrCreateProperty(path) : null;
|
||||
}
|
||||
|
||||
function isImmutableAtInstr(
|
||||
identifier: Identifier,
|
||||
instr: InstructionId,
|
||||
context: CollectNonNullsInBlocksContext,
|
||||
): boolean {
|
||||
if (context.nestedFnImmutableContext != null) {
|
||||
/**
|
||||
* Comparing instructions ids across inner-outer function bodies is not valid, as they are numbered
|
||||
*/
|
||||
return context.nestedFnImmutableContext.has(identifier.id);
|
||||
} else {
|
||||
/**
|
||||
* Since this runs *after* buildReactiveScopeTerminals, identifier mutable ranges
|
||||
* are not valid with respect to current instruction id numbering.
|
||||
* We use attached reactive scope ranges as a proxy for mutable range, but this
|
||||
* is an overestimate as (1) scope ranges merge and align to form valid program
|
||||
* blocks and (2) passes like MemoizeFbtAndMacroOperands may assign scopes to
|
||||
* non-mutable identifiers.
|
||||
*
|
||||
* See comment in exported function for why we track known immutable identifiers.
|
||||
*/
|
||||
const mutableAtInstr =
|
||||
identifier.mutableRange.end > identifier.mutableRange.start + 1 &&
|
||||
identifier.scope != null &&
|
||||
inRange(
|
||||
{
|
||||
id: instr,
|
||||
},
|
||||
identifier.scope.range,
|
||||
);
|
||||
return (
|
||||
!mutableAtInstr || context.knownImmutableIdentifiers.has(identifier.id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
type CollectNonNullsInBlocksContext = {
|
||||
temporaries: ReadonlyMap<IdentifierId, ReactiveScopeDependency>;
|
||||
knownImmutableIdentifiers: ReadonlySet<IdentifierId>;
|
||||
hoistableFromOptionals: ReadonlyMap<BlockId, ReactiveScopeDependency>;
|
||||
registry: PropertyPathRegistry;
|
||||
/**
|
||||
* (For nested / inner function declarations)
|
||||
* Context variables (i.e. captured from an outer scope) that are immutable.
|
||||
* Note that this technically could be merged into `knownImmutableIdentifiers`,
|
||||
* but are currently kept separate for readability.
|
||||
*/
|
||||
nestedFnImmutableContext: ReadonlySet<IdentifierId> | null;
|
||||
};
|
||||
function collectNonNullsInBlocks(
|
||||
fn: HIRFunction,
|
||||
temporaries: ReadonlyMap<IdentifierId, ReactiveScopeDependency>,
|
||||
hoistableFromOptionals: ReadonlyMap<BlockId, ReactiveScopeDependency>,
|
||||
registry: PropertyPathRegistry,
|
||||
context: CollectNonNullsInBlocksContext,
|
||||
): ReadonlyMap<BlockId, BlockInfo> {
|
||||
/**
|
||||
* Due to current limitations of mutable range inference, there are edge cases in
|
||||
* which we infer known-immutable values (e.g. props or hook params) to have a
|
||||
* mutable range and scope.
|
||||
* (see `destructure-array-declaration-to-context-var` fixture)
|
||||
* We track known immutable identifiers to reduce regressions (as PropagateScopeDeps
|
||||
* is being rewritten to HIR).
|
||||
*/
|
||||
const knownImmutableIdentifiers = new Set<IdentifierId>();
|
||||
if (fn.fnType === 'Component' || fn.fnType === 'Hook') {
|
||||
for (const p of fn.params) {
|
||||
if (p.kind === 'Identifier') {
|
||||
knownImmutableIdentifiers.add(p.identifier.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Known non-null objects such as functional component props can be safely
|
||||
* read from any block.
|
||||
@@ -261,7 +323,9 @@ function collectNonNullsInBlocks(
|
||||
fn.params[0].kind === 'Identifier'
|
||||
) {
|
||||
const identifier = fn.params[0].identifier;
|
||||
knownNonNullIdentifiers.add(registry.getOrCreateIdentifier(identifier));
|
||||
knownNonNullIdentifiers.add(
|
||||
context.registry.getOrCreateIdentifier(identifier),
|
||||
);
|
||||
}
|
||||
const nodes = new Map<BlockId, BlockInfo>();
|
||||
for (const [_, block] of fn.body.blocks) {
|
||||
@@ -269,45 +333,48 @@ function collectNonNullsInBlocks(
|
||||
knownNonNullIdentifiers,
|
||||
);
|
||||
|
||||
const maybeOptionalChain = hoistableFromOptionals.get(block.id);
|
||||
const maybeOptionalChain = context.hoistableFromOptionals.get(block.id);
|
||||
if (maybeOptionalChain != null) {
|
||||
assumedNonNullObjects.add(
|
||||
registry.getOrCreateProperty(maybeOptionalChain),
|
||||
context.registry.getOrCreateProperty(maybeOptionalChain),
|
||||
);
|
||||
}
|
||||
for (const instr of block.instructions) {
|
||||
const maybeNonNull = getMaybeNonNullInInstruction(
|
||||
instr.value,
|
||||
temporaries,
|
||||
registry,
|
||||
);
|
||||
if (maybeNonNull != null) {
|
||||
const baseIdentifier = maybeNonNull.fullPath.identifier;
|
||||
/**
|
||||
* Since this runs *after* buildReactiveScopeTerminals, identifier mutable ranges
|
||||
* are not valid with respect to current instruction id numbering.
|
||||
* We use attached reactive scope ranges as a proxy for mutable range, but this
|
||||
* is an overestimate as (1) scope ranges merge and align to form valid program
|
||||
* blocks and (2) passes like MemoizeFbtAndMacroOperands may assign scopes to
|
||||
* non-mutable identifiers.
|
||||
*
|
||||
* See comment at top of function for why we track known immutable identifiers.
|
||||
*/
|
||||
const isMutableAtInstr =
|
||||
baseIdentifier.mutableRange.end >
|
||||
baseIdentifier.mutableRange.start + 1 &&
|
||||
baseIdentifier.scope != null &&
|
||||
inRange(
|
||||
{
|
||||
id: instr.id,
|
||||
},
|
||||
baseIdentifier.scope.range,
|
||||
);
|
||||
if (
|
||||
!isMutableAtInstr ||
|
||||
knownImmutableIdentifiers.has(baseIdentifier.id)
|
||||
) {
|
||||
assumedNonNullObjects.add(maybeNonNull);
|
||||
const maybeNonNull = getMaybeNonNullInInstruction(instr.value, context);
|
||||
if (
|
||||
maybeNonNull != null &&
|
||||
isImmutableAtInstr(maybeNonNull.fullPath.identifier, instr.id, context)
|
||||
) {
|
||||
assumedNonNullObjects.add(maybeNonNull);
|
||||
}
|
||||
if (
|
||||
instr.value.kind === 'FunctionExpression' &&
|
||||
!fn.env.config.enableTreatFunctionDepsAsConditional
|
||||
) {
|
||||
const innerFn = instr.value.loweredFunc;
|
||||
const innerTemporaries = collectTemporariesSidemap(
|
||||
innerFn.func,
|
||||
new Set(),
|
||||
);
|
||||
const innerOptionals = collectOptionalChainSidemap(innerFn.func);
|
||||
const innerHoistableMap = collectHoistablePropertyLoads(
|
||||
innerFn.func,
|
||||
innerTemporaries,
|
||||
innerOptionals.hoistableObjects,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -515,3 +582,27 @@ function reduceMaybeOptionalChains(
|
||||
}
|
||||
} while (changed);
|
||||
}
|
||||
|
||||
function collectFunctionExpressionFakeLoads(
|
||||
fn: HIRFunction,
|
||||
): Set<IdentifierId> {
|
||||
const sources = new Map<IdentifierId, IdentifierId>();
|
||||
const functionExpressionReferences = new Set<IdentifierId>();
|
||||
|
||||
for (const [_, block] of fn.body.blocks) {
|
||||
for (const {lvalue, value} of block.instructions) {
|
||||
if (value.kind === 'FunctionExpression') {
|
||||
for (const reference of value.loweredFunc.dependencies) {
|
||||
let curr: IdentifierId | undefined = reference.identifier.id;
|
||||
while (curr != null) {
|
||||
functionExpressionReferences.add(curr);
|
||||
curr = sources.get(curr);
|
||||
}
|
||||
}
|
||||
} else if (value.kind === 'PropertyLoad') {
|
||||
sources.set(lvalue.identifier.id, value.object.identifier.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return functionExpressionReferences;
|
||||
}
|
||||
|
||||
+7
-5
@@ -17,7 +17,10 @@ import {
|
||||
areEqualPaths,
|
||||
IdentifierId,
|
||||
} from './HIR';
|
||||
import {collectHoistablePropertyLoads} from './CollectHoistablePropertyLoads';
|
||||
import {
|
||||
collectHoistablePropertyLoads,
|
||||
keyByScopeId,
|
||||
} from './CollectHoistablePropertyLoads';
|
||||
import {
|
||||
ScopeBlockTraversal,
|
||||
eachInstructionOperand,
|
||||
@@ -41,10 +44,9 @@ export function propagateScopeDependenciesHIR(fn: HIRFunction): void {
|
||||
hoistableObjects,
|
||||
} = collectOptionalChainSidemap(fn);
|
||||
|
||||
const hoistablePropertyLoads = collectHoistablePropertyLoads(
|
||||
const hoistablePropertyLoads = keyByScopeId(
|
||||
fn,
|
||||
temporaries,
|
||||
hoistableObjects,
|
||||
collectHoistablePropertyLoads(fn, temporaries, hoistableObjects, null),
|
||||
);
|
||||
|
||||
const scopeDeps = collectDependencies(
|
||||
@@ -209,7 +211,7 @@ function findTemporariesUsedOutsideDeclaringScope(
|
||||
* of $1, as the evaluation of `arr.length` changes between instructions $1 and
|
||||
* $3. We do not track $1 -> arr.length in this case.
|
||||
*/
|
||||
function collectTemporariesSidemap(
|
||||
export function collectTemporariesSidemap(
|
||||
fn: HIRFunction,
|
||||
usedOutsideDeclaringScope: ReadonlySet<DeclarationId>,
|
||||
): ReadonlyMap<IdentifierId, ReactiveScopeDependency> {
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {shallowCopy, mutate, Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({
|
||||
a,
|
||||
shouldReadA,
|
||||
}: {
|
||||
a: {b: {c: number}; x: number};
|
||||
shouldReadA: boolean;
|
||||
}) {
|
||||
const local = shallowCopy(a);
|
||||
mutate(local);
|
||||
return (
|
||||
<Stringify
|
||||
fn={() => {
|
||||
if (shouldReadA) return local.b.c;
|
||||
return null;
|
||||
}}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null, shouldReadA: true}],
|
||||
sequentialRenders: [
|
||||
{a: null, shouldReadA: true},
|
||||
{a: null, shouldReadA: false},
|
||||
{a: {b: {c: 4}}, shouldReadA: true},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
|
||||
import { shallowCopy, mutate, Stringify } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(5);
|
||||
const { a, shouldReadA } = t0;
|
||||
let local;
|
||||
if ($[0] !== a) {
|
||||
local = shallowCopy(a);
|
||||
mutate(local);
|
||||
$[0] = a;
|
||||
$[1] = local;
|
||||
} else {
|
||||
local = $[1];
|
||||
}
|
||||
let t1;
|
||||
if ($[2] !== shouldReadA || $[3] !== local) {
|
||||
t1 = (
|
||||
<Stringify
|
||||
fn={() => {
|
||||
if (shouldReadA) {
|
||||
return local.b.c;
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
$[2] = shouldReadA;
|
||||
$[3] = local;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ a: null, shouldReadA: true }],
|
||||
sequentialRenders: [
|
||||
{ a: null, shouldReadA: true },
|
||||
{ a: null, shouldReadA: false },
|
||||
{ a: { b: { c: 4 } }, shouldReadA: true },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[ (exception in render) TypeError: Cannot read properties of undefined (reading 'c') ]]
|
||||
<div>{"fn":{"kind":"Function","result":null},"shouldInvokeFns":true}</div>
|
||||
<div>{"fn":{"kind":"Function","result":4},"shouldInvokeFns":true}</div>
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {shallowCopy, mutate, Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({
|
||||
a,
|
||||
shouldReadA,
|
||||
}: {
|
||||
a: {b: {c: number}; x: number};
|
||||
shouldReadA: boolean;
|
||||
}) {
|
||||
const local = shallowCopy(a);
|
||||
mutate(local);
|
||||
return (
|
||||
<Stringify
|
||||
fn={() => {
|
||||
if (shouldReadA) return local.b.c;
|
||||
return null;
|
||||
}}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null, shouldReadA: true}],
|
||||
sequentialRenders: [
|
||||
{a: null, shouldReadA: true},
|
||||
{a: null, shouldReadA: false},
|
||||
{a: {b: {c: 4}}, shouldReadA: true},
|
||||
],
|
||||
};
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Foo({a, shouldReadA}) {
|
||||
return (
|
||||
<Stringify
|
||||
fn={() => {
|
||||
if (shouldReadA) return a.b.c;
|
||||
return null;
|
||||
}}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{a: null, shouldReadA: true}],
|
||||
sequentialRenders: [
|
||||
{a: null, shouldReadA: true},
|
||||
{a: null, shouldReadA: false},
|
||||
{a: {b: {c: 4}}, shouldReadA: true},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Foo(t0) {
|
||||
const $ = _c(3);
|
||||
const { a, shouldReadA } = t0;
|
||||
let t1;
|
||||
if ($[0] !== shouldReadA || $[1] !== a) {
|
||||
t1 = (
|
||||
<Stringify
|
||||
fn={() => {
|
||||
if (shouldReadA) {
|
||||
return a.b.c;
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
$[0] = shouldReadA;
|
||||
$[1] = a;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{ a: null, shouldReadA: true }],
|
||||
sequentialRenders: [
|
||||
{ a: null, shouldReadA: true },
|
||||
{ a: null, shouldReadA: false },
|
||||
{ a: { b: { c: 4 } }, shouldReadA: true },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[ (exception in render) TypeError: Cannot read properties of null (reading 'b') ]]
|
||||
<div>{"fn":{"kind":"Function","result":null},"shouldInvokeFns":true}</div>
|
||||
<div>{"fn":{"kind":"Function","result":4},"shouldInvokeFns":true}</div>
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Foo({a, shouldReadA}) {
|
||||
return (
|
||||
<Stringify
|
||||
fn={() => {
|
||||
if (shouldReadA) return a.b.c;
|
||||
return null;
|
||||
}}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{a: null, shouldReadA: true}],
|
||||
sequentialRenders: [
|
||||
{a: null, shouldReadA: true},
|
||||
{a: null, shouldReadA: false},
|
||||
{a: {b: {c: 4}}, shouldReadA: true},
|
||||
],
|
||||
};
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}) {
|
||||
return <Stringify fn={() => a.b.c} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [{a: null}, {a: {b: {c: 4}}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(2);
|
||||
const { a } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a.b.c) {
|
||||
t1 = <Stringify fn={() => a.b.c} shouldInvokeFns={true} />;
|
||||
$[0] = a.b.c;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ a: null }],
|
||||
sequentialRenders: [{ a: null }, { a: { b: { c: 4 } } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[ (exception in render) TypeError: Cannot read properties of null (reading 'b') ]]
|
||||
<div>{"fn":{"kind":"Function","result":4},"shouldInvokeFns":true}</div>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}) {
|
||||
return <Stringify fn={() => a.b.c} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [{a: null}, {a: {b: {c: 4}}}],
|
||||
};
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {identity, makeArray, Stringify, useIdentity} from 'shared-runtime';
|
||||
|
||||
function Foo({a, cond}) {
|
||||
// Assume fn will be uncond evaluated, so we can safely evaluate {a.<any>,
|
||||
// a.b.<any}
|
||||
const fn = () => [a, a.b.c];
|
||||
useIdentity(null);
|
||||
const x = makeArray();
|
||||
if (cond) {
|
||||
x.push(identity(a.b.c));
|
||||
}
|
||||
return <Stringify fn={fn} x={x} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{a: null, cond: true}],
|
||||
sequentialRenders: [
|
||||
{a: null, cond: true},
|
||||
{a: {b: {c: 4}}, cond: true},
|
||||
{a: {b: {c: 4}}, cond: true},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
|
||||
import { identity, makeArray, Stringify, useIdentity } from "shared-runtime";
|
||||
|
||||
function Foo(t0) {
|
||||
const $ = _c(8);
|
||||
const { a, cond } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a) {
|
||||
t1 = () => [a, a.b.c];
|
||||
$[0] = a;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const fn = t1;
|
||||
useIdentity(null);
|
||||
let x;
|
||||
if ($[2] !== cond || $[3] !== a.b.c) {
|
||||
x = makeArray();
|
||||
if (cond) {
|
||||
x.push(identity(a.b.c));
|
||||
}
|
||||
$[2] = cond;
|
||||
$[3] = a.b.c;
|
||||
$[4] = x;
|
||||
} else {
|
||||
x = $[4];
|
||||
}
|
||||
let t2;
|
||||
if ($[5] !== fn || $[6] !== x) {
|
||||
t2 = <Stringify fn={fn} x={x} shouldInvokeFns={true} />;
|
||||
$[5] = fn;
|
||||
$[6] = x;
|
||||
$[7] = t2;
|
||||
} else {
|
||||
t2 = $[7];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{ a: null, cond: true }],
|
||||
sequentialRenders: [
|
||||
{ a: null, cond: true },
|
||||
{ a: { b: { c: 4 } }, cond: true },
|
||||
{ a: { b: { c: 4 } }, cond: true },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[ (exception in render) TypeError: Cannot read properties of null (reading 'b') ]]
|
||||
<div>{"fn":{"kind":"Function","result":[{"b":{"c":4}},4]},"x":[4],"shouldInvokeFns":true}</div>
|
||||
<div>{"fn":{"kind":"Function","result":[{"b":{"c":4}},4]},"x":[4],"shouldInvokeFns":true}</div>
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {identity, makeArray, Stringify, useIdentity} from 'shared-runtime';
|
||||
|
||||
function Foo({a, cond}) {
|
||||
// Assume fn will be uncond evaluated, so we can safely evaluate {a.<any>,
|
||||
// a.b.<any}
|
||||
const fn = () => [a, a.b.c];
|
||||
useIdentity(null);
|
||||
const x = makeArray();
|
||||
if (cond) {
|
||||
x.push(identity(a.b.c));
|
||||
}
|
||||
return <Stringify fn={fn} x={x} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{a: null, cond: true}],
|
||||
sequentialRenders: [
|
||||
{a: null, cond: true},
|
||||
{a: {b: {c: 4}}, cond: true},
|
||||
{a: {b: {c: 4}}, cond: true},
|
||||
],
|
||||
};
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {mutate, shallowCopy, Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}: {a: {b: {c: number}}}) {
|
||||
const local = shallowCopy(a);
|
||||
mutate(local);
|
||||
const fn = () => local.b.c;
|
||||
return <Stringify fn={fn} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [{a: null}, {a: {b: {c: 4}}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
|
||||
import { mutate, shallowCopy, Stringify } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(6);
|
||||
const { a } = t0;
|
||||
let local;
|
||||
if ($[0] !== a) {
|
||||
local = shallowCopy(a);
|
||||
mutate(local);
|
||||
$[0] = a;
|
||||
$[1] = local;
|
||||
} else {
|
||||
local = $[1];
|
||||
}
|
||||
let t1;
|
||||
if ($[2] !== local.b.c) {
|
||||
t1 = () => local.b.c;
|
||||
$[2] = local.b.c;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const fn = t1;
|
||||
let t2;
|
||||
if ($[4] !== fn) {
|
||||
t2 = <Stringify fn={fn} shouldInvokeFns={true} />;
|
||||
$[4] = fn;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ a: null }],
|
||||
sequentialRenders: [{ a: null }, { a: { b: { c: 4 } } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[ (exception in render) TypeError: Cannot read properties of undefined (reading 'c') ]]
|
||||
<div>{"fn":{"kind":"Function","result":4},"shouldInvokeFns":true}</div>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {mutate, shallowCopy, Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}: {a: {b: {c: number}}}) {
|
||||
const local = shallowCopy(a);
|
||||
mutate(local);
|
||||
const fn = () => local.b.c;
|
||||
return <Stringify fn={fn} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [{a: null}, {a: {b: {c: 4}}}],
|
||||
};
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {identity, makeArray, Stringify, useIdentity} from 'shared-runtime';
|
||||
|
||||
function Foo({a, cond}) {
|
||||
// Assume fn can be uncond evaluated, so we can safely evaluate a.b?.c.<any>
|
||||
const fn = () => [a, a.b?.c.d];
|
||||
useIdentity(null);
|
||||
const arr = makeArray();
|
||||
if (cond) {
|
||||
arr.push(identity(a.b?.c.e));
|
||||
}
|
||||
return <Stringify fn={fn} arr={arr} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{a: null, cond: true}],
|
||||
sequentialRenders: [
|
||||
{a: null, cond: true},
|
||||
{a: {b: {c: {d: 5}}}, cond: true},
|
||||
{a: {b: null}, cond: false},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
|
||||
import { identity, makeArray, Stringify, useIdentity } from "shared-runtime";
|
||||
|
||||
function Foo(t0) {
|
||||
const $ = _c(8);
|
||||
const { a, cond } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a) {
|
||||
t1 = () => [a, a.b?.c.d];
|
||||
$[0] = a;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const fn = t1;
|
||||
useIdentity(null);
|
||||
let arr;
|
||||
if ($[2] !== cond || $[3] !== a.b?.c.e) {
|
||||
arr = makeArray();
|
||||
if (cond) {
|
||||
arr.push(identity(a.b?.c.e));
|
||||
}
|
||||
$[2] = cond;
|
||||
$[3] = a.b?.c.e;
|
||||
$[4] = arr;
|
||||
} else {
|
||||
arr = $[4];
|
||||
}
|
||||
let t2;
|
||||
if ($[5] !== fn || $[6] !== arr) {
|
||||
t2 = <Stringify fn={fn} arr={arr} shouldInvokeFns={true} />;
|
||||
$[5] = fn;
|
||||
$[6] = arr;
|
||||
$[7] = t2;
|
||||
} else {
|
||||
t2 = $[7];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{ a: null, cond: true }],
|
||||
sequentialRenders: [
|
||||
{ a: null, cond: true },
|
||||
{ a: { b: { c: { d: 5 } } }, cond: true },
|
||||
{ a: { b: null }, cond: false },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[ (exception in render) TypeError: Cannot read properties of null (reading 'b') ]]
|
||||
<div>{"fn":{"kind":"Function","result":[{"b":{"c":{"d":5}}},5]},"arr":[null],"shouldInvokeFns":true}</div>
|
||||
<div>{"fn":{"kind":"Function","result":[{"b":null},null]},"arr":[],"shouldInvokeFns":true}</div>
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {identity, makeArray, Stringify, useIdentity} from 'shared-runtime';
|
||||
|
||||
function Foo({a, cond}) {
|
||||
// Assume fn can be uncond evaluated, so we can safely evaluate a.b?.c.<any>
|
||||
const fn = () => [a, a.b?.c.d];
|
||||
useIdentity(null);
|
||||
const arr = makeArray();
|
||||
if (cond) {
|
||||
arr.push(identity(a.b?.c.e));
|
||||
}
|
||||
return <Stringify fn={fn} arr={arr} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{a: null, cond: true}],
|
||||
sequentialRenders: [
|
||||
{a: null, cond: true},
|
||||
{a: {b: {c: {d: 5}}}, cond: true},
|
||||
{a: {b: null}, cond: false},
|
||||
],
|
||||
};
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {shallowCopy, Stringify, mutate} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}: {a: {b: {c: number}}}) {
|
||||
const local = shallowCopy(a);
|
||||
mutate(local);
|
||||
const fn = () => [() => local.b.c];
|
||||
return <Stringify fn={fn} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [{a: null}, {a: {b: {c: 4}}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
|
||||
import { shallowCopy, Stringify, mutate } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(6);
|
||||
const { a } = t0;
|
||||
let local;
|
||||
if ($[0] !== a) {
|
||||
local = shallowCopy(a);
|
||||
mutate(local);
|
||||
$[0] = a;
|
||||
$[1] = local;
|
||||
} else {
|
||||
local = $[1];
|
||||
}
|
||||
let t1;
|
||||
if ($[2] !== local.b.c) {
|
||||
t1 = () => [() => local.b.c];
|
||||
$[2] = local.b.c;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const fn = t1;
|
||||
let t2;
|
||||
if ($[4] !== fn) {
|
||||
t2 = <Stringify fn={fn} shouldInvokeFns={true} />;
|
||||
$[4] = fn;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ a: null }],
|
||||
sequentialRenders: [{ a: null }, { a: { b: { c: 4 } } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[ (exception in render) TypeError: Cannot read properties of undefined (reading 'c') ]]
|
||||
<div>{"fn":{"kind":"Function","result":[{"kind":"Function","result":4}]},"shouldInvokeFns":true}</div>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {shallowCopy, Stringify, mutate} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}: {a: {b: {c: number}}}) {
|
||||
const local = shallowCopy(a);
|
||||
mutate(local);
|
||||
const fn = () => [() => local.b.c];
|
||||
return <Stringify fn={fn} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [{a: null}, {a: {b: {c: 4}}}],
|
||||
};
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}) {
|
||||
const fn = () => {
|
||||
return () => ({
|
||||
value: a.b.c,
|
||||
});
|
||||
};
|
||||
return <Stringify fn={fn} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [{a: null}, {a: {b: {c: 4}}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(4);
|
||||
const { a } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a.b.c) {
|
||||
t1 = () => () => ({ value: a.b.c });
|
||||
$[0] = a.b.c;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const fn = t1;
|
||||
let t2;
|
||||
if ($[2] !== fn) {
|
||||
t2 = <Stringify fn={fn} shouldInvokeFns={true} />;
|
||||
$[2] = fn;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ a: null }],
|
||||
sequentialRenders: [{ a: null }, { a: { b: { c: 4 } } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[ (exception in render) TypeError: Cannot read properties of null (reading 'b') ]]
|
||||
<div>{"fn":{"kind":"Function","result":{"kind":"Function","result":{"value":4}}},"shouldInvokeFns":true}</div>
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}) {
|
||||
const fn = () => {
|
||||
return () => ({
|
||||
value: a.b.c,
|
||||
});
|
||||
};
|
||||
return <Stringify fn={fn} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [{a: null}, {a: {b: {c: 4}}}],
|
||||
};
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {identity, Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}) {
|
||||
const x = {
|
||||
fn() {
|
||||
return identity(a.b.c);
|
||||
},
|
||||
};
|
||||
return <Stringify x={x} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [{a: null}, {a: {b: {c: 4}}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
|
||||
import { identity, Stringify } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(4);
|
||||
const { a } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a.b.c) {
|
||||
t1 = {
|
||||
fn() {
|
||||
return identity(a.b.c);
|
||||
},
|
||||
};
|
||||
$[0] = a.b.c;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const x = t1;
|
||||
let t2;
|
||||
if ($[2] !== x) {
|
||||
t2 = <Stringify x={x} shouldInvokeFns={true} />;
|
||||
$[2] = x;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ a: null }],
|
||||
sequentialRenders: [{ a: null }, { a: { b: { c: 4 } } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[ (exception in render) TypeError: Cannot read properties of null (reading 'b') ]]
|
||||
<div>{"x":{"fn":{"kind":"Function","result":4}},"shouldInvokeFns":true}</div>
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {identity, Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}) {
|
||||
const x = {
|
||||
fn() {
|
||||
return identity(a.b.c);
|
||||
},
|
||||
};
|
||||
return <Stringify x={x} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [{a: null}, {a: {b: {c: 4}}}],
|
||||
};
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}) {
|
||||
return <Stringify fn={() => a.b?.c.d?.e} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [
|
||||
{a: null},
|
||||
{a: {b: null}},
|
||||
{a: {b: {c: {d: null}}}},
|
||||
{a: {b: {c: {d: {e: 4}}}}},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(2);
|
||||
const { a } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a.b) {
|
||||
t1 = <Stringify fn={() => a.b?.c.d?.e} shouldInvokeFns={true} />;
|
||||
$[0] = a.b;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ a: null }],
|
||||
sequentialRenders: [
|
||||
{ a: null },
|
||||
{ a: { b: null } },
|
||||
{ a: { b: { c: { d: null } } } },
|
||||
{ a: { b: { c: { d: { e: 4 } } } } },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[ (exception in render) TypeError: Cannot read properties of null (reading 'b') ]]
|
||||
<div>{"fn":{"kind":"Function"},"shouldInvokeFns":true}</div>
|
||||
<div>{"fn":{"kind":"Function"},"shouldInvokeFns":true}</div>
|
||||
<div>{"fn":{"kind":"Function","result":4},"shouldInvokeFns":true}</div>
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// @enablePropagateDepsInHIR
|
||||
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}) {
|
||||
return <Stringify fn={() => a.b?.c.d?.e} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [
|
||||
{a: null},
|
||||
{a: {b: null}},
|
||||
{a: {b: {c: {d: null}}}},
|
||||
{a: {b: {c: {d: {e: 4}}}}},
|
||||
],
|
||||
};
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Foo({a, shouldReadA}) {
|
||||
return (
|
||||
<Stringify
|
||||
fn={() => {
|
||||
if (shouldReadA) return a.b.c;
|
||||
return null;
|
||||
}}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{a: null, shouldReadA: true}],
|
||||
sequentialRenders: [
|
||||
{a: null, shouldReadA: true},
|
||||
{a: null, shouldReadA: false},
|
||||
{a: {b: {c: 4}}, shouldReadA: true},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Foo(t0) {
|
||||
const $ = _c(3);
|
||||
const { a, shouldReadA } = t0;
|
||||
let t1;
|
||||
if ($[0] !== shouldReadA || $[1] !== a.b.c) {
|
||||
t1 = (
|
||||
<Stringify
|
||||
fn={() => {
|
||||
if (shouldReadA) {
|
||||
return a.b.c;
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
$[0] = shouldReadA;
|
||||
$[1] = a.b.c;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{ a: null, shouldReadA: true }],
|
||||
sequentialRenders: [
|
||||
{ a: null, shouldReadA: true },
|
||||
{ a: null, shouldReadA: false },
|
||||
{ a: { b: { c: 4 } }, shouldReadA: true },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Foo({a, shouldReadA}) {
|
||||
return (
|
||||
<Stringify
|
||||
fn={() => {
|
||||
if (shouldReadA) return a.b.c;
|
||||
return null;
|
||||
}}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{a: null, shouldReadA: true}],
|
||||
sequentialRenders: [
|
||||
{a: null, shouldReadA: true},
|
||||
{a: null, shouldReadA: false},
|
||||
{a: {b: {c: 4}}, shouldReadA: true},
|
||||
],
|
||||
};
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}) {
|
||||
return <Stringify fn={() => a.b?.c.d?.e} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [
|
||||
{a: null},
|
||||
{a: {b: null}},
|
||||
{a: {b: {c: {d: null}}}},
|
||||
{a: {b: {c: {d: {e: 4}}}}},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(2);
|
||||
const { a } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a.b) {
|
||||
t1 = <Stringify fn={() => a.b?.c.d?.e} shouldInvokeFns={true} />;
|
||||
$[0] = a.b;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ a: null }],
|
||||
sequentialRenders: [
|
||||
{ a: null },
|
||||
{ a: { b: null } },
|
||||
{ a: { b: { c: { d: null } } } },
|
||||
{ a: { b: { c: { d: { e: 4 } } } } },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [[ (exception in render) TypeError: Cannot read properties of null (reading 'b') ]]
|
||||
<div>{"fn":{"kind":"Function"},"shouldInvokeFns":true}</div>
|
||||
<div>{"fn":{"kind":"Function"},"shouldInvokeFns":true}</div>
|
||||
<div>{"fn":{"kind":"Function","result":4},"shouldInvokeFns":true}</div>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function useFoo({a}) {
|
||||
return <Stringify fn={() => a.b?.c.d?.e} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: null}],
|
||||
sequentialRenders: [
|
||||
{a: null},
|
||||
{a: {b: null}},
|
||||
{a: {b: {c: {d: null}}}},
|
||||
{a: {b: {c: {d: {e: 4}}}}},
|
||||
],
|
||||
};
|
||||
@@ -479,6 +479,7 @@ const skipFilter = new Set([
|
||||
'fbt/bug-fbt-plural-multiple-mixed-call-tag',
|
||||
'bug-invalid-hoisting-functionexpr',
|
||||
'bug-try-catch-maybe-null-dependency',
|
||||
'reduce-reactive-deps/bug-infer-function-cond-access-not-hoisted',
|
||||
'reduce-reactive-deps/bug-merge-uncond-optional-chain-and-cond',
|
||||
'original-reactive-scopes-fork/bug-nonmutating-capture-in-unsplittable-memo-block',
|
||||
'original-reactive-scopes-fork/bug-hoisted-declaration-with-scope',
|
||||
|
||||
Reference in New Issue
Block a user