Compare commits
2 Commits
eslint-plu
...
gh/mofeiZ/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8ca7e361a | ||
|
|
88341cf22c |
@@ -7,6 +7,7 @@ import {
|
||||
Set_union,
|
||||
getOrInsertDefault,
|
||||
} from '../Utils/utils';
|
||||
import {collectOptionalChainSidemap} from './CollectOptionalChainDependencies';
|
||||
import {
|
||||
BasicBlock,
|
||||
BlockId,
|
||||
@@ -16,9 +17,11 @@ import {
|
||||
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 = {
|
||||
@@ -209,57 +241,77 @@ class PropertyPathRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
function addNonNullPropertyPath(
|
||||
source: Identifier,
|
||||
sourceNode: PropertyPathNode,
|
||||
instrId: InstructionId,
|
||||
knownImmutableIdentifiers: Set<IdentifierId>,
|
||||
result: Set<PropertyPathNode>,
|
||||
): void {
|
||||
/**
|
||||
* 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 =
|
||||
source.mutableRange.end > source.mutableRange.start + 1 &&
|
||||
source.scope != null &&
|
||||
inRange({id: instrId}, source.scope.range);
|
||||
if (
|
||||
!isMutableAtInstr ||
|
||||
knownImmutableIdentifiers.has(sourceNode.fullPath.identifier.id)
|
||||
) {
|
||||
result.add(sourceNode);
|
||||
function getMaybeNonNullInInstruction(
|
||||
instr: InstructionValue,
|
||||
context: CollectNonNullsInBlocksContext,
|
||||
): PropertyPathNode | null {
|
||||
let path = null;
|
||||
if (instr.kind === 'PropertyLoad') {
|
||||
path = context.temporaries.get(instr.object.identifier.id) ?? {
|
||||
identifier: instr.object.identifier,
|
||||
path: [],
|
||||
};
|
||||
} else if (instr.kind === 'Destructure') {
|
||||
path = context.temporaries.get(instr.value.identifier.id) ?? null;
|
||||
} else if (instr.kind === 'ComputedLoad') {
|
||||
path = context.temporaries.get(instr.object.identifier.id) ?? 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.
|
||||
@@ -271,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) {
|
||||
@@ -279,48 +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) {
|
||||
if (instr.value.kind === 'PropertyLoad') {
|
||||
const source = temporaries.get(instr.value.object.identifier.id) ?? {
|
||||
identifier: instr.value.object.identifier,
|
||||
path: [],
|
||||
};
|
||||
addNonNullPropertyPath(
|
||||
instr.value.object.identifier,
|
||||
registry.getOrCreateProperty(source),
|
||||
instr.id,
|
||||
knownImmutableIdentifiers,
|
||||
assumedNonNullObjects,
|
||||
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(),
|
||||
);
|
||||
} else if (instr.value.kind === 'Destructure') {
|
||||
const source = instr.value.value.identifier.id;
|
||||
const sourceNode = temporaries.get(source);
|
||||
if (sourceNode != null) {
|
||||
addNonNullPropertyPath(
|
||||
instr.value.value.identifier,
|
||||
registry.getOrCreateProperty(sourceNode),
|
||||
instr.id,
|
||||
knownImmutableIdentifiers,
|
||||
assumedNonNullObjects,
|
||||
);
|
||||
}
|
||||
} else if (instr.value.kind === 'ComputedLoad') {
|
||||
const source = instr.value.object.identifier.id;
|
||||
const sourceNode = temporaries.get(source);
|
||||
if (sourceNode != null) {
|
||||
addNonNullPropertyPath(
|
||||
instr.value.object.identifier,
|
||||
registry.getOrCreateProperty(sourceNode),
|
||||
instr.id,
|
||||
knownImmutableIdentifiers,
|
||||
assumedNonNullObjects,
|
||||
);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -528,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;
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {identity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Not safe to hoist read of maybeNullObject.value.inner outside of the
|
||||
* try-catch block, as that might throw
|
||||
*/
|
||||
function useFoo(maybeNullObject: {value: {inner: number}} | null) {
|
||||
const y = [];
|
||||
try {
|
||||
y.push(identity(maybeNullObject.value.inner));
|
||||
} catch {
|
||||
y.push('null');
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [null],
|
||||
sequentialRenders: [null, {value: 2}, {value: 3}, null],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { identity } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Not safe to hoist read of maybeNullObject.value.inner outside of the
|
||||
* try-catch block, as that might throw
|
||||
*/
|
||||
function useFoo(maybeNullObject) {
|
||||
const $ = _c(2);
|
||||
let y;
|
||||
if ($[0] !== maybeNullObject.value.inner) {
|
||||
y = [];
|
||||
try {
|
||||
y.push(identity(maybeNullObject.value.inner));
|
||||
} catch {
|
||||
y.push("null");
|
||||
}
|
||||
$[0] = maybeNullObject.value.inner;
|
||||
$[1] = y;
|
||||
} else {
|
||||
y = $[1];
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [null],
|
||||
sequentialRenders: [null, { value: 2 }, { value: 3 }, null],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import {identity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Not safe to hoist read of maybeNullObject.value.inner outside of the
|
||||
* try-catch block, as that might throw
|
||||
*/
|
||||
function useFoo(maybeNullObject: {value: {inner: number}} | null) {
|
||||
const y = [];
|
||||
try {
|
||||
y.push(identity(maybeNullObject.value.inner));
|
||||
} catch {
|
||||
y.push('null');
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [null],
|
||||
sequentialRenders: [null, {value: 2}, {value: 3}, null],
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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},
|
||||
],
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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},
|
||||
],
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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}}}],
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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},
|
||||
],
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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}}}],
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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},
|
||||
],
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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}}}],
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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}}}],
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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}}}],
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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}}}}},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enablePropagateDepsInHIR
|
||||
import {identity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Not safe to hoist read of maybeNullObject.value.inner outside of the
|
||||
* try-catch block, as that might throw
|
||||
*/
|
||||
function useFoo(maybeNullObject: {value: {inner: number}} | null) {
|
||||
const y = [];
|
||||
try {
|
||||
y.push(identity(maybeNullObject.value.inner));
|
||||
} catch {
|
||||
y.push('null');
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [null],
|
||||
sequentialRenders: [null, {value: 2}, {value: 3}, null],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
import { identity } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Not safe to hoist read of maybeNullObject.value.inner outside of the
|
||||
* try-catch block, as that might throw
|
||||
*/
|
||||
function useFoo(maybeNullObject) {
|
||||
const $ = _c(4);
|
||||
let y;
|
||||
if ($[0] !== maybeNullObject) {
|
||||
y = [];
|
||||
try {
|
||||
let t0;
|
||||
if ($[2] !== maybeNullObject.value.inner) {
|
||||
t0 = identity(maybeNullObject.value.inner);
|
||||
$[2] = maybeNullObject.value.inner;
|
||||
$[3] = t0;
|
||||
} else {
|
||||
t0 = $[3];
|
||||
}
|
||||
y.push(t0);
|
||||
} catch {
|
||||
y.push("null");
|
||||
}
|
||||
$[0] = maybeNullObject;
|
||||
$[1] = y;
|
||||
} else {
|
||||
y = $[1];
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [null],
|
||||
sequentialRenders: [null, { value: 2 }, { value: 3 }, null],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) ["null"]
|
||||
[null]
|
||||
[null]
|
||||
["null"]
|
||||
@@ -0,0 +1,23 @@
|
||||
// @enablePropagateDepsInHIR
|
||||
import {identity} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Not safe to hoist read of maybeNullObject.value.inner outside of the
|
||||
* try-catch block, as that might throw
|
||||
*/
|
||||
function useFoo(maybeNullObject: {value: {inner: number}} | null) {
|
||||
const y = [];
|
||||
try {
|
||||
y.push(identity(maybeNullObject.value.inner));
|
||||
} catch {
|
||||
y.push('null');
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [null],
|
||||
sequentialRenders: [null, {value: 2}, {value: 3}, null],
|
||||
};
|
||||
@@ -32,9 +32,9 @@ import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
const { throwInput } = require("shared-runtime");
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(2);
|
||||
const $ = _c(3);
|
||||
let x;
|
||||
if ($[0] !== props) {
|
||||
if ($[0] !== props.y || $[1] !== props.e) {
|
||||
try {
|
||||
const y = [];
|
||||
y.push(props.y);
|
||||
@@ -44,10 +44,11 @@ function Component(props) {
|
||||
e.push(props.e);
|
||||
x = e;
|
||||
}
|
||||
$[0] = props;
|
||||
$[1] = x;
|
||||
$[0] = props.y;
|
||||
$[1] = props.e;
|
||||
$[2] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
x = $[2];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
const { throwInput } = require("shared-runtime");
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(2);
|
||||
const $ = _c(3);
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
if ($[0] !== props.y || $[1] !== props.e) {
|
||||
t0 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
try {
|
||||
@@ -47,10 +47,11 @@ function Component(props) {
|
||||
break bb0;
|
||||
}
|
||||
}
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
$[0] = props.y;
|
||||
$[1] = props.e;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
t0 = $[2];
|
||||
}
|
||||
if (t0 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t0;
|
||||
|
||||
@@ -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 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
@@ -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},
|
||||
],
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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}}}}},
|
||||
],
|
||||
};
|
||||
@@ -478,6 +478,8 @@ const skipFilter = new Set([
|
||||
'fbt/bug-fbt-plural-multiple-function-calls',
|
||||
'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