Compare commits

..

1 Commits

Author SHA1 Message Date
Joe Savona
be41c94e5c [compiler] ref guards apply up to fallthrough of the test
Fixes #30782

When developers do an `if (ref.current == null)` guard for lazy ref initialization, the "safe" blocks should extend up to the if's fallthrough. Previously we only allowed writing to the ref in the if consequent, but this meant that you couldn't use a ternary, logical, etc in the if body.
2025-07-29 10:52:47 -07:00
31 changed files with 22 additions and 1192 deletions

View File

@@ -114,99 +114,6 @@ 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',
},
],
},
}),
],
]),
],
[

View File

@@ -142,7 +142,6 @@ function parseAliasingSignatureConfig(
const effects = typeConfig.effects.map(
(effect: AliasingEffectConfig): AliasingEffect => {
switch (effect.kind) {
case 'ImmutableCapture':
case 'CreateFrom':
case 'Capture':
case 'Alias':

View File

@@ -111,19 +111,6 @@ 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;
@@ -200,7 +187,6 @@ export type AliasingEffectConfig =
| AssignEffectConfig
| AliasEffectConfig
| CaptureEffectConfig
| ImmutableCaptureEffectConfig
| ImpureEffectConfig
| MutateEffectConfig
| MutateTransitiveConditionallyConfig
@@ -213,7 +199,6 @@ export const AliasingEffectSchema: z.ZodType<AliasingEffectConfig> = z.union([
AssignEffectSchema,
AliasEffectSchema,
CaptureEffectSchema,
ImmutableCaptureEffectSchema,
ImpureEffectSchema,
MutateEffectSchema,
MutateTransitiveConditionallySchema,

View File

@@ -80,18 +80,8 @@ type RefAccessRefType =
type RefFnType = {readRefEffect: boolean; returnType: RefAccessType};
class Env {
class Env extends Map<IdentifierId, RefAccessType> {
#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;
@@ -101,14 +91,8 @@ class Env {
return this.#changed;
}
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);
override set(key: IdentifierId, value: RefAccessType): this {
const cur = this.get(key);
const widenedValue = joinRefAccessTypes(value, cur ?? {kind: 'None'});
if (
!(cur == null && widenedValue.kind === 'None') &&
@@ -116,8 +100,7 @@ class Env {
) {
this.#changed = true;
}
this.#data.set(operandId, widenedValue);
return this;
return super.set(key, widenedValue);
}
}
@@ -125,48 +108,9 @@ 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'};
@@ -490,12 +434,7 @@ function validateNoRefAccessInRenderImpl(
* By default we check that function call operands are not refs,
* ref values, or functions that can access refs.
*/
if (
isRefLValue ||
(hookKind != null &&
hookKind !== 'useState' &&
hookKind !== 'useReducer')
) {
if (isRefLValue || hookKind != null) {
/**
* Special cases:
*
@@ -580,25 +519,11 @@ function validateNoRefAccessInRenderImpl(
} else {
validateNoRefUpdate(errors, env, instr.value.object, instr.loc);
}
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);
for (const operand of eachInstructionValueOperand(instr.value)) {
if (operand === instr.value.object) {
continue;
}
validateNoRefValueAccess(errors, env, operand);
}
break;
}
@@ -800,7 +725,11 @@ function validateNoRefUpdate(
loc: SourceLocation,
): void {
const type = destructure(env.get(operand.identifier.id));
if (type?.kind === 'Ref' || type?.kind === 'RefValue') {
if (
type?.kind === 'Ref' ||
type?.kind === 'RefValue' ||
(type?.kind === 'Structure' && type.fn?.readRefEffect)
) {
errors.pushDiagnostic(
CompilerDiagnostic.create({
severity: ErrorSeverity.InvalidReact,

View File

@@ -1,52 +0,0 @@
## 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>

View File

@@ -1,14 +0,0 @@
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}],
};

View File

@@ -1,45 +0,0 @@
## 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} />;
```

View File

@@ -1,17 +0,0 @@
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}],
};

View File

@@ -1,41 +0,0 @@
## 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 | }
```

View File

@@ -1,13 +0,0 @@
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}],
};

View File

@@ -1,37 +0,0 @@
## 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 |
```

View File

@@ -1,9 +0,0 @@
import {useRef} from 'react';
function Component() {
const ref = useRef(null);
const object = {};
object.foo = () => ref.current;
const refValue = object.foo();
return <div>{refValue}</div>;
}

View File

@@ -1,41 +0,0 @@
## 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 | }
```

View File

@@ -1,13 +0,0 @@
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}],
};

View File

@@ -41,13 +41,14 @@ 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:12:28
10 | const x = {a, val: val.ref.current};
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
11 |
> 12 | return <VideoList videos={x} />;
| ^ Cannot access ref value during render
12 | return <VideoList videos={x} />;
13 | }
14 |
```

View File

@@ -1,57 +0,0 @@
## 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 | });
```

View File

@@ -1,16 +0,0 @@
// @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()}}],
};

View File

@@ -1,57 +0,0 @@
## 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 | });
```

View File

@@ -1,16 +0,0 @@
// @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()}}],
};

View File

@@ -1,57 +0,0 @@
## 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>

View File

@@ -1,15 +0,0 @@
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()}}],
};

View File

@@ -1,108 +0,0 @@
## 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>

View File

@@ -1,36 +0,0 @@
// @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!'},
],
},
],
};

View File

@@ -1,57 +0,0 @@
## 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>

View File

@@ -1,15 +0,0 @@
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()}}],
};

View File

@@ -1,103 +0,0 @@
## 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>

View File

@@ -1,39 +0,0 @@
// @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!'},
],
},
],
};

View File

@@ -1,97 +0,0 @@
## 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>

View File

@@ -1,36 +0,0 @@
// @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!'},
],
},
],
};

View File

@@ -101,7 +101,7 @@ const COMPILER_OPTIONS: Partial<PluginOptions> = {
// Don't emit errors on Flow suppressions--Flow already gave a signal
flowSuppressions: false,
environment: validateEnvironmentConfig({
validateRefAccessDuringRender: true,
validateRefAccessDuringRender: false,
validateNoSetStateInRender: true,
validateNoSetStateInEffects: true,
validateNoJSXInTryStatements: true,

View File

@@ -103,7 +103,7 @@ const COMPILER_OPTIONS: Partial<PluginOptions> = {
// Don't emit errors on Flow suppressions--Flow already gave a signal
flowSuppressions: false,
environment: validateEnvironmentConfig({
validateRefAccessDuringRender: true,
validateRefAccessDuringRender: false,
validateNoSetStateInRender: true,
validateNoSetStateInEffects: true,
validateNoJSXInTryStatements: true,