Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f05deca4e | ||
|
|
75be876f2a | ||
|
|
7d696dc3b8 | ||
|
|
04a7a61918 | ||
|
|
c2326b1336 | ||
|
|
4395689980 | ||
|
|
6891dcb87d | ||
|
|
3f40eb73a8 | ||
|
|
1d7e942da7 |
@@ -608,7 +608,7 @@ export const EnvironmentConfigSchema = z.object({
|
||||
*
|
||||
* Here the variables `ref` and `myRef` will be typed as Refs.
|
||||
*/
|
||||
enableTreatRefLikeIdentifiersAsRefs: z.boolean().default(false),
|
||||
enableTreatRefLikeIdentifiersAsRefs: z.boolean().default(true),
|
||||
|
||||
/*
|
||||
* If specified a value, the compiler lowers any calls to `useContext` to use
|
||||
|
||||
@@ -114,6 +114,99 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
}),
|
||||
],
|
||||
[
|
||||
'entries',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [Effect.Capture],
|
||||
restParam: null,
|
||||
returnType: {kind: 'Object', shapeId: BuiltInArrayId},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
aliasing: {
|
||||
receiver: '@receiver',
|
||||
params: ['@object'],
|
||||
rest: null,
|
||||
returns: '@returns',
|
||||
temporaries: [],
|
||||
effects: [
|
||||
{
|
||||
kind: 'Create',
|
||||
into: '@returns',
|
||||
reason: ValueReason.KnownReturnSignature,
|
||||
value: ValueKind.Mutable,
|
||||
},
|
||||
// Object values are captured into the return
|
||||
{
|
||||
kind: 'Capture',
|
||||
from: '@object',
|
||||
into: '@returns',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
[
|
||||
'keys',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [Effect.Read],
|
||||
restParam: null,
|
||||
returnType: {kind: 'Object', shapeId: BuiltInArrayId},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
aliasing: {
|
||||
receiver: '@receiver',
|
||||
params: ['@object'],
|
||||
rest: null,
|
||||
returns: '@returns',
|
||||
temporaries: [],
|
||||
effects: [
|
||||
{
|
||||
kind: 'Create',
|
||||
into: '@returns',
|
||||
reason: ValueReason.KnownReturnSignature,
|
||||
value: ValueKind.Mutable,
|
||||
},
|
||||
// Only keys are captured, and keys are immutable
|
||||
{
|
||||
kind: 'ImmutableCapture',
|
||||
from: '@object',
|
||||
into: '@returns',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
[
|
||||
'values',
|
||||
addFunction(DEFAULT_SHAPES, [], {
|
||||
positionalParams: [Effect.Capture],
|
||||
restParam: null,
|
||||
returnType: {kind: 'Object', shapeId: BuiltInArrayId},
|
||||
calleeEffect: Effect.Read,
|
||||
returnValueKind: ValueKind.Mutable,
|
||||
aliasing: {
|
||||
receiver: '@receiver',
|
||||
params: ['@object'],
|
||||
rest: null,
|
||||
returns: '@returns',
|
||||
temporaries: [],
|
||||
effects: [
|
||||
{
|
||||
kind: 'Create',
|
||||
into: '@returns',
|
||||
reason: ValueReason.KnownReturnSignature,
|
||||
value: ValueKind.Mutable,
|
||||
},
|
||||
// Object values are captured into the return
|
||||
{
|
||||
kind: 'Capture',
|
||||
from: '@object',
|
||||
into: '@returns',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
]),
|
||||
],
|
||||
[
|
||||
|
||||
@@ -142,6 +142,7 @@ function parseAliasingSignatureConfig(
|
||||
const effects = typeConfig.effects.map(
|
||||
(effect: AliasingEffectConfig): AliasingEffect => {
|
||||
switch (effect.kind) {
|
||||
case 'ImmutableCapture':
|
||||
case 'CreateFrom':
|
||||
case 'Capture':
|
||||
case 'Alias':
|
||||
|
||||
@@ -111,6 +111,19 @@ export const AliasEffectSchema: z.ZodType<AliasEffectConfig> = z.object({
|
||||
into: LifetimeIdSchema,
|
||||
});
|
||||
|
||||
export type ImmutableCaptureEffectConfig = {
|
||||
kind: 'ImmutableCapture';
|
||||
from: string;
|
||||
into: string;
|
||||
};
|
||||
|
||||
export const ImmutableCaptureEffectSchema: z.ZodType<ImmutableCaptureEffectConfig> =
|
||||
z.object({
|
||||
kind: z.literal('ImmutableCapture'),
|
||||
from: LifetimeIdSchema,
|
||||
into: LifetimeIdSchema,
|
||||
});
|
||||
|
||||
export type CaptureEffectConfig = {
|
||||
kind: 'Capture';
|
||||
from: string;
|
||||
@@ -187,6 +200,7 @@ export type AliasingEffectConfig =
|
||||
| AssignEffectConfig
|
||||
| AliasEffectConfig
|
||||
| CaptureEffectConfig
|
||||
| ImmutableCaptureEffectConfig
|
||||
| ImpureEffectConfig
|
||||
| MutateEffectConfig
|
||||
| MutateTransitiveConditionallyConfig
|
||||
@@ -199,6 +213,7 @@ export const AliasingEffectSchema: z.ZodType<AliasingEffectConfig> = z.union([
|
||||
AssignEffectSchema,
|
||||
AliasEffectSchema,
|
||||
CaptureEffectSchema,
|
||||
ImmutableCaptureEffectSchema,
|
||||
ImpureEffectSchema,
|
||||
MutateEffectSchema,
|
||||
MutateTransitiveConditionallySchema,
|
||||
|
||||
@@ -119,6 +119,7 @@ class FindLastUsageVisitor extends ReactiveFunctionVisitor<void> {
|
||||
|
||||
class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | null> {
|
||||
lastUsage: Map<DeclarationId, InstructionId>;
|
||||
temporaries: Map<DeclarationId, DeclarationId> = new Map();
|
||||
|
||||
constructor(lastUsage: Map<DeclarationId, InstructionId>) {
|
||||
super();
|
||||
@@ -215,6 +216,12 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
|
||||
current.lvalues.add(
|
||||
instr.instruction.lvalue.identifier.declarationId,
|
||||
);
|
||||
if (instr.instruction.value.kind === 'LoadLocal') {
|
||||
this.temporaries.set(
|
||||
instr.instruction.lvalue.identifier.declarationId,
|
||||
instr.instruction.value.place.identifier.declarationId,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -236,6 +243,13 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
|
||||
)) {
|
||||
current.lvalues.add(lvalue.identifier.declarationId);
|
||||
}
|
||||
this.temporaries.set(
|
||||
instr.instruction.value.lvalue.place.identifier
|
||||
.declarationId,
|
||||
this.temporaries.get(
|
||||
instr.instruction.value.value.identifier.declarationId,
|
||||
) ?? instr.instruction.value.value.identifier.declarationId,
|
||||
);
|
||||
} else {
|
||||
log(
|
||||
`Reset scope @${current.block.scope.id} from StoreLocal in [${instr.instruction.id}]`,
|
||||
@@ -260,7 +274,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
|
||||
case 'scope': {
|
||||
if (
|
||||
current !== null &&
|
||||
canMergeScopes(current.block, instr) &&
|
||||
canMergeScopes(current.block, instr, this.temporaries) &&
|
||||
areLValuesLastUsedByScope(
|
||||
instr.scope,
|
||||
current.lvalues,
|
||||
@@ -426,6 +440,7 @@ function areLValuesLastUsedByScope(
|
||||
function canMergeScopes(
|
||||
current: ReactiveScopeBlock,
|
||||
next: ReactiveScopeBlock,
|
||||
temporaries: Map<DeclarationId, DeclarationId>,
|
||||
): boolean {
|
||||
// Don't merge scopes with reassignments
|
||||
if (
|
||||
@@ -465,11 +480,14 @@ function canMergeScopes(
|
||||
(next.scope.dependencies.size !== 0 &&
|
||||
[...next.scope.dependencies].every(
|
||||
dep =>
|
||||
dep.path.length === 0 &&
|
||||
isAlwaysInvalidatingType(dep.identifier.type) &&
|
||||
Iterable_some(
|
||||
current.scope.declarations.values(),
|
||||
decl =>
|
||||
decl.identifier.declarationId === dep.identifier.declarationId,
|
||||
decl.identifier.declarationId === dep.identifier.declarationId ||
|
||||
decl.identifier.declarationId ===
|
||||
temporaries.get(dep.identifier.declarationId),
|
||||
),
|
||||
))
|
||||
) {
|
||||
@@ -477,8 +495,12 @@ function canMergeScopes(
|
||||
return true;
|
||||
}
|
||||
log(` cannot merge scopes:`);
|
||||
log(` ${printReactiveScopeSummary(current.scope)}`);
|
||||
log(` ${printReactiveScopeSummary(next.scope)}`);
|
||||
log(
|
||||
` ${printReactiveScopeSummary(current.scope)} ${[...current.scope.declarations.values()].map(decl => decl.identifier.declarationId)}`,
|
||||
);
|
||||
log(
|
||||
` ${printReactiveScopeSummary(next.scope)} ${[...next.scope.dependencies].map(dep => `${dep.identifier.declarationId} ${temporaries.get(dep.identifier.declarationId) ?? dep.identifier.declarationId}`)}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
eachTerminalOperand,
|
||||
} from '../HIR/visitors';
|
||||
import {Err, Ok, Result} from '../Utils/Result';
|
||||
import {retainWhere} from '../Utils/utils';
|
||||
|
||||
/**
|
||||
* Validates that a function does not access a ref value during render. This includes a partial check
|
||||
@@ -79,8 +80,18 @@ type RefAccessRefType =
|
||||
|
||||
type RefFnType = {readRefEffect: boolean; returnType: RefAccessType};
|
||||
|
||||
class Env extends Map<IdentifierId, RefAccessType> {
|
||||
class Env {
|
||||
#changed = false;
|
||||
#data: Map<IdentifierId, RefAccessType> = new Map();
|
||||
#temporaries: Map<IdentifierId, Place> = new Map();
|
||||
|
||||
lookup(place: Place): Place {
|
||||
return this.#temporaries.get(place.identifier.id) ?? place;
|
||||
}
|
||||
|
||||
define(place: Place, value: Place): void {
|
||||
this.#temporaries.set(place.identifier.id, value);
|
||||
}
|
||||
|
||||
resetChanged(): void {
|
||||
this.#changed = false;
|
||||
@@ -90,8 +101,14 @@ class Env extends Map<IdentifierId, RefAccessType> {
|
||||
return this.#changed;
|
||||
}
|
||||
|
||||
override set(key: IdentifierId, value: RefAccessType): this {
|
||||
const cur = this.get(key);
|
||||
get(key: IdentifierId): RefAccessType | undefined {
|
||||
const operandId = this.#temporaries.get(key)?.identifier.id ?? key;
|
||||
return this.#data.get(operandId);
|
||||
}
|
||||
|
||||
set(key: IdentifierId, value: RefAccessType): this {
|
||||
const operandId = this.#temporaries.get(key)?.identifier.id ?? key;
|
||||
const cur = this.#data.get(operandId);
|
||||
const widenedValue = joinRefAccessTypes(value, cur ?? {kind: 'None'});
|
||||
if (
|
||||
!(cur == null && widenedValue.kind === 'None') &&
|
||||
@@ -99,7 +116,8 @@ class Env extends Map<IdentifierId, RefAccessType> {
|
||||
) {
|
||||
this.#changed = true;
|
||||
}
|
||||
return super.set(key, widenedValue);
|
||||
this.#data.set(operandId, widenedValue);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,9 +125,48 @@ export function validateNoRefAccessInRender(
|
||||
fn: HIRFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const env = new Env();
|
||||
collectTemporariesSidemap(fn, env);
|
||||
return validateNoRefAccessInRenderImpl(fn, env).map(_ => undefined);
|
||||
}
|
||||
|
||||
function collectTemporariesSidemap(fn: HIRFunction, env: Env): void {
|
||||
for (const block of fn.body.blocks.values()) {
|
||||
for (const instr of block.instructions) {
|
||||
const {lvalue, value} = instr;
|
||||
switch (value.kind) {
|
||||
case 'LoadLocal': {
|
||||
const temp = env.lookup(value.place);
|
||||
if (temp != null) {
|
||||
env.define(lvalue, temp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'StoreLocal': {
|
||||
const temp = env.lookup(value.value);
|
||||
if (temp != null) {
|
||||
env.define(lvalue, temp);
|
||||
env.define(value.lvalue.place, temp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'PropertyLoad': {
|
||||
if (
|
||||
isUseRefType(value.object.identifier) &&
|
||||
value.property === 'current'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const temp = env.lookup(value.object);
|
||||
if (temp != null) {
|
||||
env.define(lvalue, temp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function refTypeOfType(place: Place): RefAccessType {
|
||||
if (isRefValueType(place.identifier)) {
|
||||
return {kind: 'RefValue'};
|
||||
@@ -279,9 +336,10 @@ function validateNoRefAccessInRenderImpl(
|
||||
for (let i = 0; (i == 0 || env.hasChanged()) && i < 10; i++) {
|
||||
env.resetChanged();
|
||||
returnValues = [];
|
||||
const safeBlocks = new Map<BlockId, RefId>();
|
||||
const safeBlocks: Array<{block: BlockId; ref: RefId}> = [];
|
||||
const errors = new CompilerError();
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
retainWhere(safeBlocks, entry => entry.block !== block.id);
|
||||
for (const phi of block.phis) {
|
||||
env.set(
|
||||
phi.place.identifier.id,
|
||||
@@ -434,13 +492,14 @@ function validateNoRefAccessInRenderImpl(
|
||||
*/
|
||||
if (
|
||||
isRefLValue ||
|
||||
interpolatedAsJsx.has(instr.lvalue.identifier.id) ||
|
||||
hookKind != null
|
||||
(hookKind != null &&
|
||||
hookKind !== 'useState' &&
|
||||
hookKind !== 'useReducer')
|
||||
) {
|
||||
/**
|
||||
* Special cases:
|
||||
*
|
||||
* 1) the lvalue is a ref
|
||||
* 1. the lvalue is a ref
|
||||
* In general passing a ref to a function may access that ref
|
||||
* value during render, so we disallow it.
|
||||
*
|
||||
@@ -452,18 +511,21 @@ function validateNoRefAccessInRenderImpl(
|
||||
*
|
||||
* Eg `const mergedRef = mergeRefs(ref1, ref2)`
|
||||
*
|
||||
* 2) the lvalue is passed as a jsx child
|
||||
*
|
||||
* For example `<Foo>{renderHelper(ref)}</Foo>`. Here we have more
|
||||
* context and infer that the ref is being passed to a component-like
|
||||
* render function which attempts to obey the rules.
|
||||
*
|
||||
* 3) hooks
|
||||
* 2. calling hooks
|
||||
*
|
||||
* Hooks are independently checked to ensure they don't access refs
|
||||
* during render.
|
||||
*/
|
||||
validateNoDirectRefValueAccess(errors, operand, env);
|
||||
} else if (interpolatedAsJsx.has(instr.lvalue.identifier.id)) {
|
||||
/**
|
||||
* Special case: the lvalue is passed as a jsx child
|
||||
*
|
||||
* For example `<Foo>{renderHelper(ref)}</Foo>`. Here we have more
|
||||
* context and infer that the ref is being passed to a component-like
|
||||
* render function which attempts to obey the rules.
|
||||
*/
|
||||
validateNoRefValueAccess(errors, env, operand);
|
||||
} else {
|
||||
validateNoRefPassedToFunction(
|
||||
errors,
|
||||
@@ -504,23 +566,39 @@ function validateNoRefAccessInRenderImpl(
|
||||
case 'PropertyStore':
|
||||
case 'ComputedDelete':
|
||||
case 'ComputedStore': {
|
||||
const safe = safeBlocks.get(block.id);
|
||||
const target = env.get(instr.value.object.identifier.id);
|
||||
let safe: (typeof safeBlocks)['0'] | null | undefined = null;
|
||||
if (
|
||||
instr.value.kind === 'PropertyStore' &&
|
||||
safe != null &&
|
||||
target?.kind === 'Ref' &&
|
||||
target.refId === safe
|
||||
target != null &&
|
||||
target.kind === 'Ref'
|
||||
) {
|
||||
safeBlocks.delete(block.id);
|
||||
safe = safeBlocks.find(entry => entry.ref === target.refId);
|
||||
}
|
||||
if (safe != null) {
|
||||
retainWhere(safeBlocks, entry => entry !== safe);
|
||||
} else {
|
||||
validateNoRefUpdate(errors, env, instr.value.object, instr.loc);
|
||||
}
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
if (operand === instr.value.object) {
|
||||
continue;
|
||||
if (
|
||||
instr.value.kind === 'ComputedDelete' ||
|
||||
instr.value.kind === 'ComputedStore'
|
||||
) {
|
||||
validateNoRefValueAccess(errors, env, instr.value.property);
|
||||
}
|
||||
if (
|
||||
instr.value.kind === 'ComputedStore' ||
|
||||
instr.value.kind === 'PropertyStore'
|
||||
) {
|
||||
validateNoDirectRefValueAccess(errors, instr.value.value, env);
|
||||
const type = env.get(instr.value.value.identifier.id);
|
||||
if (type != null && type.kind === 'Structure') {
|
||||
let objectType: RefAccessType = type;
|
||||
if (target != null) {
|
||||
objectType = joinRefAccessTypes(objectType, target);
|
||||
}
|
||||
env.set(instr.value.object.identifier.id, objectType);
|
||||
}
|
||||
validateNoRefValueAccess(errors, env, operand);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -600,8 +678,11 @@ function validateNoRefAccessInRenderImpl(
|
||||
|
||||
if (block.terminal.kind === 'if') {
|
||||
const test = env.get(block.terminal.test.identifier.id);
|
||||
if (test?.kind === 'Guard') {
|
||||
safeBlocks.set(block.terminal.consequent, test.refId);
|
||||
if (
|
||||
test?.kind === 'Guard' &&
|
||||
safeBlocks.find(entry => entry.ref === test.refId) == null
|
||||
) {
|
||||
safeBlocks.push({block: block.terminal.fallthrough, ref: test.refId});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,11 +800,7 @@ function validateNoRefUpdate(
|
||||
loc: SourceLocation,
|
||||
): void {
|
||||
const type = destructure(env.get(operand.identifier.id));
|
||||
if (
|
||||
type?.kind === 'Ref' ||
|
||||
type?.kind === 'RefValue' ||
|
||||
(type?.kind === 'Structure' && type.fn?.readRefEffect)
|
||||
) {
|
||||
if (type?.kind === 'Ref' || type?.kind === 'RefValue') {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useRef} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(props.value);
|
||||
const object = {};
|
||||
object.foo = () => ref.current;
|
||||
return <Stringify object={object} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useRef } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(1);
|
||||
const ref = useRef(props.value);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const object = {};
|
||||
object.foo = () => ref.current;
|
||||
t0 = <Stringify object={object} shouldInvokeFns={true} />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: 42 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"object":{"foo":{"kind":"Function","result":42}},"shouldInvokeFns":true}</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
import {useRef} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(props.value);
|
||||
const object = {};
|
||||
object.foo = () => ref.current;
|
||||
return <Stringify object={object} shouldInvokeFns={true} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
};
|
||||
@@ -27,6 +27,7 @@ function Component() {
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
@@ -100,8 +101,10 @@ function Component() {
|
||||
return t6;
|
||||
}
|
||||
|
||||
function Child(t0) {
|
||||
const { ref } = t0;
|
||||
function Child({ ref }) {
|
||||
"use no memo";
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ function Component() {
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
|
||||
@@ -23,6 +23,7 @@ function Component() {
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
@@ -86,8 +87,10 @@ function Component() {
|
||||
return t5;
|
||||
}
|
||||
|
||||
function Child(t0) {
|
||||
const { ref } = t0;
|
||||
function Child({ ref }) {
|
||||
"use no memo";
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ function Component() {
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
|
||||
@@ -25,6 +25,7 @@ function Component() {
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
@@ -83,8 +84,10 @@ function Component() {
|
||||
}
|
||||
function _temp() {}
|
||||
|
||||
function Child(t0) {
|
||||
const { ref } = t0;
|
||||
function Child({ ref }) {
|
||||
"use no memo";
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ function Component() {
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateRefAccessDuringRender
|
||||
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(null);
|
||||
if (ref.current == null) {
|
||||
// the logical means the ref write is in a different block
|
||||
// from the if consequent. this tests that the "safe" blocks
|
||||
// extend up to the if's fallthrough
|
||||
ref.current = props.unknownKey ?? props.value;
|
||||
}
|
||||
return <Child ref={ref} />;
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender
|
||||
|
||||
import { useRef } from "react";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(1);
|
||||
const ref = useRef(null);
|
||||
if (ref.current == null) {
|
||||
ref.current = props.unknownKey ?? props.value;
|
||||
}
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <Child ref={ref} />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
function Child({ ref }) {
|
||||
"use no memo";
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: 42 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) 42
|
||||
@@ -0,0 +1,24 @@
|
||||
// @validateRefAccessDuringRender
|
||||
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(null);
|
||||
if (ref.current == null) {
|
||||
// the logical means the ref write is in a different block
|
||||
// from the if consequent. this tests that the "safe" blocks
|
||||
// extend up to the if's fallthrough
|
||||
ref.current = props.unknownKey ?? props.value;
|
||||
}
|
||||
return <Child ref={ref} />;
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
};
|
||||
@@ -19,7 +19,7 @@ function Component(props) {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(7);
|
||||
const $ = _c(5);
|
||||
let t0;
|
||||
if ($[0] !== props.x) {
|
||||
t0 = foo(props.x);
|
||||
@@ -31,26 +31,19 @@ function Component(props) {
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[2] !== props || $[3] !== x) {
|
||||
t1 = function () {
|
||||
const fn = function () {
|
||||
const arr = [...bar(props)];
|
||||
return arr.at(x);
|
||||
};
|
||||
|
||||
t1 = fn();
|
||||
$[2] = props;
|
||||
$[3] = x;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
const fn = t1;
|
||||
let t2;
|
||||
if ($[5] !== fn) {
|
||||
t2 = fn();
|
||||
$[5] = fn;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
const fnResult = t2;
|
||||
const fnResult = t1;
|
||||
return fnResult;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,34 +23,18 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(6);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.a) {
|
||||
t0 = { a: props.a };
|
||||
const item = { a: props.a };
|
||||
const items = [item];
|
||||
t0 = items.map(_temp);
|
||||
$[0] = props.a;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const item = t0;
|
||||
let t1;
|
||||
if ($[2] !== item) {
|
||||
t1 = [item];
|
||||
$[2] = item;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const items = t1;
|
||||
let t2;
|
||||
if ($[4] !== items) {
|
||||
t2 = items.map(_temp);
|
||||
$[4] = items;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
const mapped = t2;
|
||||
const mapped = t0;
|
||||
return mapped;
|
||||
}
|
||||
function _temp(item_0) {
|
||||
|
||||
@@ -21,26 +21,18 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
const f = _temp;
|
||||
let t0;
|
||||
if ($[0] !== props.items) {
|
||||
t0 = [...props.items].map(f);
|
||||
const x = [...props.items].map(f);
|
||||
t0 = [x, f];
|
||||
$[0] = props.items;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[2] !== x) {
|
||||
t1 = [x, f];
|
||||
$[2] = x;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
function _temp(item) {
|
||||
return item;
|
||||
|
||||
@@ -23,27 +23,19 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function component(a) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== a) {
|
||||
t0 = { a };
|
||||
const z = { a };
|
||||
t0 = () => {
|
||||
console.log(z);
|
||||
};
|
||||
$[0] = a;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const z = t0;
|
||||
let t1;
|
||||
if ($[2] !== z) {
|
||||
t1 = () => {
|
||||
console.log(z);
|
||||
};
|
||||
$[2] = z;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const x = t1;
|
||||
const x = t0;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,27 +23,19 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function component(a) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== a) {
|
||||
t0 = { a };
|
||||
const z = { a };
|
||||
t0 = function () {
|
||||
console.log(z);
|
||||
};
|
||||
$[0] = a;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const z = t0;
|
||||
let t1;
|
||||
if ($[2] !== z) {
|
||||
t1 = function () {
|
||||
console.log(z);
|
||||
};
|
||||
$[2] = z;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const x = t1;
|
||||
const x = t0;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,35 +22,19 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
function Component(t0) {
|
||||
const $ = _c(6);
|
||||
const $ = _c(2);
|
||||
const { a } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a) {
|
||||
t1 = { a };
|
||||
const z = { a };
|
||||
const p = () => <Stringify>{z}</Stringify>;
|
||||
t1 = p();
|
||||
$[0] = a;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const z = t1;
|
||||
let t2;
|
||||
if ($[2] !== z) {
|
||||
t2 = () => <Stringify>{z}</Stringify>;
|
||||
$[2] = z;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
const p = t2;
|
||||
let t3;
|
||||
if ($[4] !== p) {
|
||||
t3 = p();
|
||||
$[4] = p;
|
||||
$[5] = t3;
|
||||
} else {
|
||||
t3 = $[5];
|
||||
}
|
||||
return t3;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -25,27 +25,19 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function component(a) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== a) {
|
||||
t0 = { a };
|
||||
const z = { a };
|
||||
t0 = function () {
|
||||
console.log(z);
|
||||
};
|
||||
$[0] = a;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const z = t0;
|
||||
let t1;
|
||||
if ($[2] !== z) {
|
||||
t1 = function () {
|
||||
console.log(z);
|
||||
};
|
||||
$[2] = z;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const x = t1;
|
||||
const x = t0;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,29 +25,21 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function component(a) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== a) {
|
||||
t0 = { a };
|
||||
const z = { a };
|
||||
t0 = function () {
|
||||
(function () {
|
||||
console.log(z);
|
||||
})();
|
||||
};
|
||||
$[0] = a;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const z = t0;
|
||||
let t1;
|
||||
if ($[2] !== z) {
|
||||
t1 = function () {
|
||||
(function () {
|
||||
console.log(z);
|
||||
})();
|
||||
};
|
||||
$[2] = z;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const x = t1;
|
||||
const x = t0;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useReducer, useRef} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(props.value);
|
||||
const [state] = useReducer(
|
||||
(state, action) => state + action,
|
||||
0,
|
||||
init => ref.current
|
||||
);
|
||||
|
||||
return <Stringify state={state} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-access-ref-in-reducer-init.ts:8:4
|
||||
6 | (state, action) => state + action,
|
||||
7 | 0,
|
||||
> 8 | init => ref.current
|
||||
| ^^^^^^^^^^^^^^^^^^^ Passing a ref to a function may read its value during render
|
||||
9 | );
|
||||
10 |
|
||||
11 | return <Stringify state={state} />;
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import {useReducer, useRef} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(props.value);
|
||||
const [state] = useReducer(
|
||||
(state, action) => state + action,
|
||||
0,
|
||||
init => ref.current
|
||||
);
|
||||
|
||||
return <Stringify state={state} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useReducer, useRef} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(props.value);
|
||||
const [state] = useReducer(() => ref.current, null);
|
||||
|
||||
return <Stringify state={state} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-access-ref-in-reducer.ts:5:29
|
||||
3 | function Component(props) {
|
||||
4 | const ref = useRef(props.value);
|
||||
> 5 | const [state] = useReducer(() => ref.current, null);
|
||||
| ^^^^^^^^^^^^^^^^^ Passing a ref to a function may read its value during render
|
||||
6 |
|
||||
7 | return <Stringify state={state} />;
|
||||
8 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import {useReducer, useRef} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(props.value);
|
||||
const [state] = useReducer(() => ref.current, null);
|
||||
|
||||
return <Stringify state={state} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component() {
|
||||
const ref = useRef(null);
|
||||
const object = {};
|
||||
object.foo = () => ref.current;
|
||||
const refValue = object.foo();
|
||||
return <div>{refValue}</div>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-access-ref-in-render-mutate-object-with-ref-function.ts:7:19
|
||||
5 | const object = {};
|
||||
6 | object.foo = () => ref.current;
|
||||
> 7 | const refValue = object.foo();
|
||||
| ^^^^^^^^^^ This function accesses a ref value
|
||||
8 | return <div>{refValue}</div>;
|
||||
9 | }
|
||||
10 |
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component() {
|
||||
const ref = useRef(null);
|
||||
const object = {};
|
||||
object.foo = () => ref.current;
|
||||
const refValue = object.foo();
|
||||
return <div>{refValue}</div>;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useRef, useState} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(props.value);
|
||||
const [state] = useState(() => ref.current);
|
||||
|
||||
return <Stringify state={state} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-access-ref-in-state-initializer.ts:5:27
|
||||
3 | function Component(props) {
|
||||
4 | const ref = useRef(props.value);
|
||||
> 5 | const [state] = useState(() => ref.current);
|
||||
| ^^^^^^^^^^^^^^^^^ Passing a ref to a function may read its value during render
|
||||
6 |
|
||||
7 | return <Stringify state={state} />;
|
||||
8 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import {useRef, useState} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(props.value);
|
||||
const [state] = useState(() => ref.current);
|
||||
|
||||
return <Stringify state={state} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
};
|
||||
@@ -29,7 +29,7 @@ error.invalid-aliased-ref-in-callback-invoked-during-render-.ts:9:33
|
||||
7 | return <Foo item={item} current={current} />;
|
||||
8 | };
|
||||
> 9 | return <Items>{props.items.map(item => renderItem(item))}</Items>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ Passing a ref to a function may read its value during render
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ Cannot access ref value during render
|
||||
10 | }
|
||||
11 |
|
||||
```
|
||||
|
||||
@@ -28,7 +28,7 @@ error.invalid-ref-in-callback-invoked-during-render.ts:8:33
|
||||
6 | return <Foo item={item} current={current} />;
|
||||
7 | };
|
||||
> 8 | return <Items>{props.items.map(item => renderItem(item))}</Items>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ Passing a ref to a function may read its value during render
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ Cannot access ref value during render
|
||||
9 | }
|
||||
10 |
|
||||
```
|
||||
|
||||
@@ -41,14 +41,13 @@ Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-use-ref-added-to-dep-without-type-info.ts:10:21
|
||||
8 | // however, this is an instance of accessing a ref during render and is disallowed
|
||||
9 | // under React's rules, so we reject this input
|
||||
> 10 | const x = {a, val: val.ref.current};
|
||||
| ^^^^^^^^^^^^^^^ Cannot access ref value during render
|
||||
error.invalid-use-ref-added-to-dep-without-type-info.ts:12:28
|
||||
10 | const x = {a, val: val.ref.current};
|
||||
11 |
|
||||
12 | return <VideoList videos={x} />;
|
||||
> 12 | return <VideoList videos={x} />;
|
||||
| ^ Cannot access ref value during render
|
||||
13 | }
|
||||
14 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify, identity, mutate, CONST_TRUE} from 'shared-runtime';
|
||||
|
||||
function Foo(props, ref) {
|
||||
const value = {};
|
||||
if (CONST_TRUE) {
|
||||
mutate(value);
|
||||
return <Stringify ref={ref} />;
|
||||
}
|
||||
mutate(value);
|
||||
if (CONST_TRUE) {
|
||||
return <Stringify ref={identity(ref)} />;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}, {current: 'fake-ref-object'}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.repro-ref-mutable-range.ts:11:36
|
||||
9 | mutate(value);
|
||||
10 | if (CONST_TRUE) {
|
||||
> 11 | return <Stringify ref={identity(ref)} />;
|
||||
| ^^^ Passing a ref to a function may read its value during render
|
||||
12 | }
|
||||
13 | return value;
|
||||
14 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {makeObject_Primitives, Stringify} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const object = {object: props.object};
|
||||
const entries = useMemo(() => Object.entries(object), [object]);
|
||||
entries.map(([, value]) => {
|
||||
value.updated = true;
|
||||
});
|
||||
return <Stringify entries={entries} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{object: {key: makeObject_Primitives()}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This dependency may be mutated later, which could cause the value to change unexpectedly.
|
||||
|
||||
error.validate-object-entries-mutation.ts:6:57
|
||||
4 | function Component(props) {
|
||||
5 | const object = {object: props.object};
|
||||
> 6 | const entries = useMemo(() => Object.entries(object), [object]);
|
||||
| ^^^^^^ This dependency may be modified later
|
||||
7 | entries.map(([, value]) => {
|
||||
8 | value.updated = true;
|
||||
9 | });
|
||||
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output.
|
||||
|
||||
error.validate-object-entries-mutation.ts:6:18
|
||||
4 | function Component(props) {
|
||||
5 | const object = {object: props.object};
|
||||
> 6 | const entries = useMemo(() => Object.entries(object), [object]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Could not preserve existing memoization
|
||||
7 | entries.map(([, value]) => {
|
||||
8 | value.updated = true;
|
||||
9 | });
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {makeObject_Primitives, Stringify} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const object = {object: props.object};
|
||||
const entries = useMemo(() => Object.entries(object), [object]);
|
||||
entries.map(([, value]) => {
|
||||
value.updated = true;
|
||||
});
|
||||
return <Stringify entries={entries} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{object: {key: makeObject_Primitives()}}],
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {makeObject_Primitives, Stringify} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const object = {object: props.object};
|
||||
const values = useMemo(() => Object.values(object), [object]);
|
||||
values.map(value => {
|
||||
value.updated = true;
|
||||
});
|
||||
return <Stringify values={values} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{object: {key: makeObject_Primitives()}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This dependency may be mutated later, which could cause the value to change unexpectedly.
|
||||
|
||||
error.validate-object-values-mutation.ts:6:55
|
||||
4 | function Component(props) {
|
||||
5 | const object = {object: props.object};
|
||||
> 6 | const values = useMemo(() => Object.values(object), [object]);
|
||||
| ^^^^^^ This dependency may be modified later
|
||||
7 | values.map(value => {
|
||||
8 | value.updated = true;
|
||||
9 | });
|
||||
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output.
|
||||
|
||||
error.validate-object-values-mutation.ts:6:17
|
||||
4 | function Component(props) {
|
||||
5 | const object = {object: props.object};
|
||||
> 6 | const values = useMemo(() => Object.values(object), [object]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Could not preserve existing memoization
|
||||
7 | values.map(value => {
|
||||
8 | value.updated = true;
|
||||
9 | });
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {makeObject_Primitives, Stringify} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const object = {object: props.object};
|
||||
const values = useMemo(() => Object.values(object), [object]);
|
||||
values.map(value => {
|
||||
value.updated = true;
|
||||
});
|
||||
return <Stringify values={values} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{object: {key: makeObject_Primitives()}}],
|
||||
};
|
||||
@@ -40,36 +40,29 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(7);
|
||||
const $ = _c(5);
|
||||
let t0;
|
||||
if ($[0] !== props.a) {
|
||||
t0 = [props.a];
|
||||
const a = [props.a];
|
||||
|
||||
t0 = [a];
|
||||
$[0] = props.a;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const a = t0;
|
||||
let t1;
|
||||
if ($[2] !== a) {
|
||||
t1 = [a];
|
||||
$[2] = a;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const b = t1;
|
||||
const b = t0;
|
||||
let c;
|
||||
if ($[4] !== b || $[5] !== props.b) {
|
||||
if ($[2] !== b || $[3] !== props.b) {
|
||||
c = [];
|
||||
const d = {};
|
||||
d.b = b;
|
||||
c.push(props.b);
|
||||
$[4] = b;
|
||||
$[5] = props.b;
|
||||
$[6] = c;
|
||||
$[2] = b;
|
||||
$[3] = props.b;
|
||||
$[4] = c;
|
||||
} else {
|
||||
c = $[6];
|
||||
c = $[4];
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -32,10 +32,10 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import fbt from "fbt";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.name) {
|
||||
t0 = fbt._(
|
||||
const element = fbt._(
|
||||
"Hello {a really long description that got split into multiple lines}",
|
||||
[
|
||||
fbt._param(
|
||||
@@ -46,21 +46,14 @@ function Component(props) {
|
||||
],
|
||||
{ hk: "1euPUp" },
|
||||
);
|
||||
|
||||
t0 = element.toString();
|
||||
$[0] = props.name;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const element = t0;
|
||||
let t1;
|
||||
if ($[2] !== element) {
|
||||
t1 = element.toString();
|
||||
$[2] = element;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -27,27 +27,28 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import fbt from "fbt";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.name) {
|
||||
t0 = fbt._('Hello {"user" name}', [fbt._param('"user" name', props.name)], {
|
||||
hk: "S0vMe",
|
||||
});
|
||||
const element = fbt._(
|
||||
'Hello {"user" name}',
|
||||
[
|
||||
fbt._param(
|
||||
'"user" name',
|
||||
|
||||
props.name,
|
||||
),
|
||||
],
|
||||
{ hk: "S0vMe" },
|
||||
);
|
||||
|
||||
t0 = element.toString();
|
||||
$[0] = props.name;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const element = t0;
|
||||
let t1;
|
||||
if ($[2] !== element) {
|
||||
t1 = element.toString();
|
||||
$[2] = element;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -27,29 +27,28 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import fbt from "fbt";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.name) {
|
||||
t0 = fbt._(
|
||||
const element = fbt._(
|
||||
"Hello {user name ☺}",
|
||||
[fbt._param("user name \u263A", props.name)],
|
||||
[
|
||||
fbt._param(
|
||||
"user name \u263A",
|
||||
|
||||
props.name,
|
||||
),
|
||||
],
|
||||
{ hk: "1En1lp" },
|
||||
);
|
||||
|
||||
t0 = element.toString();
|
||||
$[0] = props.name;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const element = t0;
|
||||
let t1;
|
||||
if ($[2] !== element) {
|
||||
t1 = element.toString();
|
||||
$[2] = element;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -27,27 +27,28 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import fbt from "fbt";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.name) {
|
||||
t0 = fbt._("Hello {user name}", [fbt._param("user name", props.name)], {
|
||||
hk: "2zEDKF",
|
||||
});
|
||||
const element = fbt._(
|
||||
"Hello {user name}",
|
||||
[
|
||||
fbt._param(
|
||||
"user name",
|
||||
|
||||
props.name,
|
||||
),
|
||||
],
|
||||
{ hk: "2zEDKF" },
|
||||
);
|
||||
|
||||
t0 = element.toString();
|
||||
$[0] = props.name;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const element = t0;
|
||||
let t1;
|
||||
if ($[2] !== element) {
|
||||
t1 = element.toString();
|
||||
$[2] = element;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -22,28 +22,21 @@ function Component(props) {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
const id = useSelectedEntitytId();
|
||||
let t0;
|
||||
if ($[0] !== id) {
|
||||
t0 = () => {
|
||||
const onLoad = () => {
|
||||
log(id);
|
||||
};
|
||||
|
||||
t0 = <Foo onLoad={onLoad} />;
|
||||
$[0] = id;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const onLoad = t0;
|
||||
let t1;
|
||||
if ($[2] !== onLoad) {
|
||||
t1 = <Foo onLoad={onLoad} />;
|
||||
$[2] = onLoad;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -21,27 +21,20 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
t0 = function () {
|
||||
const f = function () {
|
||||
return <div>{props.name}</div>;
|
||||
};
|
||||
|
||||
t0 = f.call();
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const f = t0;
|
||||
let t1;
|
||||
if ($[2] !== f) {
|
||||
t1 = f.call();
|
||||
$[2] = f;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -55,33 +55,26 @@ import { Stringify } from "shared-runtime";
|
||||
* (kind: exception) Cannot read properties of null (reading 'prop')
|
||||
*/
|
||||
function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const $ = _c(3);
|
||||
const { obj, isObjNull } = t0;
|
||||
let t1;
|
||||
if ($[0] !== isObjNull || $[1] !== obj) {
|
||||
t1 = () => {
|
||||
const callback = () => {
|
||||
if (!isObjNull) {
|
||||
return obj.prop;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
t1 = <Stringify shouldInvokeFns={true} callback={callback} />;
|
||||
$[0] = isObjNull;
|
||||
$[1] = obj;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
const callback = t1;
|
||||
let t2;
|
||||
if ($[3] !== callback) {
|
||||
t2 = <Stringify shouldInvokeFns={true} callback={callback} />;
|
||||
$[3] = callback;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
return t2;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -27,7 +27,7 @@ function useFoo() {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
function useFoo() {
|
||||
const $ = _c(9);
|
||||
const $ = _c(7);
|
||||
const onClick = (response) => {
|
||||
setState(DISABLED_FORM);
|
||||
};
|
||||
@@ -48,31 +48,24 @@ function useFoo() {
|
||||
const handleLogout = t1;
|
||||
let t2;
|
||||
if ($[2] !== handleLogout) {
|
||||
t2 = () => <ColumnItem onPress={() => handleLogout()} />;
|
||||
const getComponent = () => <ColumnItem onPress={() => handleLogout()} />;
|
||||
|
||||
t2 = getComponent();
|
||||
$[2] = handleLogout;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
const getComponent = t2;
|
||||
let t3;
|
||||
if ($[4] !== getComponent) {
|
||||
t3 = getComponent();
|
||||
$[4] = getComponent;
|
||||
$[5] = t3;
|
||||
if ($[4] !== onClick || $[5] !== t2) {
|
||||
t3 = [t2, onClick];
|
||||
$[4] = onClick;
|
||||
$[5] = t2;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[5];
|
||||
t3 = $[6];
|
||||
}
|
||||
let t4;
|
||||
if ($[6] !== onClick || $[7] !== t3) {
|
||||
t4 = [t3, onClick];
|
||||
$[6] = onClick;
|
||||
$[7] = t3;
|
||||
$[8] = t4;
|
||||
} else {
|
||||
t4 = $[8];
|
||||
}
|
||||
return t4;
|
||||
return t3;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"severity":"InvalidReact","category":"This value cannot be modified","description":"Modifying component props or hook arguments is not allowed. Consider using a local variable instead.","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":289},"end":{"line":9,"column":16,"index":303},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"value cannot be modified"}]}}}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"severity":"InvalidReact","category":"Cannot access refs during render","description":"React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":289},"end":{"line":9,"column":16,"index":303},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"Cannot update ref during render"}]}}}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":237},"end":{"line":8,"column":50,"index":285},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":259},"end":{"line":8,"column":30,"index":265},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
@@ -51,12 +51,12 @@ function RefsInEffects() {
|
||||
const ref = useRefHelper();
|
||||
const wrapped = useDeeperRefHelper();
|
||||
let t0;
|
||||
if ($[0] !== ref.current || $[1] !== wrapped.foo.current) {
|
||||
if ($[0] !== ref || $[1] !== wrapped.foo.current) {
|
||||
t0 = () => {
|
||||
print(ref.current);
|
||||
print(wrapped.foo.current);
|
||||
};
|
||||
$[0] = ref.current;
|
||||
$[0] = ref;
|
||||
$[1] = wrapped.foo.current;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
|
||||
@@ -42,74 +42,58 @@ import { c as _c } from "react/compiler-runtime"; /**
|
||||
* conservative and assume that all named lambdas are conditionally called.
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(17);
|
||||
const $ = _c(13);
|
||||
const { arr1, arr2 } = t0;
|
||||
let t1;
|
||||
if ($[0] !== arr1[0]) {
|
||||
t1 = () => arr1[0].value;
|
||||
const getVal1 = () => arr1[0].value;
|
||||
t1 = (e) => getVal1() + e.value;
|
||||
$[0] = arr1[0];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const getVal1 = t1;
|
||||
const cb1 = t1;
|
||||
let t2;
|
||||
if ($[2] !== getVal1) {
|
||||
t2 = (e) => getVal1() + e.value;
|
||||
$[2] = getVal1;
|
||||
$[3] = t2;
|
||||
if ($[2] !== arr1 || $[3] !== cb1) {
|
||||
t2 = arr1.map(cb1);
|
||||
$[2] = arr1;
|
||||
$[3] = cb1;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
t2 = $[4];
|
||||
}
|
||||
const cb1 = t2;
|
||||
const x = t2;
|
||||
let t3;
|
||||
if ($[4] !== arr1 || $[5] !== cb1) {
|
||||
t3 = arr1.map(cb1);
|
||||
$[4] = arr1;
|
||||
$[5] = cb1;
|
||||
if ($[5] !== arr2) {
|
||||
const getVal2 = () => arr2[0].value;
|
||||
t3 = (e_0) => getVal2() + e_0.value;
|
||||
$[5] = arr2;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
const x = t3;
|
||||
const cb2 = t3;
|
||||
let t4;
|
||||
if ($[7] !== arr2) {
|
||||
t4 = () => arr2[0].value;
|
||||
$[7] = arr2;
|
||||
$[8] = t4;
|
||||
if ($[7] !== arr1 || $[8] !== cb2) {
|
||||
t4 = arr1.map(cb2);
|
||||
$[7] = arr1;
|
||||
$[8] = cb2;
|
||||
$[9] = t4;
|
||||
} else {
|
||||
t4 = $[8];
|
||||
t4 = $[9];
|
||||
}
|
||||
const getVal2 = t4;
|
||||
const y = t4;
|
||||
let t5;
|
||||
if ($[9] !== getVal2) {
|
||||
t5 = (e_0) => getVal2() + e_0.value;
|
||||
$[9] = getVal2;
|
||||
$[10] = t5;
|
||||
if ($[10] !== x || $[11] !== y) {
|
||||
t5 = [x, y];
|
||||
$[10] = x;
|
||||
$[11] = y;
|
||||
$[12] = t5;
|
||||
} else {
|
||||
t5 = $[10];
|
||||
t5 = $[12];
|
||||
}
|
||||
const cb2 = t5;
|
||||
let t6;
|
||||
if ($[11] !== arr1 || $[12] !== cb2) {
|
||||
t6 = arr1.map(cb2);
|
||||
$[11] = arr1;
|
||||
$[12] = cb2;
|
||||
$[13] = t6;
|
||||
} else {
|
||||
t6 = $[13];
|
||||
}
|
||||
const y = t6;
|
||||
let t7;
|
||||
if ($[14] !== x || $[15] !== y) {
|
||||
t7 = [x, y];
|
||||
$[14] = x;
|
||||
$[15] = y;
|
||||
$[16] = t7;
|
||||
} else {
|
||||
t7 = $[16];
|
||||
}
|
||||
return t7;
|
||||
return t5;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -42,7 +42,7 @@ import { useRef } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(9);
|
||||
const $ = _c(7);
|
||||
const { a, b } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a.value) {
|
||||
@@ -70,29 +70,22 @@ function Component(t0) {
|
||||
const hasLogged = useRef(false);
|
||||
let t3;
|
||||
if ($[4] !== logA || $[5] !== logB) {
|
||||
t3 = () => {
|
||||
const log = () => {
|
||||
if (!hasLogged.current) {
|
||||
logA();
|
||||
logB();
|
||||
hasLogged.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
t3 = <Stringify log={log} shouldInvokeFns={true} />;
|
||||
$[4] = logA;
|
||||
$[5] = logB;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
const log = t3;
|
||||
let t4;
|
||||
if ($[7] !== log) {
|
||||
t4 = <Stringify log={log} shouldInvokeFns={true} />;
|
||||
$[7] = log;
|
||||
$[8] = t4;
|
||||
} else {
|
||||
t4 = $[8];
|
||||
}
|
||||
return t4;
|
||||
return t3;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -24,28 +24,20 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import * as SharedRuntime from "shared-runtime";
|
||||
import { invoke } from "shared-runtime";
|
||||
function useComponentFactory(t0) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
const { name } = t0;
|
||||
let t1;
|
||||
if ($[0] !== name) {
|
||||
t1 = () => (
|
||||
const cb = () => (
|
||||
<SharedRuntime.Stringify>hello world {name}</SharedRuntime.Stringify>
|
||||
);
|
||||
t1 = invoke(cb);
|
||||
$[0] = name;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb = t1;
|
||||
let t2;
|
||||
if ($[2] !== cb) {
|
||||
t2 = invoke(cb);
|
||||
$[2] = cb;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -25,34 +25,18 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
|
||||
function Component(props) {
|
||||
const $ = _c(6);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.a) {
|
||||
t0 = { a: props.a };
|
||||
const item = { a: props.a };
|
||||
const items = [item];
|
||||
t0 = items.map(_temp);
|
||||
$[0] = props.a;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const item = t0;
|
||||
let t1;
|
||||
if ($[2] !== item) {
|
||||
t1 = [item];
|
||||
$[2] = item;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const items = t1;
|
||||
let t2;
|
||||
if ($[4] !== items) {
|
||||
t2 = items.map(_temp);
|
||||
$[4] = items;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
const mapped = t2;
|
||||
const mapped = t0;
|
||||
return mapped;
|
||||
}
|
||||
function _temp(item_0) {
|
||||
|
||||
@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"severity":"InvalidReact","category":"This value cannot be modified","description":"Modifying component props or hook arguments is not allowed. Consider using a local variable instead.","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":321},"end":{"line":9,"column":16,"index":335},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"value cannot be modified"}]}}}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"severity":"InvalidReact","category":"Cannot access refs during render","description":"React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":321},"end":{"line":9,"column":16,"index":335},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"Cannot update ref during render"}]}}}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":269},"end":{"line":8,"column":50,"index":317},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":291},"end":{"line":8,"column":30,"index":297},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {makeObject_Primitives, Stringify} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const object = {object: props.object};
|
||||
const entries = Object.entries(object);
|
||||
entries.map(([, value]) => {
|
||||
value.updated = true;
|
||||
});
|
||||
return <Stringify entries={entries} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{object: {key: makeObject_Primitives()}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { makeObject_Primitives, Stringify } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.object) {
|
||||
const object = { object: props.object };
|
||||
const entries = Object.entries(object);
|
||||
entries.map(_temp);
|
||||
t0 = <Stringify entries={entries} />;
|
||||
$[0] = props.object;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp(t0) {
|
||||
const [, value] = t0;
|
||||
value.updated = true;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ object: { key: makeObject_Primitives() } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"entries":[["object",{"key":{"a":0,"b":"value1","c":true},"updated":true}]]}</div>
|
||||
@@ -0,0 +1,15 @@
|
||||
import {makeObject_Primitives, Stringify} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const object = {object: props.object};
|
||||
const entries = Object.entries(object);
|
||||
entries.map(([, value]) => {
|
||||
value.updated = true;
|
||||
});
|
||||
return <Stringify entries={entries} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{object: {key: makeObject_Primitives()}}],
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// derived from https://github.com/facebook/react/issues/32261
|
||||
function Component({items}) {
|
||||
const record = useMemo(
|
||||
() =>
|
||||
Object.fromEntries(
|
||||
items.map(item => [item.id, ref => <Stringify ref={ref} {...item} />])
|
||||
),
|
||||
[items]
|
||||
);
|
||||
|
||||
// Without a declaration for Object.entries(), this would be assumed to mutate
|
||||
// `record`, meaning existing memoization couldn't be preserved
|
||||
return (
|
||||
<div>
|
||||
{Object.keys(record).map(id => (
|
||||
<Stringify key={id} render={record[id]} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
items: [
|
||||
{id: '0', name: 'Hello'},
|
||||
{id: '1', name: 'World!'},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
// derived from https://github.com/facebook/react/issues/32261
|
||||
function Component(t0) {
|
||||
const $ = _c(7);
|
||||
const { items } = t0;
|
||||
let t1;
|
||||
if ($[0] !== items) {
|
||||
t1 = Object.fromEntries(items.map(_temp));
|
||||
$[0] = items;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const record = t1;
|
||||
let t2;
|
||||
if ($[2] !== record) {
|
||||
t2 = Object.keys(record);
|
||||
$[2] = record;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
let t3;
|
||||
if ($[4] !== record || $[5] !== t2) {
|
||||
t3 = (
|
||||
<div>
|
||||
{t2.map((id) => (
|
||||
<Stringify key={id} render={record[id]} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
$[4] = record;
|
||||
$[5] = t2;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
function _temp(item) {
|
||||
return [item.id, (ref) => <Stringify ref={ref} {...item} />];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
items: [
|
||||
{ id: "0", name: "Hello" },
|
||||
{ id: "1", name: "World!" },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div><div>{"render":"[[ function params=1 ]]"}</div><div>{"render":"[[ function params=1 ]]"}</div></div>
|
||||
@@ -0,0 +1,36 @@
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// derived from https://github.com/facebook/react/issues/32261
|
||||
function Component({items}) {
|
||||
const record = useMemo(
|
||||
() =>
|
||||
Object.fromEntries(
|
||||
items.map(item => [item.id, ref => <Stringify ref={ref} {...item} />])
|
||||
),
|
||||
[items]
|
||||
);
|
||||
|
||||
// Without a declaration for Object.entries(), this would be assumed to mutate
|
||||
// `record`, meaning existing memoization couldn't be preserved
|
||||
return (
|
||||
<div>
|
||||
{Object.keys(record).map(id => (
|
||||
<Stringify key={id} render={record[id]} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
items: [
|
||||
{id: '0', name: 'Hello'},
|
||||
{id: '1', name: 'World!'},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {makeObject_Primitives, Stringify} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const object = {object: props.object};
|
||||
const entries = Object.entries(object);
|
||||
entries.map(([, value]) => {
|
||||
value.updated = true;
|
||||
});
|
||||
return <Stringify entries={entries} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{object: {key: makeObject_Primitives()}}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { makeObject_Primitives, Stringify } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.object) {
|
||||
const object = { object: props.object };
|
||||
const entries = Object.entries(object);
|
||||
entries.map(_temp);
|
||||
t0 = <Stringify entries={entries} />;
|
||||
$[0] = props.object;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
function _temp(t0) {
|
||||
const [, value] = t0;
|
||||
value.updated = true;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ object: { key: makeObject_Primitives() } }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"entries":[["object",{"key":{"a":0,"b":"value1","c":true},"updated":true}]]}</div>
|
||||
@@ -0,0 +1,15 @@
|
||||
import {makeObject_Primitives, Stringify} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
const object = {object: props.object};
|
||||
const entries = Object.entries(object);
|
||||
entries.map(([, value]) => {
|
||||
value.updated = true;
|
||||
});
|
||||
return <Stringify entries={entries} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{object: {key: makeObject_Primitives()}}],
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// derived from https://github.com/facebook/react/issues/32261
|
||||
function Component({items}) {
|
||||
const record = useMemo(
|
||||
() =>
|
||||
Object.fromEntries(
|
||||
items.map(item => [
|
||||
item.id,
|
||||
{id: item.id, render: ref => <Stringify ref={ref} {...item} />},
|
||||
])
|
||||
),
|
||||
[items]
|
||||
);
|
||||
|
||||
// Without a declaration for Object.entries(), this would be assumed to mutate
|
||||
// `record`, meaning existing memoization couldn't be preserved
|
||||
return (
|
||||
<div>
|
||||
{Object.values(record).map(({id, render}) => (
|
||||
<Stringify key={id} render={render} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
items: [
|
||||
{id: '0', name: 'Hello'},
|
||||
{id: '1', name: 'World!'},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
// derived from https://github.com/facebook/react/issues/32261
|
||||
function Component(t0) {
|
||||
const $ = _c(4);
|
||||
const { items } = t0;
|
||||
let t1;
|
||||
if ($[0] !== items) {
|
||||
t1 = Object.fromEntries(items.map(_temp));
|
||||
$[0] = items;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const record = t1;
|
||||
let t2;
|
||||
if ($[2] !== record) {
|
||||
t2 = <div>{Object.values(record).map(_temp2)}</div>;
|
||||
$[2] = record;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
function _temp2(t0) {
|
||||
const { id, render } = t0;
|
||||
return <Stringify key={id} render={render} />;
|
||||
}
|
||||
function _temp(item) {
|
||||
return [
|
||||
item.id,
|
||||
{ id: item.id, render: (ref) => <Stringify ref={ref} {...item} /> },
|
||||
];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
items: [
|
||||
{ id: "0", name: "Hello" },
|
||||
{ id: "1", name: "World!" },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div><div>{"render":"[[ function params=1 ]]"}</div><div>{"render":"[[ function params=1 ]]"}</div></div>
|
||||
@@ -0,0 +1,39 @@
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// derived from https://github.com/facebook/react/issues/32261
|
||||
function Component({items}) {
|
||||
const record = useMemo(
|
||||
() =>
|
||||
Object.fromEntries(
|
||||
items.map(item => [
|
||||
item.id,
|
||||
{id: item.id, render: ref => <Stringify ref={ref} {...item} />},
|
||||
])
|
||||
),
|
||||
[items]
|
||||
);
|
||||
|
||||
// Without a declaration for Object.entries(), this would be assumed to mutate
|
||||
// `record`, meaning existing memoization couldn't be preserved
|
||||
return (
|
||||
<div>
|
||||
{Object.values(record).map(({id, render}) => (
|
||||
<Stringify key={id} render={render} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
items: [
|
||||
{id: '0', name: 'Hello'},
|
||||
{id: '1', name: 'World!'},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useCallback} from 'react';
|
||||
|
||||
function useHook(maybeRef) {
|
||||
return useCallback(() => {
|
||||
return [maybeRef.current];
|
||||
}, [maybeRef]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `maybeRef.current`, but the source dependencies were [maybeRef]. Differences in ref.current access.
|
||||
|
||||
error.maybe-invalid-useCallback-read-maybeRef.ts:5:21
|
||||
3 |
|
||||
4 | function useHook(maybeRef) {
|
||||
> 5 | return useCallback(() => {
|
||||
| ^^^^^^^
|
||||
> 6 | return [maybeRef.current];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 7 | }, [maybeRef]);
|
||||
| ^^^^ Could not preserve existing manual memoization
|
||||
8 | }
|
||||
9 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
|
||||
function useHook(maybeRef, shouldRead) {
|
||||
return useMemo(() => {
|
||||
return () => [maybeRef.current];
|
||||
}, [shouldRead, maybeRef]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `maybeRef.current`, but the source dependencies were [shouldRead, maybeRef]. Differences in ref.current access.
|
||||
|
||||
error.maybe-invalid-useMemo-read-maybeRef.ts:5:17
|
||||
3 |
|
||||
4 | function useHook(maybeRef, shouldRead) {
|
||||
> 5 | return useMemo(() => {
|
||||
| ^^^^^^^
|
||||
> 6 | return () => [maybeRef.current];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 7 | }, [shouldRead, maybeRef]);
|
||||
| ^^^^ Could not preserve existing manual memoization
|
||||
8 | }
|
||||
9 |
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useCallback} from 'react';
|
||||
|
||||
function useHook(maybeRef) {
|
||||
return useCallback(() => {
|
||||
return [maybeRef.current];
|
||||
}, [maybeRef]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
import { useCallback } from "react";
|
||||
|
||||
function useHook(maybeRef) {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== maybeRef) {
|
||||
t0 = () => [maybeRef.current];
|
||||
$[0] = maybeRef;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
|
||||
function useHook(maybeRef, shouldRead) {
|
||||
return useMemo(() => {
|
||||
return () => [maybeRef.current];
|
||||
}, [shouldRead, maybeRef]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
|
||||
function useHook(maybeRef, shouldRead) {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== maybeRef) {
|
||||
t0 = () => [maybeRef.current];
|
||||
$[0] = maybeRef;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -23,28 +23,20 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function component(a, b) {
|
||||
const $ = _c(5);
|
||||
const $ = _c(3);
|
||||
let t0;
|
||||
if ($[0] !== a || $[1] !== b) {
|
||||
t0 = { a, b };
|
||||
const z = { a, b };
|
||||
t0 = function () {
|
||||
console.log(z);
|
||||
};
|
||||
$[0] = a;
|
||||
$[1] = b;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
const z = t0;
|
||||
let t1;
|
||||
if ($[3] !== z) {
|
||||
t1 = function () {
|
||||
console.log(z);
|
||||
};
|
||||
$[3] = z;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
const x = t1;
|
||||
const x = t0;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
import { mutate, shallowCopy, Stringify } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(6);
|
||||
const $ = _c(4);
|
||||
const { a } = t0;
|
||||
let local;
|
||||
if ($[0] !== a) {
|
||||
@@ -42,22 +42,14 @@ function useFoo(t0) {
|
||||
}
|
||||
let t1;
|
||||
if ($[2] !== local.b.c) {
|
||||
t1 = () => local.b.c;
|
||||
const fn = () => local.b.c;
|
||||
t1 = <Stringify fn={fn} shouldInvokeFns={true} />;
|
||||
$[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;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -29,7 +29,7 @@ import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
import { shallowCopy, Stringify, mutate } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(6);
|
||||
const $ = _c(4);
|
||||
const { a } = t0;
|
||||
let local;
|
||||
if ($[0] !== a) {
|
||||
@@ -42,22 +42,14 @@ function useFoo(t0) {
|
||||
}
|
||||
let t1;
|
||||
if ($[2] !== local) {
|
||||
t1 = () => [() => local.b.c];
|
||||
const fn = () => [() => local.b.c];
|
||||
t1 = <Stringify fn={fn} shouldInvokeFns={true} />;
|
||||
$[2] = local;
|
||||
$[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;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -31,26 +31,19 @@ import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
const { a } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a.b.c) {
|
||||
t1 = () => () => ({ value: a.b.c });
|
||||
const fn = () => () => ({ value: a.b.c });
|
||||
|
||||
t1 = <Stringify fn={fn} shouldInvokeFns={true} />;
|
||||
$[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;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -31,30 +31,23 @@ import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
|
||||
import { identity, Stringify } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
const { a } = t0;
|
||||
let t1;
|
||||
if ($[0] !== a) {
|
||||
t1 = {
|
||||
const x = {
|
||||
fn() {
|
||||
return identity(a.b.c);
|
||||
},
|
||||
};
|
||||
|
||||
t1 = <Stringify x={x} shouldInvokeFns={true} />;
|
||||
$[0] = a;
|
||||
$[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;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -29,7 +29,7 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { useHook } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(6);
|
||||
const $ = _c(4);
|
||||
const o = {};
|
||||
let t0;
|
||||
if ($[0] !== props.value) {
|
||||
@@ -44,22 +44,15 @@ function Component(props) {
|
||||
o.value = props.value;
|
||||
let t1;
|
||||
if ($[2] !== x) {
|
||||
t1 = <div>{x}</div>;
|
||||
const y = <div>{x}</div>;
|
||||
|
||||
t1 = <div>{y}</div>;
|
||||
$[2] = x;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const y = t1;
|
||||
let t2;
|
||||
if ($[4] !== y) {
|
||||
t2 = <div>{y}</div>;
|
||||
$[4] = y;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
return t2;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -31,7 +31,7 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { useHook, identity } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let x = 42;
|
||||
if (props.cond) {
|
||||
x = [];
|
||||
@@ -41,22 +41,15 @@ function Component(props) {
|
||||
identity(x);
|
||||
let t0;
|
||||
if ($[0] !== x) {
|
||||
t0 = [x];
|
||||
const y = [x];
|
||||
|
||||
t0 = [y];
|
||||
$[0] = x;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const y = t0;
|
||||
let t1;
|
||||
if ($[2] !== y) {
|
||||
t1 = [y];
|
||||
$[2] = y;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -66,25 +66,17 @@ function Parent(t0) {
|
||||
}
|
||||
|
||||
function ChildImpl(_props, ref) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== ref) {
|
||||
t0 = () => ref.current;
|
||||
const cb = () => ref.current;
|
||||
t0 = <Stringify cb={cb} shouldInvokeFns={true} />;
|
||||
$[0] = ref;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const cb = t0;
|
||||
let t1;
|
||||
if ($[2] !== cb) {
|
||||
t1 = <Stringify cb={cb} shouldInvokeFns={true} />;
|
||||
$[2] = cb;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
const Child = forwardRef(ChildImpl);
|
||||
|
||||
@@ -41,29 +41,21 @@ import { Stringify } from "shared-runtime";
|
||||
* `pruneNonReactiveDependencies`
|
||||
*/
|
||||
function Component(t0) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
const { cond } = t0;
|
||||
const ref1 = useRef(1);
|
||||
const ref2 = useRef(2);
|
||||
const ref = cond ? ref1 : ref2;
|
||||
let t1;
|
||||
if ($[0] !== ref) {
|
||||
t1 = () => ref.current;
|
||||
const cb = () => ref.current;
|
||||
t1 = <Stringify cb={cb} shouldInvokeFns={true} />;
|
||||
$[0] = ref;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const cb = t1;
|
||||
let t2;
|
||||
if ($[2] !== cb) {
|
||||
t2 = <Stringify cb={cb} shouldInvokeFns={true} />;
|
||||
$[2] = cb;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -35,34 +35,23 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(6);
|
||||
let a;
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.b) {
|
||||
a = {};
|
||||
const a = {};
|
||||
const b = [];
|
||||
b.push(props.b);
|
||||
a.a = null;
|
||||
|
||||
t0 = [a];
|
||||
const c = [a];
|
||||
|
||||
t0 = [c, a];
|
||||
$[0] = props.b;
|
||||
$[1] = a;
|
||||
$[2] = t0;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
a = $[1];
|
||||
t0 = $[2];
|
||||
t0 = $[1];
|
||||
}
|
||||
const c = t0;
|
||||
let t1;
|
||||
if ($[3] !== a || $[4] !== c) {
|
||||
t1 = [c, a];
|
||||
$[3] = a;
|
||||
$[4] = c;
|
||||
$[5] = t1;
|
||||
} else {
|
||||
t1 = $[5];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -32,18 +32,24 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
const $ = _c(4);
|
||||
let x;
|
||||
if ($[0] !== props.input) {
|
||||
const x = [];
|
||||
x = [];
|
||||
const y = x;
|
||||
y.push(props.input);
|
||||
|
||||
t0 = [x[0]];
|
||||
$[0] = props.input;
|
||||
$[1] = t0;
|
||||
$[1] = x;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
x = $[1];
|
||||
}
|
||||
let t0;
|
||||
if ($[2] !== x[0]) {
|
||||
t0 = [x[0]];
|
||||
$[2] = x[0];
|
||||
$[3] = t0;
|
||||
} else {
|
||||
t0 = $[3];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
@@ -35,22 +35,28 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
const $ = _c(4);
|
||||
let x;
|
||||
if ($[0] !== props.input) {
|
||||
const x = [];
|
||||
x = [];
|
||||
const f = (arg) => {
|
||||
const y = x;
|
||||
y.push(arg);
|
||||
};
|
||||
|
||||
f(props.input);
|
||||
|
||||
t0 = [x[0]];
|
||||
$[0] = props.input;
|
||||
$[1] = t0;
|
||||
$[1] = x;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
x = $[1];
|
||||
}
|
||||
let t0;
|
||||
if ($[2] !== x[0]) {
|
||||
t0 = [x[0]];
|
||||
$[2] = x[0];
|
||||
$[3] = t0;
|
||||
} else {
|
||||
t0 = $[3];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
@@ -18,28 +18,21 @@ function Foo({a}) {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender:false
|
||||
function Foo(t0) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
const { a } = t0;
|
||||
const ref = useRef();
|
||||
const val = ref.current;
|
||||
let t1;
|
||||
if ($[0] !== a) {
|
||||
t1 = { a, val };
|
||||
const x = { a, val };
|
||||
|
||||
t1 = <VideoList videos={x} />;
|
||||
$[0] = a;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const x = t1;
|
||||
let t2;
|
||||
if ($[2] !== x) {
|
||||
t2 = <VideoList videos={x} />;
|
||||
$[2] = x;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
return t1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -17,27 +17,20 @@ function Foo({a}) {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender:false
|
||||
function Foo(t0) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
const { a } = t0;
|
||||
const ref = useRef();
|
||||
let t1;
|
||||
if ($[0] !== a) {
|
||||
t1 = { a, val: ref.current };
|
||||
const x = { a, val: ref.current };
|
||||
|
||||
t1 = <VideoList videos={x} />;
|
||||
$[0] = a;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const x = t1;
|
||||
let t2;
|
||||
if ($[2] !== x) {
|
||||
t2 = <VideoList videos={x} />;
|
||||
$[2] = x;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
return t1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -41,11 +41,11 @@ const $ = "module_$";
|
||||
const t0 = "module_t0";
|
||||
const c_0 = "module_c_0";
|
||||
function useFoo(props) {
|
||||
const $0 = _c(4);
|
||||
const $0 = _c(2);
|
||||
const c_00 = $0[0] !== props.value;
|
||||
let t1;
|
||||
if (c_00) {
|
||||
t1 = () => {
|
||||
const a = () => {
|
||||
const b = () => {
|
||||
const c = () => {
|
||||
console.log($);
|
||||
@@ -57,22 +57,14 @@ function useFoo(props) {
|
||||
};
|
||||
return b;
|
||||
};
|
||||
|
||||
t1 = a()()();
|
||||
$0[0] = props.value;
|
||||
$0[1] = t1;
|
||||
} else {
|
||||
t1 = $0[1];
|
||||
}
|
||||
const a = t1;
|
||||
const c_2 = $0[2] !== a;
|
||||
let t2;
|
||||
if (c_2) {
|
||||
t2 = a()()();
|
||||
$0[2] = a;
|
||||
$0[3] = t2;
|
||||
} else {
|
||||
t2 = $0[3];
|
||||
}
|
||||
return t2;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -35,60 +35,44 @@ import { useMemo } from "react";
|
||||
import { useFragment } from "shared-runtime";
|
||||
|
||||
function Component() {
|
||||
const $ = _c(11);
|
||||
const $ = _c(7);
|
||||
const data = useFragment();
|
||||
let t0;
|
||||
if ($[0] !== data.nodes) {
|
||||
t0 = data.nodes ?? [];
|
||||
const nodes = data.nodes ?? [];
|
||||
const flatMap = nodes.flatMap(_temp);
|
||||
t0 = flatMap.filter(_temp2);
|
||||
$[0] = data.nodes;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const nodes = t0;
|
||||
const filtered = t0;
|
||||
let t1;
|
||||
if ($[2] !== nodes) {
|
||||
t1 = nodes.flatMap(_temp);
|
||||
$[2] = nodes;
|
||||
if ($[2] !== filtered) {
|
||||
t1 = filtered.map();
|
||||
$[2] = filtered;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const flatMap = t1;
|
||||
let t2;
|
||||
if ($[4] !== flatMap) {
|
||||
t2 = flatMap.filter(_temp2);
|
||||
$[4] = flatMap;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
const filtered = t2;
|
||||
let t3;
|
||||
if ($[6] !== filtered) {
|
||||
t3 = filtered.map();
|
||||
$[6] = filtered;
|
||||
$[7] = t3;
|
||||
} else {
|
||||
t3 = $[7];
|
||||
}
|
||||
const map = t3;
|
||||
const map = t1;
|
||||
const index = filtered.findIndex(_temp3);
|
||||
let t4;
|
||||
if ($[8] !== index || $[9] !== map) {
|
||||
t4 = (
|
||||
let t2;
|
||||
if ($[4] !== index || $[5] !== map) {
|
||||
t2 = (
|
||||
<div>
|
||||
{map}
|
||||
{index}
|
||||
</div>
|
||||
);
|
||||
$[8] = index;
|
||||
$[9] = map;
|
||||
$[10] = t4;
|
||||
$[4] = index;
|
||||
$[5] = map;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t4 = $[10];
|
||||
t2 = $[6];
|
||||
}
|
||||
return t4;
|
||||
return t2;
|
||||
}
|
||||
function _temp3(x) {
|
||||
return x === null;
|
||||
|
||||
@@ -32,60 +32,44 @@ import { useMemo } from "react";
|
||||
import { useFragment } from "shared-runtime";
|
||||
|
||||
function Component() {
|
||||
const $ = _c(11);
|
||||
const $ = _c(7);
|
||||
const data = useFragment();
|
||||
let t0;
|
||||
if ($[0] !== data.nodes) {
|
||||
t0 = data.nodes ?? [];
|
||||
const nodes = data.nodes ?? [];
|
||||
const flatMap = nodes.flatMap(_temp);
|
||||
t0 = flatMap.filter(_temp2);
|
||||
$[0] = data.nodes;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const nodes = t0;
|
||||
const filtered = t0;
|
||||
let t1;
|
||||
if ($[2] !== nodes) {
|
||||
t1 = nodes.flatMap(_temp);
|
||||
$[2] = nodes;
|
||||
if ($[2] !== filtered) {
|
||||
t1 = filtered.map();
|
||||
$[2] = filtered;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const flatMap = t1;
|
||||
let t2;
|
||||
if ($[4] !== flatMap) {
|
||||
t2 = flatMap.filter(_temp2);
|
||||
$[4] = flatMap;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
const filtered = t2;
|
||||
let t3;
|
||||
if ($[6] !== filtered) {
|
||||
t3 = filtered.map();
|
||||
$[6] = filtered;
|
||||
$[7] = t3;
|
||||
} else {
|
||||
t3 = $[7];
|
||||
}
|
||||
const map = t3;
|
||||
const map = t1;
|
||||
const index = filtered.findIndex(_temp3);
|
||||
let t4;
|
||||
if ($[8] !== index || $[9] !== map) {
|
||||
t4 = (
|
||||
let t2;
|
||||
if ($[4] !== index || $[5] !== map) {
|
||||
t2 = (
|
||||
<div>
|
||||
{map}
|
||||
{index}
|
||||
</div>
|
||||
);
|
||||
$[8] = index;
|
||||
$[9] = map;
|
||||
$[10] = t4;
|
||||
$[4] = index;
|
||||
$[5] = map;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t4 = $[10];
|
||||
t2 = $[6];
|
||||
}
|
||||
return t4;
|
||||
return t2;
|
||||
}
|
||||
function _temp3(x) {
|
||||
return x === null;
|
||||
|
||||
@@ -30,40 +30,33 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { identity, makeObject_Primitives, Stringify } from "shared-runtime";
|
||||
|
||||
function Example(props) {
|
||||
const $ = _c(7);
|
||||
const $ = _c(5);
|
||||
const object = props.object;
|
||||
let t0;
|
||||
if ($[0] !== object || $[1] !== props.value) {
|
||||
t0 = () => {
|
||||
const f = () => {
|
||||
const obj = identity(object);
|
||||
obj.property = props.value;
|
||||
return obj;
|
||||
};
|
||||
|
||||
t0 = f();
|
||||
$[0] = object;
|
||||
$[1] = props.value;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
const f = t0;
|
||||
const obj_0 = t0;
|
||||
let t1;
|
||||
if ($[3] !== f) {
|
||||
t1 = f();
|
||||
$[3] = f;
|
||||
if ($[3] !== obj_0) {
|
||||
t1 = <Stringify obj={obj_0} />;
|
||||
$[3] = obj_0;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
const obj_0 = t1;
|
||||
let t2;
|
||||
if ($[5] !== obj_0) {
|
||||
t2 = <Stringify obj={obj_0} />;
|
||||
$[5] = obj_0;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
return t2;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -30,40 +30,33 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { makeObject_Primitives, Stringify } from "shared-runtime";
|
||||
|
||||
function Example(props) {
|
||||
const $ = _c(7);
|
||||
const $ = _c(5);
|
||||
const object = props.object;
|
||||
let t0;
|
||||
if ($[0] !== object || $[1] !== props.value) {
|
||||
t0 = () => {
|
||||
const f = () => {
|
||||
const obj = object.makeObject();
|
||||
obj.property = props.value;
|
||||
return obj;
|
||||
};
|
||||
|
||||
t0 = f();
|
||||
$[0] = object;
|
||||
$[1] = props.value;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
const f = t0;
|
||||
const obj_0 = t0;
|
||||
let t1;
|
||||
if ($[3] !== f) {
|
||||
t1 = f();
|
||||
$[3] = f;
|
||||
if ($[3] !== obj_0) {
|
||||
t1 = <Stringify obj={obj_0} />;
|
||||
$[3] = obj_0;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
const obj_0 = t1;
|
||||
let t2;
|
||||
if ($[5] !== obj_0) {
|
||||
t2 = <Stringify obj={obj_0} />;
|
||||
$[5] = obj_0;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
return t2;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// derived from https://github.com/facebook/react/issues/32261
|
||||
function Component({items}) {
|
||||
const record = useMemo(
|
||||
() =>
|
||||
Object.fromEntries(
|
||||
items.map(item => [item.id, ref => <Stringify ref={ref} {...item} />])
|
||||
),
|
||||
[items]
|
||||
);
|
||||
|
||||
// Without a declaration for Object.entries(), this would be assumed to mutate
|
||||
// `record`, meaning existing memoization couldn't be preserved
|
||||
return (
|
||||
<div>
|
||||
{Object.entries(record).map(([id, render]) => (
|
||||
<Stringify key={id} render={render} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
items: [
|
||||
{id: '0', name: 'Hello'},
|
||||
{id: '1', name: 'World!'},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
// derived from https://github.com/facebook/react/issues/32261
|
||||
function Component(t0) {
|
||||
const $ = _c(4);
|
||||
const { items } = t0;
|
||||
let t1;
|
||||
if ($[0] !== items) {
|
||||
t1 = Object.fromEntries(items.map(_temp));
|
||||
$[0] = items;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const record = t1;
|
||||
let t2;
|
||||
if ($[2] !== record) {
|
||||
t2 = <div>{Object.entries(record).map(_temp2)}</div>;
|
||||
$[2] = record;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
function _temp2(t0) {
|
||||
const [id, render] = t0;
|
||||
return <Stringify key={id} render={render} />;
|
||||
}
|
||||
function _temp(item) {
|
||||
return [item.id, (ref) => <Stringify ref={ref} {...item} />];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
items: [
|
||||
{ id: "0", name: "Hello" },
|
||||
{ id: "1", name: "World!" },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div><div>{"render":"[[ function params=1 ]]"}</div><div>{"render":"[[ function params=1 ]]"}</div></div>
|
||||
@@ -0,0 +1,36 @@
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// derived from https://github.com/facebook/react/issues/32261
|
||||
function Component({items}) {
|
||||
const record = useMemo(
|
||||
() =>
|
||||
Object.fromEntries(
|
||||
items.map(item => [item.id, ref => <Stringify ref={ref} {...item} />])
|
||||
),
|
||||
[items]
|
||||
);
|
||||
|
||||
// Without a declaration for Object.entries(), this would be assumed to mutate
|
||||
// `record`, meaning existing memoization couldn't be preserved
|
||||
return (
|
||||
<div>
|
||||
{Object.entries(record).map(([id, render]) => (
|
||||
<Stringify key={id} render={render} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
items: [
|
||||
{id: '0', name: 'Hello'},
|
||||
{id: '1', name: 'World!'},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify, identity, mutate, CONST_TRUE} from 'shared-runtime';
|
||||
|
||||
function Foo(props, ref) {
|
||||
const value = {};
|
||||
if (CONST_TRUE) {
|
||||
mutate(value);
|
||||
return <Stringify ref={ref} />;
|
||||
}
|
||||
mutate(value);
|
||||
if (CONST_TRUE) {
|
||||
return <Stringify ref={identity(ref)} />;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}, {current: 'fake-ref-object'}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify, identity, mutate, CONST_TRUE } from "shared-runtime";
|
||||
|
||||
function Foo(props, ref) {
|
||||
const $ = _c(7);
|
||||
let t0;
|
||||
let value;
|
||||
if ($[0] !== ref) {
|
||||
t0 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
value = {};
|
||||
if (CONST_TRUE) {
|
||||
mutate(value);
|
||||
t0 = <Stringify ref={ref} />;
|
||||
break bb0;
|
||||
}
|
||||
|
||||
mutate(value);
|
||||
}
|
||||
$[0] = ref;
|
||||
$[1] = t0;
|
||||
$[2] = value;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
value = $[2];
|
||||
}
|
||||
if (t0 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t0;
|
||||
}
|
||||
if (CONST_TRUE) {
|
||||
let t1;
|
||||
if ($[3] !== ref) {
|
||||
t1 = identity(ref);
|
||||
$[3] = ref;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
let t2;
|
||||
if ($[5] !== t1) {
|
||||
t2 = <Stringify ref={t1} />;
|
||||
$[5] = t1;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}, { current: "fake-ref-object" }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"ref":{"current":"fake-ref-object"}}</div>
|
||||
@@ -45,7 +45,7 @@ import { Stringify, identity, makeArray, toJSON } from "shared-runtime";
|
||||
import { useMemo } from "react";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(12);
|
||||
const $ = _c(10);
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] !== props) {
|
||||
@@ -71,57 +71,50 @@ function Component(props) {
|
||||
}
|
||||
let t2;
|
||||
if ($[3] !== t0) {
|
||||
t2 = { url: t0 };
|
||||
const linkProps = { url: t0 };
|
||||
|
||||
const x = {};
|
||||
let t3;
|
||||
let t4;
|
||||
let t5;
|
||||
let t6;
|
||||
let t7;
|
||||
if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = [1];
|
||||
t4 = [2];
|
||||
t5 = [3];
|
||||
t6 = [4];
|
||||
t7 = [5];
|
||||
$[5] = t3;
|
||||
$[6] = t4;
|
||||
$[7] = t5;
|
||||
$[8] = t6;
|
||||
$[9] = t7;
|
||||
} else {
|
||||
t3 = $[5];
|
||||
t4 = $[6];
|
||||
t5 = $[7];
|
||||
t6 = $[8];
|
||||
t7 = $[9];
|
||||
}
|
||||
t2 = (
|
||||
<Stringify
|
||||
link={linkProps}
|
||||
val1={t3}
|
||||
val2={t4}
|
||||
val3={t5}
|
||||
val4={t6}
|
||||
val5={t7}
|
||||
>
|
||||
{makeArray(x, 2)}
|
||||
</Stringify>
|
||||
);
|
||||
$[3] = t0;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
const linkProps = t2;
|
||||
let t3;
|
||||
if ($[5] !== linkProps) {
|
||||
const x = {};
|
||||
let t4;
|
||||
let t5;
|
||||
let t6;
|
||||
let t7;
|
||||
let t8;
|
||||
if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t4 = [1];
|
||||
t5 = [2];
|
||||
t6 = [3];
|
||||
t7 = [4];
|
||||
t8 = [5];
|
||||
$[7] = t4;
|
||||
$[8] = t5;
|
||||
$[9] = t6;
|
||||
$[10] = t7;
|
||||
$[11] = t8;
|
||||
} else {
|
||||
t4 = $[7];
|
||||
t5 = $[8];
|
||||
t6 = $[9];
|
||||
t7 = $[10];
|
||||
t8 = $[11];
|
||||
}
|
||||
t3 = (
|
||||
<Stringify
|
||||
link={linkProps}
|
||||
val1={t4}
|
||||
val2={t5}
|
||||
val3={t6}
|
||||
val4={t7}
|
||||
val5={t8}
|
||||
>
|
||||
{makeArray(x, 2)}
|
||||
</Stringify>
|
||||
);
|
||||
$[5] = linkProps;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
return t3;
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -48,28 +48,21 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { StaticText1, Stringify, Text } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
const { buttons } = props;
|
||||
let t0;
|
||||
if ($[0] !== buttons) {
|
||||
const [, ...nonPrimaryButtons] = buttons;
|
||||
|
||||
t0 = nonPrimaryButtons.map(_temp);
|
||||
const renderedNonPrimaryButtons = nonPrimaryButtons.map(_temp);
|
||||
|
||||
t0 = <StaticText1>{renderedNonPrimaryButtons}</StaticText1>;
|
||||
$[0] = buttons;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const renderedNonPrimaryButtons = t0;
|
||||
let t1;
|
||||
if ($[2] !== renderedNonPrimaryButtons) {
|
||||
t1 = <StaticText1>{renderedNonPrimaryButtons}</StaticText1>;
|
||||
$[2] = renderedNonPrimaryButtons;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
function _temp(buttonProps, i) {
|
||||
return (
|
||||
|
||||
@@ -29,10 +29,10 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { throwInput } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
t0 = () => {
|
||||
const callback = () => {
|
||||
try {
|
||||
throwInput([props.value]);
|
||||
} catch (t1) {
|
||||
@@ -40,21 +40,14 @@ function Component(props) {
|
||||
return e;
|
||||
}
|
||||
};
|
||||
|
||||
t0 = callback();
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const callback = t0;
|
||||
let t1;
|
||||
if ($[2] !== callback) {
|
||||
t1 = callback();
|
||||
$[2] = callback;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -25,25 +25,17 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableUseTypeAnnotations
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.id) {
|
||||
t0 = makeArray(props.id);
|
||||
const x = makeArray(props.id);
|
||||
t0 = x.at(0);
|
||||
$[0] = props.id;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[2] !== x) {
|
||||
t1 = x.at(0);
|
||||
$[2] = x;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const y = t1;
|
||||
const y = t0;
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,25 +29,17 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { identity } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.id) {
|
||||
t0 = makeArray(props.id);
|
||||
const x = makeArray(props.id);
|
||||
t0 = x.at(0);
|
||||
$[0] = props.id;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[2] !== x) {
|
||||
t1 = x.at(0);
|
||||
$[2] = x;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const y = t1;
|
||||
const y = t0;
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,25 +22,17 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== props.foo) {
|
||||
t0 = [props.foo];
|
||||
const x = [props.foo];
|
||||
t0 = <div x={x}>"foo"</div>;
|
||||
$[0] = props.foo;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[2] !== x) {
|
||||
t1 = <div x={x}>"foo"</div>;
|
||||
$[2] = x;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -39,41 +39,34 @@ import { Stringify } from "shared-runtime";
|
||||
const FooContext = createContext({ current: true });
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(6);
|
||||
const $ = _c(4);
|
||||
const foo = useContext(FooContext);
|
||||
let t0;
|
||||
if ($[0] !== foo.current) {
|
||||
t0 = () => {
|
||||
const getValue = () => {
|
||||
if (foo.current) {
|
||||
return {};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
t0 = getValue();
|
||||
$[0] = foo.current;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const getValue = t0;
|
||||
const value = t0;
|
||||
let t1;
|
||||
if ($[2] !== getValue) {
|
||||
t1 = getValue();
|
||||
$[2] = getValue;
|
||||
if ($[2] !== value) {
|
||||
t1 = <Stringify value={value} />;
|
||||
$[2] = value;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const value = t1;
|
||||
let t2;
|
||||
if ($[4] !== value) {
|
||||
t2 = <Stringify value={value} />;
|
||||
$[4] = value;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
return t2;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user