mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
[compiler][cleanup] Remove unused enableReactiveScopesInHIR flag
Reactive scopes in HIR has been stable for over 3 months now and is the future direction of react compiler, removing this flag to reduce implementation forks. ghstack-source-id: 65cdf63cf76029fa22d40fd85aba0ac976dcfc08 Pull Request resolved: https://github.com/facebook/react/pull/30891
This commit is contained in:
parent
a03254bc60
commit
43264a61d0
|
|
@ -46,18 +46,13 @@ import {instructionReordering} from '../Optimization/InstructionReordering';
|
|||
import {
|
||||
CodegenFunction,
|
||||
alignObjectMethodScopes,
|
||||
alignReactiveScopesToBlockScopes,
|
||||
assertScopeInstructionsWithinScopes,
|
||||
assertWellFormedBreakTargets,
|
||||
buildReactiveBlocks,
|
||||
buildReactiveFunction,
|
||||
codegenFunction,
|
||||
extractScopeDeclarationsFromDestructuring,
|
||||
flattenReactiveLoops,
|
||||
flattenScopesWithHooksOrUse,
|
||||
inferReactiveScopeVariables,
|
||||
memoizeFbtAndMacroOperandsInSameScope,
|
||||
mergeOverlappingReactiveScopes,
|
||||
mergeReactiveScopesThatInvalidateTogether,
|
||||
promoteUsedTemporaries,
|
||||
propagateEarlyReturns,
|
||||
|
|
@ -300,54 +295,52 @@ function* runWithEnvironment(
|
|||
value: hir,
|
||||
});
|
||||
|
||||
if (env.config.enableReactiveScopesInHIR) {
|
||||
pruneUnusedLabelsHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'PruneUnusedLabelsHIR',
|
||||
value: hir,
|
||||
});
|
||||
pruneUnusedLabelsHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'PruneUnusedLabelsHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
alignReactiveScopesToBlockScopesHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'AlignReactiveScopesToBlockScopesHIR',
|
||||
value: hir,
|
||||
});
|
||||
alignReactiveScopesToBlockScopesHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'AlignReactiveScopesToBlockScopesHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
mergeOverlappingReactiveScopesHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'MergeOverlappingReactiveScopesHIR',
|
||||
value: hir,
|
||||
});
|
||||
assertValidBlockNesting(hir);
|
||||
mergeOverlappingReactiveScopesHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'MergeOverlappingReactiveScopesHIR',
|
||||
value: hir,
|
||||
});
|
||||
assertValidBlockNesting(hir);
|
||||
|
||||
buildReactiveScopeTerminalsHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'BuildReactiveScopeTerminalsHIR',
|
||||
value: hir,
|
||||
});
|
||||
buildReactiveScopeTerminalsHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'BuildReactiveScopeTerminalsHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
assertValidBlockNesting(hir);
|
||||
assertValidBlockNesting(hir);
|
||||
|
||||
flattenReactiveLoopsHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'FlattenReactiveLoopsHIR',
|
||||
value: hir,
|
||||
});
|
||||
flattenReactiveLoopsHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'FlattenReactiveLoopsHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
flattenScopesWithHooksOrUseHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'FlattenScopesWithHooksOrUseHIR',
|
||||
value: hir,
|
||||
});
|
||||
assertTerminalSuccessorsExist(hir);
|
||||
assertTerminalPredsExist(hir);
|
||||
}
|
||||
flattenScopesWithHooksOrUseHIR(hir);
|
||||
yield log({
|
||||
kind: 'hir',
|
||||
name: 'FlattenScopesWithHooksOrUseHIR',
|
||||
value: hir,
|
||||
});
|
||||
assertTerminalSuccessorsExist(hir);
|
||||
assertTerminalPredsExist(hir);
|
||||
|
||||
const reactiveFunction = buildReactiveFunction(hir);
|
||||
yield log({
|
||||
|
|
@ -364,44 +357,6 @@ function* runWithEnvironment(
|
|||
name: 'PruneUnusedLabels',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
if (!env.config.enableReactiveScopesInHIR) {
|
||||
alignReactiveScopesToBlockScopes(reactiveFunction);
|
||||
yield log({
|
||||
kind: 'reactive',
|
||||
name: 'AlignReactiveScopesToBlockScopes',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
mergeOverlappingReactiveScopes(reactiveFunction);
|
||||
yield log({
|
||||
kind: 'reactive',
|
||||
name: 'MergeOverlappingReactiveScopes',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
buildReactiveBlocks(reactiveFunction);
|
||||
yield log({
|
||||
kind: 'reactive',
|
||||
name: 'BuildReactiveBlocks',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
flattenReactiveLoops(reactiveFunction);
|
||||
yield log({
|
||||
kind: 'reactive',
|
||||
name: 'FlattenReactiveLoops',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
flattenScopesWithHooksOrUse(reactiveFunction);
|
||||
yield log({
|
||||
kind: 'reactive',
|
||||
name: 'FlattenScopesWithHooks',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
}
|
||||
|
||||
assertScopeInstructionsWithinScopes(reactiveFunction);
|
||||
|
||||
propagateScopeDependencies(reactiveFunction);
|
||||
|
|
|
|||
|
|
@ -222,8 +222,6 @@ const EnvironmentConfigSchema = z.object({
|
|||
*/
|
||||
enableUseTypeAnnotations: z.boolean().default(false),
|
||||
|
||||
enableReactiveScopesInHIR: z.boolean().default(true),
|
||||
|
||||
/**
|
||||
* Enables inference of optional dependency chains. Without this flag
|
||||
* a property chain such as `props?.items?.foo` will infer as a dep on
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableReactiveScopesInHIR:false
|
||||
|
||||
import {Stringify, identity, makeArray, mutate} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
|
|
@ -37,8 +35,7 @@ export const FIXTURE_ENTRYPOINT = {
|
|||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false
|
||||
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify, identity, makeArray, mutate } from "shared-runtime";
|
||||
|
||||
/**
|
||||
|
|
@ -52,11 +49,12 @@ import { Stringify, identity, makeArray, mutate } from "shared-runtime";
|
|||
* handles this correctly.
|
||||
*/
|
||||
function Foo(t0) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(3);
|
||||
const { cond1, cond2 } = t0;
|
||||
const arr = makeArray({ a: 2 }, 2, []);
|
||||
let t1;
|
||||
if ($[0] !== cond1 || $[1] !== cond2 || $[2] !== arr) {
|
||||
if ($[0] !== cond1 || $[1] !== cond2) {
|
||||
const arr = makeArray({ a: 2 }, 2, []);
|
||||
|
||||
t1 = cond1 ? (
|
||||
<>
|
||||
<div>{identity("foo")}</div>
|
||||
|
|
@ -65,10 +63,9 @@ function Foo(t0) {
|
|||
) : null;
|
||||
$[0] = cond1;
|
||||
$[1] = cond2;
|
||||
$[2] = arr;
|
||||
$[3] = t1;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
t1 = $[2];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// @enableReactiveScopesInHIR:false
|
||||
|
||||
import {Stringify, identity, makeArray, mutate} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableReactiveScopesInHIR:false
|
||||
|
||||
/**
|
||||
* This is a weird case as data has type `BuiltInMixedReadonly`.
|
||||
* The only scoped value we currently infer in this program is the
|
||||
* PropertyLoad `data?.toString`.
|
||||
*/
|
||||
import {useFragment} from 'shared-runtime';
|
||||
|
||||
function Foo() {
|
||||
const data = useFragment();
|
||||
return [data?.toString() || ''];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false
|
||||
|
||||
/**
|
||||
* This is a weird case as data has type `BuiltInMixedReadonly`.
|
||||
* The only scoped value we currently infer in this program is the
|
||||
* PropertyLoad `data?.toString`.
|
||||
*/
|
||||
import { useFragment } from "shared-runtime";
|
||||
|
||||
function Foo() {
|
||||
const $ = _c(2);
|
||||
const data = useFragment();
|
||||
const t0 = data?.toString() || "";
|
||||
let t1;
|
||||
if ($[0] !== t0) {
|
||||
t1 = [t0];
|
||||
$[0] = t0;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) ["[object Object]"]
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// @enableReactiveScopesInHIR:false
|
||||
|
||||
/**
|
||||
* This is a weird case as data has type `BuiltInMixedReadonly`.
|
||||
* The only scoped value we currently infer in this program is the
|
||||
* PropertyLoad `data?.toString`.
|
||||
*/
|
||||
import {useFragment} from 'shared-runtime';
|
||||
|
||||
function Foo() {
|
||||
const data = useFragment();
|
||||
return [data?.toString() || ''];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [],
|
||||
};
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableReactiveScopesInHIR:false
|
||||
import {StaticText1, Stringify, identity, useHook} from 'shared-runtime';
|
||||
/**
|
||||
* `button` and `dispatcher` must end up in the same memo block. It would be
|
||||
* invalid for `button` to take a dependency on `dispatcher` as dispatcher
|
||||
* is created later.
|
||||
*
|
||||
* Sprout error:
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) "[[ function params=1 ]]"
|
||||
* Forget:
|
||||
* (kind: exception) Cannot access 'dispatcher' before initialization
|
||||
*/
|
||||
function useFoo({onClose}) {
|
||||
const button = StaticText1 ?? (
|
||||
<Stringify
|
||||
primary={{
|
||||
label: identity('label'),
|
||||
onPress: onClose,
|
||||
}}
|
||||
secondary={{
|
||||
onPress: () => {
|
||||
dispatcher.go('route2');
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const dispatcher = useHook();
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{onClose: identity()}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false
|
||||
import { StaticText1, Stringify, identity, useHook } from "shared-runtime";
|
||||
/**
|
||||
* `button` and `dispatcher` must end up in the same memo block. It would be
|
||||
* invalid for `button` to take a dependency on `dispatcher` as dispatcher
|
||||
* is created later.
|
||||
*
|
||||
* Sprout error:
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) "[[ function params=1 ]]"
|
||||
* Forget:
|
||||
* (kind: exception) Cannot access 'dispatcher' before initialization
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(3);
|
||||
const { onClose } = t0;
|
||||
let t1;
|
||||
if ($[0] !== onClose || $[1] !== dispatcher) {
|
||||
t1 = StaticText1 ?? (
|
||||
<Stringify
|
||||
primary={{ label: identity("label"), onPress: onClose }}
|
||||
secondary={{
|
||||
onPress: () => {
|
||||
dispatcher.go("route2");
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
$[0] = onClose;
|
||||
$[1] = dispatcher;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
const button = t1;
|
||||
|
||||
const dispatcher = useHook();
|
||||
return button;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ onClose: identity() }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
// @enableReactiveScopesInHIR:false
|
||||
import {StaticText1, Stringify, identity, useHook} from 'shared-runtime';
|
||||
/**
|
||||
* `button` and `dispatcher` must end up in the same memo block. It would be
|
||||
* invalid for `button` to take a dependency on `dispatcher` as dispatcher
|
||||
* is created later.
|
||||
*
|
||||
* Sprout error:
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) "[[ function params=1 ]]"
|
||||
* Forget:
|
||||
* (kind: exception) Cannot access 'dispatcher' before initialization
|
||||
*/
|
||||
function useFoo({onClose}) {
|
||||
const button = StaticText1 ?? (
|
||||
<Stringify
|
||||
primary={{
|
||||
label: identity('label'),
|
||||
onPress: onClose,
|
||||
}}
|
||||
secondary={{
|
||||
onPress: () => {
|
||||
dispatcher.go('route2');
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const dispatcher = useHook();
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{onClose: identity()}],
|
||||
};
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableReactiveScopesInHIR:false
|
||||
import {identity, mutate} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* The root cause of this bug is in `InferReactiveScopeVariables`. Currently,
|
||||
* InferReactiveScopeVariables do not ensure that maybe-aliased values get
|
||||
* assigned the same reactive scope. This is safe only when an already-
|
||||
* constructed value is captured, e.g.
|
||||
* ```js
|
||||
* const x = makeObj(); ⌝ mutable range of x
|
||||
* mutate(x); ⌟
|
||||
* <-- after this point, we can produce a canonical version
|
||||
* of x for all following aliases
|
||||
* const y = [];
|
||||
* y.push(x); <-- y captures x
|
||||
* ```
|
||||
*
|
||||
* However, if a value is captured/aliased during its mutable range and the
|
||||
* capturing container is separately memoized, it becomes difficult to guarantee
|
||||
* that all aliases refer to the same value.
|
||||
*
|
||||
* Sprout error:
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) [{"wat0":"joe"},3]
|
||||
* [{"wat0":"joe"},3]
|
||||
* Forget:
|
||||
* (kind: ok) [{"wat0":"joe"},3]
|
||||
* [[ (exception in render) Error: oh no! ]]
|
||||
*
|
||||
*/
|
||||
function useFoo({a, b}) {
|
||||
const x = {a};
|
||||
const y = {};
|
||||
mutate(x);
|
||||
const z = [identity(y), b];
|
||||
mutate(y);
|
||||
|
||||
if (z[0] !== y) {
|
||||
throw new Error('oh no!');
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: 2, b: 3}],
|
||||
sequentialRenders: [
|
||||
{a: 2, b: 3},
|
||||
{a: 4, b: 3},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false
|
||||
import { identity, mutate } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* The root cause of this bug is in `InferReactiveScopeVariables`. Currently,
|
||||
* InferReactiveScopeVariables do not ensure that maybe-aliased values get
|
||||
* assigned the same reactive scope. This is safe only when an already-
|
||||
* constructed value is captured, e.g.
|
||||
* ```js
|
||||
* const x = makeObj(); ⌝ mutable range of x
|
||||
* mutate(x); ⌟
|
||||
* <-- after this point, we can produce a canonical version
|
||||
* of x for all following aliases
|
||||
* const y = [];
|
||||
* y.push(x); <-- y captures x
|
||||
* ```
|
||||
*
|
||||
* However, if a value is captured/aliased during its mutable range and the
|
||||
* capturing container is separately memoized, it becomes difficult to guarantee
|
||||
* that all aliases refer to the same value.
|
||||
*
|
||||
* Sprout error:
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) [{"wat0":"joe"},3]
|
||||
* [{"wat0":"joe"},3]
|
||||
* Forget:
|
||||
* (kind: ok) [{"wat0":"joe"},3]
|
||||
* [[ (exception in render) Error: oh no! ]]
|
||||
*
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(6);
|
||||
const { a, b } = t0;
|
||||
let z;
|
||||
let y;
|
||||
if ($[0] !== a || $[1] !== b) {
|
||||
const x = { a };
|
||||
y = {};
|
||||
mutate(x);
|
||||
let t1;
|
||||
if ($[4] !== b) {
|
||||
t1 = [identity(y), b];
|
||||
$[4] = b;
|
||||
$[5] = t1;
|
||||
} else {
|
||||
t1 = $[5];
|
||||
}
|
||||
z = t1;
|
||||
mutate(y);
|
||||
$[0] = a;
|
||||
$[1] = b;
|
||||
$[2] = z;
|
||||
$[3] = y;
|
||||
} else {
|
||||
z = $[2];
|
||||
y = $[3];
|
||||
}
|
||||
if (z[0] !== y) {
|
||||
throw new Error("oh no!");
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ a: 2, b: 3 }],
|
||||
sequentialRenders: [
|
||||
{ a: 2, b: 3 },
|
||||
{ a: 4, b: 3 },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
// @enableReactiveScopesInHIR:false
|
||||
import {identity, mutate} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* The root cause of this bug is in `InferReactiveScopeVariables`. Currently,
|
||||
* InferReactiveScopeVariables do not ensure that maybe-aliased values get
|
||||
* assigned the same reactive scope. This is safe only when an already-
|
||||
* constructed value is captured, e.g.
|
||||
* ```js
|
||||
* const x = makeObj(); ⌝ mutable range of x
|
||||
* mutate(x); ⌟
|
||||
* <-- after this point, we can produce a canonical version
|
||||
* of x for all following aliases
|
||||
* const y = [];
|
||||
* y.push(x); <-- y captures x
|
||||
* ```
|
||||
*
|
||||
* However, if a value is captured/aliased during its mutable range and the
|
||||
* capturing container is separately memoized, it becomes difficult to guarantee
|
||||
* that all aliases refer to the same value.
|
||||
*
|
||||
* Sprout error:
|
||||
* Found differences in evaluator results
|
||||
* Non-forget (expected):
|
||||
* (kind: ok) [{"wat0":"joe"},3]
|
||||
* [{"wat0":"joe"},3]
|
||||
* Forget:
|
||||
* (kind: ok) [{"wat0":"joe"},3]
|
||||
* [[ (exception in render) Error: oh no! ]]
|
||||
*
|
||||
*/
|
||||
function useFoo({a, b}) {
|
||||
const x = {a};
|
||||
const y = {};
|
||||
mutate(x);
|
||||
const z = [identity(y), b];
|
||||
mutate(y);
|
||||
|
||||
if (z[0] !== y) {
|
||||
throw new Error('oh no!');
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: 2, b: 3}],
|
||||
sequentialRenders: [
|
||||
{a: 2, b: 3},
|
||||
{a: 4, b: 3},
|
||||
],
|
||||
};
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableReactiveScopesInHIR:false
|
||||
import {useRef} from 'react';
|
||||
import {addOne} from 'shared-runtime';
|
||||
|
||||
function useKeyCommand() {
|
||||
const currentPosition = useRef(0);
|
||||
const handleKey = direction => () => {
|
||||
const position = currentPosition.current;
|
||||
const nextPosition = direction === 'left' ? addOne(position) : position;
|
||||
currentPosition.current = nextPosition;
|
||||
};
|
||||
const moveLeft = {
|
||||
handler: handleKey('left'),
|
||||
};
|
||||
const moveRight = {
|
||||
handler: handleKey('right'),
|
||||
};
|
||||
return [moveLeft, moveRight];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useKeyCommand,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
11 | };
|
||||
12 | const moveLeft = {
|
||||
> 13 | handler: handleKey('left'),
|
||||
| ^^^^^^^^^ InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) (13:13)
|
||||
|
||||
InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (13:13)
|
||||
|
||||
InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) (16:16)
|
||||
|
||||
InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (16:16)
|
||||
14 | };
|
||||
15 | const moveRight = {
|
||||
16 | handler: handleKey('right'),
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
// @enableReactiveScopesInHIR:false
|
||||
import {useRef} from 'react';
|
||||
import {addOne} from 'shared-runtime';
|
||||
|
||||
function useKeyCommand() {
|
||||
const currentPosition = useRef(0);
|
||||
const handleKey = direction => () => {
|
||||
const position = currentPosition.current;
|
||||
const nextPosition = direction === 'left' ? addOne(position) : position;
|
||||
currentPosition.current = nextPosition;
|
||||
};
|
||||
const moveLeft = {
|
||||
handler: handleKey('left'),
|
||||
};
|
||||
const moveRight = {
|
||||
handler: handleKey('right'),
|
||||
};
|
||||
return [moveLeft, moveRight];
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useKeyCommand,
|
||||
params: [],
|
||||
};
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableReactiveScopesInHIR:false
|
||||
|
||||
import {Stringify, identity, makeArray, mutate} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Here, identity('foo') is an immutable allocating instruction.
|
||||
* `arr` is a mutable value whose mutable range ends at `arr.map`.
|
||||
*
|
||||
* The previous (reactive function) version of alignScopesToBlocks set the range of
|
||||
* both scopes to end at value blocks within the <></> expression.
|
||||
* However, both scope ranges should be aligned to the outer value block
|
||||
* (e.g. `cond1 ? <>: null`). The HIR version of alignScopesToBlocks
|
||||
* handles this correctly.
|
||||
*/
|
||||
function Foo({cond1, cond2}) {
|
||||
const arr = makeArray<any>({a: 2}, 2, []);
|
||||
|
||||
return cond1 ? (
|
||||
<>
|
||||
<div>{identity('foo')}</div>
|
||||
<Stringify value={cond2 ? arr.map(mutate) : null} />
|
||||
</>
|
||||
) : null;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{cond1: true, cond2: true}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false
|
||||
|
||||
import { Stringify, identity, makeArray, mutate } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* Here, identity('foo') is an immutable allocating instruction.
|
||||
* `arr` is a mutable value whose mutable range ends at `arr.map`.
|
||||
*
|
||||
* The previous (reactive function) version of alignScopesToBlocks set the range of
|
||||
* both scopes to end at value blocks within the <></> expression.
|
||||
* However, both scope ranges should be aligned to the outer value block
|
||||
* (e.g. `cond1 ? <>: null`). The HIR version of alignScopesToBlocks
|
||||
* handles this correctly.
|
||||
*/
|
||||
function Foo(t0) {
|
||||
const $ = _c(4);
|
||||
const { cond1, cond2 } = t0;
|
||||
const arr = makeArray({ a: 2 }, 2, []);
|
||||
let t1;
|
||||
if ($[0] !== cond1 || $[1] !== cond2 || $[2] !== arr) {
|
||||
t1 = cond1 ? (
|
||||
<>
|
||||
<div>{identity("foo")}</div>
|
||||
<Stringify value={cond2 ? arr.map(mutate) : null} />
|
||||
</>
|
||||
) : null;
|
||||
$[0] = cond1;
|
||||
$[1] = cond2;
|
||||
$[2] = arr;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{ cond1: true, cond2: true }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>foo</div><div>{"value":[null,null,null]}</div>
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
// @enableReactiveScopesInHIR:false
|
||||
|
||||
import {Stringify, identity, makeArray, mutate} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* Here, identity('foo') is an immutable allocating instruction.
|
||||
* `arr` is a mutable value whose mutable range ends at `arr.map`.
|
||||
*
|
||||
* The previous (reactive function) version of alignScopesToBlocks set the range of
|
||||
* both scopes to end at value blocks within the <></> expression.
|
||||
* However, both scope ranges should be aligned to the outer value block
|
||||
* (e.g. `cond1 ? <>: null`). The HIR version of alignScopesToBlocks
|
||||
* handles this correctly.
|
||||
*/
|
||||
function Foo({cond1, cond2}) {
|
||||
const arr = makeArray<any>({a: 2}, 2, []);
|
||||
|
||||
return cond1 ? (
|
||||
<>
|
||||
<div>{identity('foo')}</div>
|
||||
<Stringify value={cond2 ? arr.map(mutate) : null} />
|
||||
</>
|
||||
) : null;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{cond1: true, cond2: true}],
|
||||
};
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableReactiveScopesInHIR:false
|
||||
import {CONST_TRUE, identity, shallowCopy} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* There are three values with their own scopes in this fixture.
|
||||
* - arr, whose mutable range extends to the `mutate(...)` call
|
||||
* - cond, which has a mutable range of exactly 1 (e.g. created but not
|
||||
* mutated)
|
||||
* - { val: CONST_TRUE }, which is also not mutated after creation. However,
|
||||
* its scope range becomes extended to the value block.
|
||||
*
|
||||
* After AlignScopesToBlockScopes, our scopes look roughly like this
|
||||
* ```js
|
||||
* [1] arr = shallowCopy() ⌝@0
|
||||
* [2] cond = identity() <- @1 |
|
||||
* [3] $0 = Ternary test=cond ⌝@2 |
|
||||
* [4] {val : CONST_TRUE} | |
|
||||
* [5] mutate(arr) | |
|
||||
* [6] return $0 ⌟ ⌟
|
||||
* ```
|
||||
*
|
||||
* Observe that instruction 5 mutates scope 0, which means that scopes 0 and 2
|
||||
* should be merged.
|
||||
*/
|
||||
function useFoo({input}) {
|
||||
const arr = shallowCopy(input);
|
||||
|
||||
const cond = identity(false);
|
||||
return cond ? {val: CONST_TRUE} : mutate(arr);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{input: 3}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false
|
||||
import { CONST_TRUE, identity, shallowCopy } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* There are three values with their own scopes in this fixture.
|
||||
* - arr, whose mutable range extends to the `mutate(...)` call
|
||||
* - cond, which has a mutable range of exactly 1 (e.g. created but not
|
||||
* mutated)
|
||||
* - { val: CONST_TRUE }, which is also not mutated after creation. However,
|
||||
* its scope range becomes extended to the value block.
|
||||
*
|
||||
* After AlignScopesToBlockScopes, our scopes look roughly like this
|
||||
* ```js
|
||||
* [1] arr = shallowCopy() ⌝@0
|
||||
* [2] cond = identity() <- @1 |
|
||||
* [3] $0 = Ternary test=cond ⌝@2 |
|
||||
* [4] {val : CONST_TRUE} | |
|
||||
* [5] mutate(arr) | |
|
||||
* [6] return $0 ⌟ ⌟
|
||||
* ```
|
||||
*
|
||||
* Observe that instruction 5 mutates scope 0, which means that scopes 0 and 2
|
||||
* should be merged.
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(3);
|
||||
const { input } = t0;
|
||||
const arr = shallowCopy(input);
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = identity(false);
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
const cond = t1;
|
||||
let t2;
|
||||
if ($[1] !== arr) {
|
||||
t2 = cond ? { val: CONST_TRUE } : mutate(arr);
|
||||
$[1] = arr;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ input: 3 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) mutate is not defined
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
// @enableReactiveScopesInHIR:false
|
||||
import {CONST_TRUE, identity, shallowCopy} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* There are three values with their own scopes in this fixture.
|
||||
* - arr, whose mutable range extends to the `mutate(...)` call
|
||||
* - cond, which has a mutable range of exactly 1 (e.g. created but not
|
||||
* mutated)
|
||||
* - { val: CONST_TRUE }, which is also not mutated after creation. However,
|
||||
* its scope range becomes extended to the value block.
|
||||
*
|
||||
* After AlignScopesToBlockScopes, our scopes look roughly like this
|
||||
* ```js
|
||||
* [1] arr = shallowCopy() ⌝@0
|
||||
* [2] cond = identity() <- @1 |
|
||||
* [3] $0 = Ternary test=cond ⌝@2 |
|
||||
* [4] {val : CONST_TRUE} | |
|
||||
* [5] mutate(arr) | |
|
||||
* [6] return $0 ⌟ ⌟
|
||||
* ```
|
||||
*
|
||||
* Observe that instruction 5 mutates scope 0, which means that scopes 0 and 2
|
||||
* should be merged.
|
||||
*/
|
||||
function useFoo({input}) {
|
||||
const arr = shallowCopy(input);
|
||||
|
||||
const cond = identity(false);
|
||||
return cond ? {val: CONST_TRUE} : mutate(arr);
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{input: 3}],
|
||||
};
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableReactiveScopesInHIR:false
|
||||
import {mutate} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* This test fixture is similar to mutation-within-jsx. The only difference
|
||||
* is that there is no `freeze` effect here, which means that `z` may be
|
||||
* mutated after its memo block through mutating `y`.
|
||||
*
|
||||
* While this is technically correct (as `z` is a nested memo block), it
|
||||
* is an edge case as we believe that values are not mutated after their
|
||||
* memo blocks (which may lead to 'tearing', i.e. mutating one render's
|
||||
* values in a subsequent render.
|
||||
*/
|
||||
function useFoo({a, b}) {
|
||||
// x and y's scopes start here
|
||||
const x = {a};
|
||||
const y = [b];
|
||||
mutate(x);
|
||||
// z captures the result of `mutate(y)`, which may be aliased to `y`.
|
||||
const z = [mutate(y)];
|
||||
// the following line may also mutate z
|
||||
mutate(y);
|
||||
// and end here
|
||||
return z;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: 2, b: 3}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false
|
||||
import { mutate } from "shared-runtime";
|
||||
|
||||
/**
|
||||
* This test fixture is similar to mutation-within-jsx. The only difference
|
||||
* is that there is no `freeze` effect here, which means that `z` may be
|
||||
* mutated after its memo block through mutating `y`.
|
||||
*
|
||||
* While this is technically correct (as `z` is a nested memo block), it
|
||||
* is an edge case as we believe that values are not mutated after their
|
||||
* memo blocks (which may lead to 'tearing', i.e. mutating one render's
|
||||
* values in a subsequent render.
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(5);
|
||||
const { a, b } = t0;
|
||||
let z;
|
||||
if ($[0] !== a || $[1] !== b) {
|
||||
const x = { a };
|
||||
const y = [b];
|
||||
mutate(x);
|
||||
|
||||
const t1 = mutate(y);
|
||||
let t2;
|
||||
if ($[3] !== t1) {
|
||||
t2 = [t1];
|
||||
$[3] = t1;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
z = t2;
|
||||
|
||||
mutate(y);
|
||||
$[0] = a;
|
||||
$[1] = b;
|
||||
$[2] = z;
|
||||
} else {
|
||||
z = $[2];
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ a: 2, b: 3 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) [null]
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
// @enableReactiveScopesInHIR:false
|
||||
import {mutate} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* This test fixture is similar to mutation-within-jsx. The only difference
|
||||
* is that there is no `freeze` effect here, which means that `z` may be
|
||||
* mutated after its memo block through mutating `y`.
|
||||
*
|
||||
* While this is technically correct (as `z` is a nested memo block), it
|
||||
* is an edge case as we believe that values are not mutated after their
|
||||
* memo blocks (which may lead to 'tearing', i.e. mutating one render's
|
||||
* values in a subsequent render.
|
||||
*/
|
||||
function useFoo({a, b}) {
|
||||
// x and y's scopes start here
|
||||
const x = {a};
|
||||
const y = [b];
|
||||
mutate(x);
|
||||
// z captures the result of `mutate(y)`, which may be aliased to `y`.
|
||||
const z = [mutate(y)];
|
||||
// the following line may also mutate z
|
||||
mutate(y);
|
||||
// and end here
|
||||
return z;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{a: 2, b: 3}],
|
||||
};
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableReactiveScopesInHIR:false
|
||||
import {
|
||||
Stringify,
|
||||
makeObject_Primitives,
|
||||
mutate,
|
||||
mutateAndReturn,
|
||||
} from 'shared-runtime';
|
||||
|
||||
function useFoo({data}) {
|
||||
let obj = null;
|
||||
let myDiv = null;
|
||||
label: {
|
||||
if (data.cond) {
|
||||
obj = makeObject_Primitives();
|
||||
if (data.cond1) {
|
||||
myDiv = <Stringify value={mutateAndReturn(obj)} />;
|
||||
break label;
|
||||
}
|
||||
mutate(obj);
|
||||
}
|
||||
}
|
||||
|
||||
return myDiv;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{data: {cond: true, cond1: true}}],
|
||||
sequentialRenders: [
|
||||
{data: {cond: true, cond1: true}},
|
||||
{data: {cond: true, cond1: true}},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false
|
||||
import {
|
||||
Stringify,
|
||||
makeObject_Primitives,
|
||||
mutate,
|
||||
mutateAndReturn,
|
||||
} from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = _c(5);
|
||||
const { data } = t0;
|
||||
let obj;
|
||||
let myDiv = null;
|
||||
bb0: if (data.cond) {
|
||||
if ($[0] !== data.cond1) {
|
||||
obj = makeObject_Primitives();
|
||||
if (data.cond1) {
|
||||
const t1 = mutateAndReturn(obj);
|
||||
let t2;
|
||||
if ($[3] !== t1) {
|
||||
t2 = <Stringify value={t1} />;
|
||||
$[3] = t1;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
myDiv = t2;
|
||||
break bb0;
|
||||
}
|
||||
|
||||
mutate(obj);
|
||||
$[0] = data.cond1;
|
||||
$[1] = obj;
|
||||
$[2] = myDiv;
|
||||
} else {
|
||||
obj = $[1];
|
||||
myDiv = $[2];
|
||||
}
|
||||
}
|
||||
return myDiv;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ data: { cond: true, cond1: true } }],
|
||||
sequentialRenders: [
|
||||
{ data: { cond: true, cond1: true } },
|
||||
{ data: { cond: true, cond1: true } },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"value":{"a":0,"b":"value1","c":true,"wat0":"joe"}}</div>
|
||||
<div>{"value":{"a":0,"b":"value1","c":true,"wat0":"joe"}}</div>
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// @enableReactiveScopesInHIR:false
|
||||
import {
|
||||
Stringify,
|
||||
makeObject_Primitives,
|
||||
mutate,
|
||||
mutateAndReturn,
|
||||
} from 'shared-runtime';
|
||||
|
||||
function useFoo({data}) {
|
||||
let obj = null;
|
||||
let myDiv = null;
|
||||
label: {
|
||||
if (data.cond) {
|
||||
obj = makeObject_Primitives();
|
||||
if (data.cond1) {
|
||||
myDiv = <Stringify value={mutateAndReturn(obj)} />;
|
||||
break label;
|
||||
}
|
||||
mutate(obj);
|
||||
}
|
||||
}
|
||||
|
||||
return myDiv;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{data: {cond: true, cond1: true}}],
|
||||
sequentialRenders: [
|
||||
{data: {cond: true, cond1: true}},
|
||||
{data: {cond: true, cond1: true}},
|
||||
],
|
||||
};
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableReactiveScopesInHIR:false
|
||||
import {
|
||||
Stringify,
|
||||
makeObject_Primitives,
|
||||
mutateAndReturn,
|
||||
} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* In this example, the `<Stringify ... />` JSX block mutates then captures obj.
|
||||
* As JSX expressions freeze their values, we know that `obj` and `myDiv` cannot
|
||||
* be mutated past this.
|
||||
* This set of mutable range + scopes is an edge case because the JSX expression
|
||||
* references values in two scopes.
|
||||
* - (freeze) the result of `mutateAndReturn`
|
||||
* this is a mutable value with a mutable range starting at `makeObject()`
|
||||
* - (mutate) the lvalue storing the result of `<Stringify .../>`
|
||||
* this is a immutable value and so gets assigned a different scope
|
||||
*
|
||||
* obj@0 = makeObj(); ⌝ scope@0
|
||||
* if (cond) { |
|
||||
* $1@0 = mutate(obj@0); |
|
||||
* myDiv@1 = JSX $1@0 <- scope@1 |
|
||||
* } ⌟
|
||||
*
|
||||
* Coincidentally, the range of `obj` is extended by alignScopesToBlocks to *past*
|
||||
* the end of the JSX instruction. As we currently alias identifier mutableRanges to
|
||||
* scope ranges, this `freeze` reference is perceived as occurring during the mutable
|
||||
* range of `obj` (even though it is after the last mutating reference).
|
||||
*
|
||||
* This case is technically safe as `myDiv` correctly takes `obj` as a dependency. As
|
||||
* a result, developers can never observe myDiv can aliasing a different value generation
|
||||
* than `obj` (e.g. the invariant `myDiv.props.value === obj` always holds).
|
||||
*/
|
||||
function useFoo({data}) {
|
||||
let obj = null;
|
||||
let myDiv = null;
|
||||
if (data.cond) {
|
||||
obj = makeObject_Primitives();
|
||||
if (data.cond1) {
|
||||
myDiv = <Stringify value={mutateAndReturn(obj)} />;
|
||||
}
|
||||
}
|
||||
return myDiv;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{data: {cond: true, cond1: true}}],
|
||||
sequentialRenders: [
|
||||
{data: {cond: true, cond1: true}},
|
||||
{data: {cond: true, cond1: true}},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false
|
||||
import {
|
||||
Stringify,
|
||||
makeObject_Primitives,
|
||||
mutateAndReturn,
|
||||
} from "shared-runtime";
|
||||
|
||||
/**
|
||||
* In this example, the `<Stringify ... />` JSX block mutates then captures obj.
|
||||
* As JSX expressions freeze their values, we know that `obj` and `myDiv` cannot
|
||||
* be mutated past this.
|
||||
* This set of mutable range + scopes is an edge case because the JSX expression
|
||||
* references values in two scopes.
|
||||
* - (freeze) the result of `mutateAndReturn`
|
||||
* this is a mutable value with a mutable range starting at `makeObject()`
|
||||
* - (mutate) the lvalue storing the result of `<Stringify .../>`
|
||||
* this is a immutable value and so gets assigned a different scope
|
||||
*
|
||||
* obj@0 = makeObj(); ⌝ scope@0
|
||||
* if (cond) { |
|
||||
* $1@0 = mutate(obj@0); |
|
||||
* myDiv@1 = JSX $1@0 <- scope@1 |
|
||||
* } ⌟
|
||||
*
|
||||
* Coincidentally, the range of `obj` is extended by alignScopesToBlocks to *past*
|
||||
* the end of the JSX instruction. As we currently alias identifier mutableRanges to
|
||||
* scope ranges, this `freeze` reference is perceived as occurring during the mutable
|
||||
* range of `obj` (even though it is after the last mutating reference).
|
||||
*
|
||||
* This case is technically safe as `myDiv` correctly takes `obj` as a dependency. As
|
||||
* a result, developers can never observe myDiv can aliasing a different value generation
|
||||
* than `obj` (e.g. the invariant `myDiv.props.value === obj` always holds).
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(5);
|
||||
const { data } = t0;
|
||||
let obj;
|
||||
let myDiv = null;
|
||||
if (data.cond) {
|
||||
if ($[0] !== data.cond1) {
|
||||
obj = makeObject_Primitives();
|
||||
if (data.cond1) {
|
||||
const t1 = mutateAndReturn(obj);
|
||||
let t2;
|
||||
if ($[3] !== t1) {
|
||||
t2 = <Stringify value={t1} />;
|
||||
$[3] = t1;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
myDiv = t2;
|
||||
}
|
||||
$[0] = data.cond1;
|
||||
$[1] = obj;
|
||||
$[2] = myDiv;
|
||||
} else {
|
||||
obj = $[1];
|
||||
myDiv = $[2];
|
||||
}
|
||||
}
|
||||
return myDiv;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ data: { cond: true, cond1: true } }],
|
||||
sequentialRenders: [
|
||||
{ data: { cond: true, cond1: true } },
|
||||
{ data: { cond: true, cond1: true } },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"value":{"a":0,"b":"value1","c":true,"wat0":"joe"}}</div>
|
||||
<div>{"value":{"a":0,"b":"value1","c":true,"wat0":"joe"}}</div>
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
// @enableReactiveScopesInHIR:false
|
||||
import {
|
||||
Stringify,
|
||||
makeObject_Primitives,
|
||||
mutateAndReturn,
|
||||
} from 'shared-runtime';
|
||||
|
||||
/**
|
||||
* In this example, the `<Stringify ... />` JSX block mutates then captures obj.
|
||||
* As JSX expressions freeze their values, we know that `obj` and `myDiv` cannot
|
||||
* be mutated past this.
|
||||
* This set of mutable range + scopes is an edge case because the JSX expression
|
||||
* references values in two scopes.
|
||||
* - (freeze) the result of `mutateAndReturn`
|
||||
* this is a mutable value with a mutable range starting at `makeObject()`
|
||||
* - (mutate) the lvalue storing the result of `<Stringify .../>`
|
||||
* this is a immutable value and so gets assigned a different scope
|
||||
*
|
||||
* obj@0 = makeObj(); ⌝ scope@0
|
||||
* if (cond) { |
|
||||
* $1@0 = mutate(obj@0); |
|
||||
* myDiv@1 = JSX $1@0 <- scope@1 |
|
||||
* } ⌟
|
||||
*
|
||||
* Coincidentally, the range of `obj` is extended by alignScopesToBlocks to *past*
|
||||
* the end of the JSX instruction. As we currently alias identifier mutableRanges to
|
||||
* scope ranges, this `freeze` reference is perceived as occurring during the mutable
|
||||
* range of `obj` (even though it is after the last mutating reference).
|
||||
*
|
||||
* This case is technically safe as `myDiv` correctly takes `obj` as a dependency. As
|
||||
* a result, developers can never observe myDiv can aliasing a different value generation
|
||||
* than `obj` (e.g. the invariant `myDiv.props.value === obj` always holds).
|
||||
*/
|
||||
function useFoo({data}) {
|
||||
let obj = null;
|
||||
let myDiv = null;
|
||||
if (data.cond) {
|
||||
obj = makeObject_Primitives();
|
||||
if (data.cond1) {
|
||||
myDiv = <Stringify value={mutateAndReturn(obj)} />;
|
||||
}
|
||||
}
|
||||
return myDiv;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{data: {cond: true, cond1: true}}],
|
||||
sequentialRenders: [
|
||||
{data: {cond: true, cond1: true}},
|
||||
{data: {cond: true, cond1: true}},
|
||||
],
|
||||
};
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableReactiveScopesInHIR:false
|
||||
import {identity, makeObject_Primitives} from 'shared-runtime';
|
||||
|
||||
function useTest({cond}) {
|
||||
const val = makeObject_Primitives();
|
||||
|
||||
useHook();
|
||||
/**
|
||||
* We don't technically need a reactive scope for this ternary as
|
||||
* it cannot produce newly allocated values.
|
||||
* While identity(...) may allocate, we can teach the compiler that
|
||||
* its result is only used as as a test condition
|
||||
*/
|
||||
const result = identity(cond) ? val : null;
|
||||
return result;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useTest,
|
||||
params: [{cond: true}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false
|
||||
import { identity, makeObject_Primitives } from "shared-runtime";
|
||||
|
||||
function useTest(t0) {
|
||||
const $ = _c(1);
|
||||
const { cond } = t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = makeObject_Primitives();
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
const val = t1;
|
||||
|
||||
useHook();
|
||||
|
||||
const result = identity(cond) ? val : null;
|
||||
return result;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useTest,
|
||||
params: [{ cond: true }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) useHook is not defined
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// @enableReactiveScopesInHIR:false
|
||||
import {identity, makeObject_Primitives} from 'shared-runtime';
|
||||
|
||||
function useTest({cond}) {
|
||||
const val = makeObject_Primitives();
|
||||
|
||||
useHook();
|
||||
/**
|
||||
* We don't technically need a reactive scope for this ternary as
|
||||
* it cannot produce newly allocated values.
|
||||
* While identity(...) may allocate, we can teach the compiler that
|
||||
* its result is only used as as a test condition
|
||||
*/
|
||||
const result = identity(cond) ? val : null;
|
||||
return result;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useTest,
|
||||
params: [{cond: true}],
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user