mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[compiler][newinference] Fixes, update remaining snapshots
A bunch of small fixes to make the remaining fixtures work. This is really really close now.
ghstack-source-id: a3d6803eb4
Pull Request resolved: https://github.com/facebook/react/pull/33477
This commit is contained in:
@@ -247,7 +247,6 @@ function runWithEnvironment(
|
||||
}
|
||||
|
||||
if (!env.config.enableNewMutationAliasingModel) {
|
||||
// NOTE: in the new model this is part of validateNoFreezingKnownMutableFunctions
|
||||
validateLocalsNotReassignedAfterRender(hir);
|
||||
}
|
||||
|
||||
@@ -275,6 +274,7 @@ function runWithEnvironment(
|
||||
if (mutabilityAliasingErrors.isErr()) {
|
||||
throw mutabilityAliasingErrors.unwrapErr();
|
||||
}
|
||||
validateLocalsNotReassignedAfterRender(hir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+48
-15
@@ -43,6 +43,7 @@ import {
|
||||
} from '../HIR/visitors';
|
||||
import {Ok, Result} from '../Utils/Result';
|
||||
import {
|
||||
getArgumentEffect,
|
||||
getFunctionCallSignature,
|
||||
isKnownMutableEffect,
|
||||
mergeValueKinds,
|
||||
@@ -547,7 +548,11 @@ function applyEffect(
|
||||
effect =>
|
||||
effect.kind === 'MutateFrozen' || effect.kind === 'MutateGlobal',
|
||||
);
|
||||
const isMutable = hasCaptures || hasTrackedSideEffects;
|
||||
// For legacy compatibility
|
||||
const capturesRef = effect.function.loweredFunc.func.context.some(
|
||||
operand => isRefOrRefValue(operand.identifier),
|
||||
);
|
||||
const isMutable = hasCaptures || hasTrackedSideEffects || capturesRef;
|
||||
for (const operand of effect.function.loweredFunc.func.context) {
|
||||
if (operand.effect !== Effect.Capture) {
|
||||
continue;
|
||||
@@ -805,6 +810,11 @@ function applyEffect(
|
||||
effects,
|
||||
);
|
||||
}
|
||||
const mutateIterator =
|
||||
arg.kind === 'Spread' ? conditionallyMutateIterator(operand) : null;
|
||||
if (mutateIterator) {
|
||||
applyEffect(context, state, mutateIterator, aliased, effects);
|
||||
}
|
||||
applyEffect(
|
||||
context,
|
||||
state,
|
||||
@@ -1299,6 +1309,22 @@ type InstructionSignature = {
|
||||
effects: ReadonlyArray<AliasingEffect>;
|
||||
};
|
||||
|
||||
function conditionallyMutateIterator(place: Place): AliasingEffect | null {
|
||||
if (
|
||||
!(
|
||||
isArrayType(place.identifier) ||
|
||||
isSetType(place.identifier) ||
|
||||
isMapType(place.identifier)
|
||||
)
|
||||
) {
|
||||
return {
|
||||
kind: 'MutateTransitiveConditionally',
|
||||
value: place,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes an effect signature for the instruction _without_ looking at the inference state,
|
||||
* and only using the semantics of the instructions and the inferred types. The idea is to make
|
||||
@@ -1334,6 +1360,10 @@ function computeSignatureForInstruction(
|
||||
into: lvalue,
|
||||
});
|
||||
} else if (element.kind === 'Spread') {
|
||||
const mutateIterator = conditionallyMutateIterator(element.place);
|
||||
if (mutateIterator != null) {
|
||||
effects.push(mutateIterator);
|
||||
}
|
||||
effects.push({
|
||||
kind: 'Capture',
|
||||
from: element.place,
|
||||
@@ -1567,7 +1597,7 @@ function computeSignatureForInstruction(
|
||||
// Extracts part of the original collection into the result
|
||||
effects.push({
|
||||
kind: 'CreateFrom',
|
||||
from: value.iterator,
|
||||
from: value.collection,
|
||||
into: lvalue,
|
||||
});
|
||||
break;
|
||||
@@ -1623,11 +1653,20 @@ function computeSignatureForInstruction(
|
||||
}
|
||||
case 'Destructure': {
|
||||
for (const patternLValue of eachInstructionValueLValue(value)) {
|
||||
effects.push({
|
||||
kind: 'CreateFrom',
|
||||
from: value.value,
|
||||
into: patternLValue,
|
||||
});
|
||||
if (isPrimitiveType(patternLValue.identifier)) {
|
||||
effects.push({
|
||||
kind: 'Create',
|
||||
into: patternLValue,
|
||||
value: ValueKind.Primitive,
|
||||
reason: ValueReason.Other,
|
||||
});
|
||||
} else {
|
||||
effects.push({
|
||||
kind: 'CreateFrom',
|
||||
from: value.value,
|
||||
into: patternLValue,
|
||||
});
|
||||
}
|
||||
}
|
||||
effects.push({kind: 'Assign', from: value.value, into: lvalue});
|
||||
break;
|
||||
@@ -1947,17 +1986,11 @@ function computeEffectsForLegacySignature(
|
||||
continue;
|
||||
}
|
||||
const place = arg.kind === 'Identifier' ? arg : arg.place;
|
||||
const effect =
|
||||
const signatureEffect =
|
||||
arg.kind === 'Identifier' && i < signature.positionalParams.length
|
||||
? signature.positionalParams[i]!
|
||||
: (signature.restParam ?? Effect.ConditionallyMutate);
|
||||
|
||||
if (arg.kind === 'Spread' && effect === Effect.Freeze) {
|
||||
CompilerError.throwTodo({
|
||||
reason: 'Support spread syntax for hook arguments',
|
||||
loc: arg.place.loc,
|
||||
});
|
||||
}
|
||||
const effect = getArgumentEffect(signatureEffect, arg);
|
||||
|
||||
visit(place, effect);
|
||||
}
|
||||
|
||||
+14
-4
@@ -121,7 +121,12 @@ export function inferMutationAliasingRanges(
|
||||
});
|
||||
} else if (effect.kind === 'CreateFrom') {
|
||||
state.createFrom(index++, effect.from, effect.into);
|
||||
} else if (effect.kind === 'Assign' || effect.kind === 'Alias') {
|
||||
} else if (effect.kind === 'Assign') {
|
||||
if (!state.nodes.has(effect.into.identifier)) {
|
||||
state.create(effect.into, {kind: 'Object'});
|
||||
}
|
||||
state.assign(index++, effect.from, effect.into);
|
||||
} else if (effect.kind === 'Alias') {
|
||||
state.assign(index++, effect.from, effect.into);
|
||||
} else if (effect.kind === 'Capture') {
|
||||
state.capture(index++, effect.from, effect.into);
|
||||
@@ -204,8 +209,8 @@ export function inferMutationAliasingRanges(
|
||||
errors,
|
||||
);
|
||||
}
|
||||
if (VERBOSE) {
|
||||
console.log(state.debug());
|
||||
if (DEBUG) {
|
||||
console.log(pretty([...state.nodes.keys()]));
|
||||
}
|
||||
fn.aliasingEffects ??= [];
|
||||
for (const param of [...fn.context, ...fn.params]) {
|
||||
@@ -534,6 +539,11 @@ class AliasingState {
|
||||
loc: SourceLocation,
|
||||
errors: CompilerError,
|
||||
): void {
|
||||
if (DEBUG) {
|
||||
console.log(
|
||||
`mutate ix=${index} start=$${start.id} end=[${end}]${transitive ? ' transitive' : ''} kind=${kind}`,
|
||||
);
|
||||
}
|
||||
const seen = new Set<Identifier>();
|
||||
const queue: Array<{
|
||||
place: Identifier;
|
||||
@@ -557,7 +567,7 @@ class AliasingState {
|
||||
}
|
||||
if (DEBUG) {
|
||||
console.log(
|
||||
`[${end}] mutate index=${index} ${printIdentifier(start)}: ${printIdentifier(node.id)}`,
|
||||
` mutate $${node.id.id} transitive=${transitive} direction=${direction}`,
|
||||
);
|
||||
}
|
||||
node.id.mutableRange.end = makeInstructionId(
|
||||
|
||||
+1
-1
@@ -1984,7 +1984,7 @@ function areArgumentsImmutableAndNonMutating(
|
||||
return true;
|
||||
}
|
||||
|
||||
function getArgumentEffect(
|
||||
export function getArgumentEffect(
|
||||
signatureEffect: Effect | null,
|
||||
arg: Place | SpreadPattern,
|
||||
): Effect {
|
||||
|
||||
+4
-1
@@ -131,7 +131,10 @@ export function validateNoFreezingKnownMutableFunctions(
|
||||
lvalue.identifier.id,
|
||||
knownMutation,
|
||||
);
|
||||
} else if (context.has(effect.value.identifier.id)) {
|
||||
} else if (
|
||||
context.has(effect.value.identifier.id) &&
|
||||
!isRefOrRefLikeMutableType(effect.value.identifier.type)
|
||||
) {
|
||||
contextMutationEffects.set(lvalue.identifier.id, {
|
||||
kind: 'ContextMutation',
|
||||
effect: Effect.Mutate,
|
||||
|
||||
+3
-10
@@ -175,21 +175,14 @@ import {
|
||||
* and mutability.
|
||||
*/
|
||||
function Component(t0) {
|
||||
const $ = _c(4);
|
||||
const $ = _c(2);
|
||||
const { prop } = t0;
|
||||
let t1;
|
||||
if ($[0] !== prop) {
|
||||
const obj = shallowCopy(prop);
|
||||
const aliasedObj = identity(obj);
|
||||
let t2;
|
||||
if ($[2] !== obj) {
|
||||
t2 = [obj.id];
|
||||
$[2] = obj;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
const id = t2;
|
||||
|
||||
const id = [obj.id];
|
||||
|
||||
mutate(aliasedObj);
|
||||
setPropertyByKey(aliasedObj, "id", prop.id + 1);
|
||||
|
||||
+21
-5
@@ -23,18 +23,34 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function Component(props) {
|
||||
const $ = _c(2);
|
||||
const $ = _c(6);
|
||||
let t0;
|
||||
if ($[0] !== props.a) {
|
||||
const item = { a: props.a };
|
||||
const items = [item];
|
||||
t0 = items.map(_temp);
|
||||
t0 = { a: props.a };
|
||||
$[0] = props.a;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const mapped = t0;
|
||||
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;
|
||||
return mapped;
|
||||
}
|
||||
function _temp(item_0) {
|
||||
|
||||
+7
-15
@@ -84,19 +84,11 @@ import { makeArray, mutate } from "shared-runtime";
|
||||
* used when we analyze CallExpressions.
|
||||
*/
|
||||
function Component(t0) {
|
||||
const $ = _c(5);
|
||||
const $ = _c(3);
|
||||
const { foo, bar } = t0;
|
||||
let t1;
|
||||
if ($[0] !== foo) {
|
||||
t1 = { foo };
|
||||
$[0] = foo;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const x = t1;
|
||||
let y;
|
||||
if ($[2] !== bar || $[3] !== x) {
|
||||
if ($[0] !== bar || $[1] !== foo) {
|
||||
const x = { foo };
|
||||
y = { bar };
|
||||
const f0 = function () {
|
||||
const a = makeArray(y);
|
||||
@@ -107,11 +99,11 @@ function Component(t0) {
|
||||
|
||||
f0();
|
||||
mutate(y.x);
|
||||
$[2] = bar;
|
||||
$[3] = x;
|
||||
$[4] = y;
|
||||
$[0] = bar;
|
||||
$[1] = foo;
|
||||
$[2] = y;
|
||||
} else {
|
||||
y = $[4];
|
||||
y = $[2];
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
+2
-20
@@ -38,7 +38,6 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { CONST_TRUE, Stringify, mutate, useIdentity } from "shared-runtime";
|
||||
|
||||
/**
|
||||
@@ -54,31 +53,14 @@ import { CONST_TRUE, Stringify, mutate, useIdentity } from "shared-runtime";
|
||||
*
|
||||
*/
|
||||
function Component() {
|
||||
const $ = _c(4);
|
||||
const obj = CONST_TRUE ? { inner: { value: "hello" } } : null;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = [obj?.inner];
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const boxedInner = t0;
|
||||
const boxedInner = [obj?.inner];
|
||||
useIdentity(null);
|
||||
mutate(obj);
|
||||
if (boxedInner[0] !== obj?.inner) {
|
||||
throw new Error("invariant broken");
|
||||
}
|
||||
let t1;
|
||||
if ($[1] !== boxedInner || $[2] !== obj) {
|
||||
t1 = <Stringify obj={obj} inner={boxedInner} />;
|
||||
$[1] = boxedInner;
|
||||
$[2] = obj;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
return <Stringify obj={obj} inner={boxedInner} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+10
-36
@@ -56,47 +56,21 @@ import { identity, mutate } from "shared-runtime";
|
||||
* [{"[object Object]":[42]},{"wat0":"joe","wat1":"joe","wat2":"joe"}]
|
||||
*/
|
||||
function Component(props) {
|
||||
const $ = _c(8);
|
||||
let key;
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
key = {};
|
||||
t0 = (mutate(key), key);
|
||||
$[0] = key;
|
||||
if ($[0] !== props.value) {
|
||||
const key = {};
|
||||
const tmp = (mutate(key), key);
|
||||
const context = { [tmp]: identity([props.value]) };
|
||||
|
||||
mutate(key);
|
||||
t0 = [context, key];
|
||||
$[0] = props.value;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
key = $[0];
|
||||
t0 = $[1];
|
||||
}
|
||||
const tmp = t0;
|
||||
let t1;
|
||||
if ($[2] !== props.value) {
|
||||
t1 = identity([props.value]);
|
||||
$[2] = props.value;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
let t2;
|
||||
if ($[4] !== t1) {
|
||||
t2 = { [tmp]: t1 };
|
||||
$[4] = t1;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
const context = t2;
|
||||
|
||||
mutate(key);
|
||||
let t3;
|
||||
if ($[6] !== context) {
|
||||
t3 = [context, key];
|
||||
$[6] = context;
|
||||
$[7] = t3;
|
||||
} else {
|
||||
t3 = $[7];
|
||||
}
|
||||
return t3;
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+35
-69
@@ -66,88 +66,54 @@ const Codes = {
|
||||
};
|
||||
|
||||
function Component(a) {
|
||||
const $ = _c(13);
|
||||
const $ = _c(4);
|
||||
let keys;
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] !== a) {
|
||||
t1 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
if (a) {
|
||||
keys = Object.keys(Codes);
|
||||
} else {
|
||||
t1 = null;
|
||||
break bb0;
|
||||
}
|
||||
|
||||
t0 = keys.map(_temp);
|
||||
if (a) {
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = Object.keys(Codes);
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
$[0] = a;
|
||||
keys = t0;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
let t0;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = keys.map(_temp);
|
||||
$[1] = t0;
|
||||
$[2] = t1;
|
||||
$[3] = keys;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
t1 = $[2];
|
||||
keys = $[3];
|
||||
}
|
||||
if (t1 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t1;
|
||||
}
|
||||
const options = t0;
|
||||
let t1;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = (
|
||||
<ValidateMemoization inputs={[]} output={keys} onlyCheckCompiled={true} />
|
||||
);
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
let t2;
|
||||
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = [];
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
let t3;
|
||||
if ($[5] !== keys) {
|
||||
t3 = (
|
||||
<ValidateMemoization inputs={t2} output={keys} onlyCheckCompiled={true} />
|
||||
);
|
||||
$[5] = keys;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
let t4;
|
||||
if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t4 = [];
|
||||
$[7] = t4;
|
||||
} else {
|
||||
t4 = $[7];
|
||||
}
|
||||
let t5;
|
||||
if ($[8] !== options) {
|
||||
t5 = (
|
||||
<ValidateMemoization
|
||||
inputs={t4}
|
||||
output={options}
|
||||
onlyCheckCompiled={true}
|
||||
/>
|
||||
);
|
||||
$[8] = options;
|
||||
$[9] = t5;
|
||||
} else {
|
||||
t5 = $[9];
|
||||
}
|
||||
let t6;
|
||||
if ($[10] !== t3 || $[11] !== t5) {
|
||||
t6 = (
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = (
|
||||
<>
|
||||
{t3}
|
||||
{t5}
|
||||
{t1}
|
||||
<ValidateMemoization
|
||||
inputs={[]}
|
||||
output={options}
|
||||
onlyCheckCompiled={true}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
$[10] = t3;
|
||||
$[11] = t5;
|
||||
$[12] = t6;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t6 = $[12];
|
||||
t2 = $[3];
|
||||
}
|
||||
return t6;
|
||||
return t2;
|
||||
}
|
||||
function _temp(code) {
|
||||
const country = Codes[code];
|
||||
|
||||
+14
-6
@@ -25,17 +25,25 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function bar(a) {
|
||||
const $ = _c(2);
|
||||
let y;
|
||||
const $ = _c(4);
|
||||
let t0;
|
||||
if ($[0] !== a) {
|
||||
const x = [a];
|
||||
t0 = [a];
|
||||
$[0] = a;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const x = t0;
|
||||
let y;
|
||||
if ($[2] !== x[0][1]) {
|
||||
y = {};
|
||||
|
||||
y = x[0][1];
|
||||
$[0] = a;
|
||||
$[1] = y;
|
||||
$[2] = x[0][1];
|
||||
$[3] = y;
|
||||
} else {
|
||||
y = $[1];
|
||||
y = $[3];
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
+16
-7
@@ -29,20 +29,29 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function bar(a, b) {
|
||||
const $ = _c(3);
|
||||
let y;
|
||||
const $ = _c(6);
|
||||
let t0;
|
||||
if ($[0] !== a || $[1] !== b) {
|
||||
const x = [a, b];
|
||||
t0 = [a, b];
|
||||
$[0] = a;
|
||||
$[1] = b;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
const x = t0;
|
||||
let y;
|
||||
if ($[3] !== x[0][1] || $[4] !== x[1][0]) {
|
||||
y = {};
|
||||
let t = {};
|
||||
|
||||
y = x[0][1];
|
||||
t = x[1][0];
|
||||
$[0] = a;
|
||||
$[1] = b;
|
||||
$[2] = y;
|
||||
$[3] = x[0][1];
|
||||
$[4] = x[1][0];
|
||||
$[5] = y;
|
||||
} else {
|
||||
y = $[2];
|
||||
y = $[5];
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
+14
-6
@@ -25,17 +25,25 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function bar(a) {
|
||||
const $ = _c(2);
|
||||
let y;
|
||||
const $ = _c(4);
|
||||
let t0;
|
||||
if ($[0] !== a) {
|
||||
const x = [a];
|
||||
t0 = [a];
|
||||
$[0] = a;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const x = t0;
|
||||
let y;
|
||||
if ($[2] !== x[0].a[1]) {
|
||||
y = {};
|
||||
|
||||
y = x[0].a[1];
|
||||
$[0] = a;
|
||||
$[1] = y;
|
||||
$[2] = x[0].a[1];
|
||||
$[3] = y;
|
||||
} else {
|
||||
y = $[1];
|
||||
y = $[3];
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
+14
-6
@@ -24,17 +24,25 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
function bar(a) {
|
||||
const $ = _c(2);
|
||||
let y;
|
||||
const $ = _c(4);
|
||||
let t0;
|
||||
if ($[0] !== a) {
|
||||
const x = [a];
|
||||
t0 = [a];
|
||||
$[0] = a;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const x = t0;
|
||||
let y;
|
||||
if ($[2] !== x[0]) {
|
||||
y = {};
|
||||
|
||||
y = x[0];
|
||||
$[0] = a;
|
||||
$[1] = y;
|
||||
$[2] = x[0];
|
||||
$[3] = y;
|
||||
} else {
|
||||
y = $[1];
|
||||
y = $[3];
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ function SomeComponent() {
|
||||
9 | return (
|
||||
10 | <Button
|
||||
> 11 | onPress={() => (sharedVal.value = Math.random())}
|
||||
| ^^^^^^^^^ InvalidReact: Mutating a value returned from a function whose return value should not be mutated. Found mutation of `sharedVal` (11:11)
|
||||
| ^^^^^^^^^ InvalidReact: This mutates a variable that React considers immutable. Found mutation of `sharedVal` (11:11)
|
||||
12 | title="Randomize"
|
||||
13 | />
|
||||
14 | );
|
||||
|
||||
+3
-8
@@ -15,16 +15,11 @@ function useFoo() {
|
||||
## Error
|
||||
|
||||
```
|
||||
1 | function useFoo() {
|
||||
2 | let x = 0;
|
||||
> 3 | return value => {
|
||||
| ^^^^^^^^^^
|
||||
3 | return value => {
|
||||
> 4 | x = value;
|
||||
| ^^^^^^^^^^^^^^
|
||||
> 5 | };
|
||||
| ^^^^ InvalidReact: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead (3:5)
|
||||
|
||||
InvalidReact: The function modifies a local variable here (4:4)
|
||||
| ^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `x` cannot be reassigned after render (4:4)
|
||||
5 | };
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
+7
-13
@@ -47,19 +47,13 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
31 | };
|
||||
32 |
|
||||
> 33 | useEffect(() => {
|
||||
| ^^^^^^^
|
||||
> 34 | onMount();
|
||||
| ^^^^^^^^^^^^^^
|
||||
> 35 | }, [onMount]);
|
||||
| ^^^^ InvalidReact: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead (33:35)
|
||||
|
||||
InvalidReact: The function modifies a local variable here (7:7)
|
||||
36 |
|
||||
37 | return 'ok';
|
||||
38 | }
|
||||
5 |
|
||||
6 | const reassignLocal = newValue => {
|
||||
> 7 | local = newValue;
|
||||
| ^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `local` cannot be reassigned after render (7:7)
|
||||
8 | };
|
||||
9 |
|
||||
10 | const onMount = newValue => {
|
||||
```
|
||||
|
||||
|
||||
+7
-13
@@ -48,19 +48,13 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
32 | };
|
||||
33 |
|
||||
> 34 | useIdentity(() => {
|
||||
| ^^^^^^^
|
||||
> 35 | callback();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
> 36 | });
|
||||
| ^^^^ InvalidReact: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead (34:36)
|
||||
|
||||
InvalidReact: The function modifies a local variable here (8:8)
|
||||
37 |
|
||||
38 | return 'ok';
|
||||
39 | }
|
||||
6 |
|
||||
7 | const reassignLocal = newValue => {
|
||||
> 8 | local = newValue;
|
||||
| ^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `local` cannot be reassigned after render (8:8)
|
||||
9 | };
|
||||
10 |
|
||||
11 | const callback = newValue => {
|
||||
```
|
||||
|
||||
|
||||
+7
-8
@@ -41,14 +41,13 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
29 | };
|
||||
30 |
|
||||
> 31 | return <button onClick={onClick}>Submit</button>;
|
||||
| ^^^^^^^ InvalidReact: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead (31:31)
|
||||
|
||||
InvalidReact: The function modifies a local variable here (5:5)
|
||||
32 | }
|
||||
33 |
|
||||
3 |
|
||||
4 | const reassignLocal = newValue => {
|
||||
> 5 | local = newValue;
|
||||
| ^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `local` cannot be reassigned after render (5:5)
|
||||
6 | };
|
||||
7 |
|
||||
8 | const onClick = newValue => {
|
||||
```
|
||||
|
||||
|
||||
+7
-9
@@ -32,15 +32,13 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Error
|
||||
|
||||
```
|
||||
15 | b.push(false);
|
||||
16 | };
|
||||
> 17 | return <div onClick={f} />;
|
||||
| ^ InvalidReact: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead (17:17)
|
||||
|
||||
InvalidReact: The function modifies a local variable here (8:8)
|
||||
18 | }
|
||||
19 |
|
||||
20 | export const FIXTURE_ENTRYPOINT = {
|
||||
6 | const f = () => {
|
||||
7 | if (cond) {
|
||||
> 8 | a = {};
|
||||
| ^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `a` cannot be reassigned after render (8:8)
|
||||
9 | b = [];
|
||||
10 | } else {
|
||||
11 | a = {};
|
||||
```
|
||||
|
||||
|
||||
+5
-6
@@ -17,14 +17,13 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
1 | function Component() {
|
||||
2 | let callback = () => {
|
||||
> 3 | onClick = () => {};
|
||||
| ^^^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `onClick` cannot be reassigned after render (3:3)
|
||||
4 | };
|
||||
5 | let onClick;
|
||||
6 |
|
||||
> 7 | return <div onClick={callback} />;
|
||||
| ^^^^^^^^ InvalidReact: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead (7:7)
|
||||
|
||||
InvalidReact: The function modifies a local variable here (3:3)
|
||||
8 | }
|
||||
9 |
|
||||
```
|
||||
|
||||
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useEffect, useState} from 'react';
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Foo() {
|
||||
/**
|
||||
* Previously, this lowered to
|
||||
* $1 = LoadContext capture setState
|
||||
* $2 = FunctionExpression deps=$1 context=setState
|
||||
* [[ at this point, we freeze the `LoadContext setState` instruction, but it will never be referenced again ]]
|
||||
*
|
||||
* Now, this function expression directly references `setState`, which freezes
|
||||
* the source `DeclareContext HoistedConst setState`. Freezing source identifiers
|
||||
* (instead of the one level removed `LoadContext`) is more semantically correct
|
||||
* for everything *other* than hoisted context declarations.
|
||||
*
|
||||
* $2 = Function context=setState
|
||||
*/
|
||||
useEffect(() => setState(2), []);
|
||||
|
||||
const [state, setState] = useState(0);
|
||||
return <Stringify state={state} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}],
|
||||
sequentialRenders: [{}, {}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Foo() {
|
||||
const $ = _c(3);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = [];
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
useEffect(() => setState(2), t0);
|
||||
|
||||
const [state, t1] = useState(0);
|
||||
const setState = t1;
|
||||
let t2;
|
||||
if ($[1] !== state) {
|
||||
t2 = <Stringify state={state} />;
|
||||
$[1] = state;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}],
|
||||
sequentialRenders: [{}, {}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"state":2}</div>
|
||||
<div>{"state":2}</div>
|
||||
+14
-8
@@ -34,22 +34,28 @@ import { print } from "shared-runtime";
|
||||
* setState types are not enough to determine to omit from deps. Must also take reactivity into account.
|
||||
*/
|
||||
function ReactiveRefInEffect(props) {
|
||||
const $ = _c(2);
|
||||
const $ = _c(4);
|
||||
const [, setState1] = useRef("initial value");
|
||||
const [, setState2] = useRef("initial value");
|
||||
let setState;
|
||||
if (props.foo) {
|
||||
setState = setState1;
|
||||
if ($[0] !== props.foo) {
|
||||
if (props.foo) {
|
||||
setState = setState1;
|
||||
} else {
|
||||
setState = setState2;
|
||||
}
|
||||
$[0] = props.foo;
|
||||
$[1] = setState;
|
||||
} else {
|
||||
setState = setState2;
|
||||
setState = $[1];
|
||||
}
|
||||
let t0;
|
||||
if ($[0] !== setState) {
|
||||
if ($[2] !== setState) {
|
||||
t0 = () => print(setState);
|
||||
$[0] = setState;
|
||||
$[1] = t0;
|
||||
$[2] = setState;
|
||||
$[3] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
t0 = $[3];
|
||||
}
|
||||
useEffect(t0, [setState]);
|
||||
}
|
||||
|
||||
+43
-38
@@ -57,62 +57,67 @@ import { Stringify } from "shared-runtime";
|
||||
* - cb1 is not assumed to be called since it's only used as a call operand
|
||||
*/
|
||||
function useFoo(t0) {
|
||||
const $ = _c(13);
|
||||
const { arr1, arr2 } = t0;
|
||||
const $ = _c(14);
|
||||
let arr1;
|
||||
let arr2;
|
||||
let t1;
|
||||
if ($[0] !== arr1[0]) {
|
||||
t1 = (e) => arr1[0].value + e.value;
|
||||
$[0] = arr1[0];
|
||||
$[1] = t1;
|
||||
if ($[0] !== t0) {
|
||||
({ arr1, arr2 } = t0);
|
||||
let t2;
|
||||
if ($[4] !== arr1[0]) {
|
||||
t2 = (e) => arr1[0].value + e.value;
|
||||
$[4] = arr1[0];
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
const cb1 = t2;
|
||||
t1 = () => arr1.map(cb1);
|
||||
$[0] = t0;
|
||||
$[1] = arr1;
|
||||
$[2] = arr2;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
arr1 = $[1];
|
||||
arr2 = $[2];
|
||||
t1 = $[3];
|
||||
}
|
||||
const cb1 = t1;
|
||||
const getArrMap1 = t1;
|
||||
let t2;
|
||||
if ($[2] !== arr1 || $[3] !== cb1) {
|
||||
t2 = () => arr1.map(cb1);
|
||||
$[2] = arr1;
|
||||
$[3] = cb1;
|
||||
$[4] = t2;
|
||||
if ($[6] !== arr2) {
|
||||
t2 = (e_0) => arr2[0].value + e_0.value;
|
||||
$[6] = arr2;
|
||||
$[7] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
t2 = $[7];
|
||||
}
|
||||
const getArrMap1 = t2;
|
||||
const cb2 = t2;
|
||||
let t3;
|
||||
if ($[5] !== arr2) {
|
||||
t3 = (e_0) => arr2[0].value + e_0.value;
|
||||
$[5] = arr2;
|
||||
$[6] = t3;
|
||||
if ($[8] !== arr1 || $[9] !== cb2) {
|
||||
t3 = () => arr1.map(cb2);
|
||||
$[8] = arr1;
|
||||
$[9] = cb2;
|
||||
$[10] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
t3 = $[10];
|
||||
}
|
||||
const cb2 = t3;
|
||||
const getArrMap2 = t3;
|
||||
let t4;
|
||||
if ($[7] !== arr1 || $[8] !== cb2) {
|
||||
t4 = () => arr1.map(cb2);
|
||||
$[7] = arr1;
|
||||
$[8] = cb2;
|
||||
$[9] = t4;
|
||||
} else {
|
||||
t4 = $[9];
|
||||
}
|
||||
const getArrMap2 = t4;
|
||||
let t5;
|
||||
if ($[10] !== getArrMap1 || $[11] !== getArrMap2) {
|
||||
t5 = (
|
||||
if ($[11] !== getArrMap1 || $[12] !== getArrMap2) {
|
||||
t4 = (
|
||||
<Stringify
|
||||
getArrMap1={getArrMap1}
|
||||
getArrMap2={getArrMap2}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
$[10] = getArrMap1;
|
||||
$[11] = getArrMap2;
|
||||
$[12] = t5;
|
||||
$[11] = getArrMap1;
|
||||
$[12] = getArrMap2;
|
||||
$[13] = t4;
|
||||
} else {
|
||||
t5 = $[12];
|
||||
t4 = $[13];
|
||||
}
|
||||
return t5;
|
||||
return t4;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
-129
@@ -1,129 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify, useIdentity} from 'shared-runtime';
|
||||
|
||||
function Component({prop1, prop2}) {
|
||||
'use memo';
|
||||
|
||||
const data = useIdentity(
|
||||
new Map([
|
||||
[0, 'value0'],
|
||||
[1, 'value1'],
|
||||
])
|
||||
);
|
||||
let i = 0;
|
||||
const items = [];
|
||||
items.push(
|
||||
<Stringify
|
||||
key={i}
|
||||
onClick={() => data.get(i) + prop1}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
i = i + 1;
|
||||
items.push(
|
||||
<Stringify
|
||||
key={i}
|
||||
onClick={() => data.get(i) + prop2}
|
||||
shouldInvokeFns={true}
|
||||
/>
|
||||
);
|
||||
return <>{items}</>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{prop1: 'prop1', prop2: 'prop2'}],
|
||||
sequentialRenders: [
|
||||
{prop1: 'prop1', prop2: 'prop2'},
|
||||
{prop1: 'prop1', prop2: 'prop2'},
|
||||
{prop1: 'changed', prop2: 'prop2'},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify, useIdentity } from "shared-runtime";
|
||||
|
||||
function Component(t0) {
|
||||
"use memo";
|
||||
const $ = _c(12);
|
||||
const { prop1, prop2 } = t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = new Map([
|
||||
[0, "value0"],
|
||||
[1, "value1"],
|
||||
]);
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
const data = useIdentity(t1);
|
||||
let t2;
|
||||
if ($[1] !== data || $[2] !== prop1 || $[3] !== prop2) {
|
||||
let i = 0;
|
||||
const items = [];
|
||||
items.push(
|
||||
<Stringify
|
||||
key={i}
|
||||
onClick={() => data.get(i) + prop1}
|
||||
shouldInvokeFns={true}
|
||||
/>,
|
||||
);
|
||||
i = i + 1;
|
||||
|
||||
const t3 = i;
|
||||
let t4;
|
||||
if ($[5] !== data || $[6] !== i || $[7] !== prop2) {
|
||||
t4 = () => data.get(i) + prop2;
|
||||
$[5] = data;
|
||||
$[6] = i;
|
||||
$[7] = prop2;
|
||||
$[8] = t4;
|
||||
} else {
|
||||
t4 = $[8];
|
||||
}
|
||||
let t5;
|
||||
if ($[9] !== t3 || $[10] !== t4) {
|
||||
t5 = <Stringify key={t3} onClick={t4} shouldInvokeFns={true} />;
|
||||
$[9] = t3;
|
||||
$[10] = t4;
|
||||
$[11] = t5;
|
||||
} else {
|
||||
t5 = $[11];
|
||||
}
|
||||
items.push(t5);
|
||||
t2 = <>{items}</>;
|
||||
$[1] = data;
|
||||
$[2] = prop1;
|
||||
$[3] = prop2;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ prop1: "prop1", prop2: "prop2" }],
|
||||
sequentialRenders: [
|
||||
{ prop1: "prop1", prop2: "prop2" },
|
||||
{ prop1: "prop1", prop2: "prop2" },
|
||||
{ prop1: "changed", prop2: "prop2" },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"onClick":{"kind":"Function","result":"value1prop1"},"shouldInvokeFns":true}</div><div>{"onClick":{"kind":"Function","result":"value1prop2"},"shouldInvokeFns":true}</div>
|
||||
<div>{"onClick":{"kind":"Function","result":"value1prop1"},"shouldInvokeFns":true}</div><div>{"onClick":{"kind":"Function","result":"value1prop2"},"shouldInvokeFns":true}</div>
|
||||
<div>{"onClick":{"kind":"Function","result":"value1changed"},"shouldInvokeFns":true}</div><div>{"onClick":{"kind":"Function","result":"value1prop2"},"shouldInvokeFns":true}</div>
|
||||
+7
-8
@@ -41,14 +41,13 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
29 | };
|
||||
30 |
|
||||
> 31 | return <button onClick={onClick}>Submit</button>;
|
||||
| ^^^^^^^ InvalidReact: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead (31:31)
|
||||
|
||||
InvalidReact: The function modifies a local variable here (5:5)
|
||||
32 | }
|
||||
33 |
|
||||
3 |
|
||||
4 | const reassignLocal = newValue => {
|
||||
> 5 | local = newValue;
|
||||
| ^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `local` cannot be reassigned after render (5:5)
|
||||
6 | };
|
||||
7 |
|
||||
8 | const onClick = newValue => {
|
||||
```
|
||||
|
||||
|
||||
+30
-23
@@ -40,39 +40,46 @@ import { useCallback } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Foo(t0) {
|
||||
const $ = _c(8);
|
||||
const $ = _c(10);
|
||||
const { arr1, arr2, foo } = t0;
|
||||
let getVal1;
|
||||
let t1;
|
||||
if ($[0] !== arr1 || $[1] !== arr2 || $[2] !== foo) {
|
||||
const x = [arr1];
|
||||
|
||||
if ($[0] !== arr1) {
|
||||
t1 = [arr1];
|
||||
$[0] = arr1;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const x = t1;
|
||||
let getVal1;
|
||||
let t2;
|
||||
if ($[2] !== arr2 || $[3] !== foo || $[4] !== x) {
|
||||
let y = [];
|
||||
|
||||
getVal1 = _temp;
|
||||
|
||||
t1 = () => [y];
|
||||
t2 = () => [y];
|
||||
foo ? (y = x.concat(arr2)) : y;
|
||||
$[0] = arr1;
|
||||
$[1] = arr2;
|
||||
$[2] = foo;
|
||||
$[3] = getVal1;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
getVal1 = $[3];
|
||||
t1 = $[4];
|
||||
}
|
||||
const getVal2 = t1;
|
||||
let t2;
|
||||
if ($[5] !== getVal1 || $[6] !== getVal2) {
|
||||
t2 = <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
|
||||
$[2] = arr2;
|
||||
$[3] = foo;
|
||||
$[4] = x;
|
||||
$[5] = getVal1;
|
||||
$[6] = getVal2;
|
||||
$[7] = t2;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[7];
|
||||
getVal1 = $[5];
|
||||
t2 = $[6];
|
||||
}
|
||||
return t2;
|
||||
const getVal2 = t2;
|
||||
let t3;
|
||||
if ($[7] !== getVal1 || $[8] !== getVal2) {
|
||||
t3 = <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
|
||||
$[7] = getVal1;
|
||||
$[8] = getVal2;
|
||||
$[9] = t3;
|
||||
} else {
|
||||
t3 = $[9];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
function _temp() {
|
||||
return { x: 2 };
|
||||
|
||||
+24
-17
@@ -36,31 +36,38 @@ import { Stringify } from "shared-runtime";
|
||||
|
||||
// We currently produce invalid output (incorrect scoping for `y` declaration)
|
||||
function useFoo(arr1, arr2) {
|
||||
const $ = _c(5);
|
||||
const $ = _c(7);
|
||||
let t0;
|
||||
if ($[0] !== arr1 || $[1] !== arr2) {
|
||||
const x = [arr1];
|
||||
|
||||
if ($[0] !== arr1) {
|
||||
t0 = [arr1];
|
||||
$[0] = arr1;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[2] !== arr2 || $[3] !== x) {
|
||||
let y;
|
||||
t0 = () => ({ y });
|
||||
t1 = () => ({ y });
|
||||
|
||||
(y = x.concat(arr2)), y;
|
||||
$[0] = arr1;
|
||||
$[1] = arr2;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
const getVal = t0;
|
||||
let t1;
|
||||
if ($[3] !== getVal) {
|
||||
t1 = <Stringify getVal={getVal} shouldInvokeFns={true} />;
|
||||
$[3] = getVal;
|
||||
$[2] = arr2;
|
||||
$[3] = x;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
return t1;
|
||||
const getVal = t1;
|
||||
let t2;
|
||||
if ($[5] !== getVal) {
|
||||
t2 = <Stringify getVal={getVal} shouldInvokeFns={true} />;
|
||||
$[5] = getVal;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+27
-20
@@ -30,29 +30,36 @@ import { c as _c } from "react/compiler-runtime";
|
||||
import { useMemo } from "react";
|
||||
|
||||
function useFoo(arr1, arr2) {
|
||||
const $ = _c(5);
|
||||
let y;
|
||||
if ($[0] !== arr1 || $[1] !== arr2) {
|
||||
const x = [arr1];
|
||||
|
||||
(y = x.concat(arr2)), y;
|
||||
$[0] = arr1;
|
||||
$[1] = arr2;
|
||||
$[2] = y;
|
||||
} else {
|
||||
y = $[2];
|
||||
}
|
||||
const $ = _c(7);
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[3] !== y) {
|
||||
t1 = { y };
|
||||
$[3] = y;
|
||||
$[4] = t1;
|
||||
if ($[0] !== arr1) {
|
||||
t0 = [arr1];
|
||||
$[0] = arr1;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
t0 = $[1];
|
||||
}
|
||||
t0 = t1;
|
||||
return t0;
|
||||
const x = t0;
|
||||
let y;
|
||||
if ($[2] !== arr2 || $[3] !== x) {
|
||||
(y = x.concat(arr2)), y;
|
||||
$[2] = arr2;
|
||||
$[3] = x;
|
||||
$[4] = y;
|
||||
} else {
|
||||
y = $[4];
|
||||
}
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[5] !== y) {
|
||||
t2 = { y };
|
||||
$[5] = y;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
t1 = t2;
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @flow
|
||||
/**
|
||||
* This hook returns a function that when called with an input object,
|
||||
* will return the result of mapping that input with the supplied map
|
||||
* function. Results are cached, so if the same input is passed again,
|
||||
* the same output object will be returned.
|
||||
*
|
||||
* Note that this technically violates the rules of React and is unsafe:
|
||||
* hooks must return immutable objects and be pure, and a function which
|
||||
* captures and mutates a value when called is inherently not pure.
|
||||
*
|
||||
* However, in this case it is technically safe _if_ the mapping function
|
||||
* is pure *and* the resulting objects are never modified. This is because
|
||||
* the function only caches: the result of `returnedFunction(someInput)`
|
||||
* strictly depends on `returnedFunction` and `someInput`, and cannot
|
||||
* otherwise change over time.
|
||||
*/
|
||||
hook useMemoMap<TInput: interface {}, TOutput>(
|
||||
map: TInput => TOutput
|
||||
): TInput => TOutput {
|
||||
return useMemo(() => {
|
||||
// The original issue is that `cache` was not memoized together with the returned
|
||||
// function. This was because neither appears to ever be mutated — the function
|
||||
// is known to mutate `cache` but the function isn't called.
|
||||
//
|
||||
// The fix is to detect cases like this — functions that are mutable but not called -
|
||||
// and ensure that their mutable captures are aliased together into the same scope.
|
||||
const cache = new WeakMap<TInput, TOutput>();
|
||||
return input => {
|
||||
let output = cache.get(input);
|
||||
if (output == null) {
|
||||
output = map(input);
|
||||
cache.set(input, output);
|
||||
}
|
||||
return output;
|
||||
};
|
||||
}, [map]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
|
||||
function useMemoMap(map) {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] !== map) {
|
||||
const cache = new WeakMap();
|
||||
t1 = (input) => {
|
||||
let output = cache.get(input);
|
||||
if (output == null) {
|
||||
output = map(input);
|
||||
cache.set(input, output);
|
||||
}
|
||||
return output;
|
||||
};
|
||||
$[0] = map;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
t0 = t1;
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
+47
-34
@@ -30,47 +30,60 @@ import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire
|
||||
import { fire } from "react";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = _c(9);
|
||||
const { bar, baz } = t0;
|
||||
let t1;
|
||||
if ($[0] !== bar) {
|
||||
t1 = () => {
|
||||
console.log(bar);
|
||||
};
|
||||
$[0] = bar;
|
||||
$[1] = t1;
|
||||
const $ = _c(13);
|
||||
let bar;
|
||||
let baz;
|
||||
let foo;
|
||||
if ($[0] !== t0) {
|
||||
({ bar, baz } = t0);
|
||||
let t1;
|
||||
if ($[4] !== bar) {
|
||||
t1 = () => {
|
||||
console.log(bar);
|
||||
};
|
||||
$[4] = bar;
|
||||
$[5] = t1;
|
||||
} else {
|
||||
t1 = $[5];
|
||||
}
|
||||
foo = t1;
|
||||
$[0] = t0;
|
||||
$[1] = bar;
|
||||
$[2] = baz;
|
||||
$[3] = foo;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
bar = $[1];
|
||||
baz = $[2];
|
||||
foo = $[3];
|
||||
}
|
||||
const foo = t1;
|
||||
const t2 = useFire(foo);
|
||||
const t3 = useFire(baz);
|
||||
let t4;
|
||||
if ($[2] !== bar || $[3] !== t2 || $[4] !== t3) {
|
||||
t4 = () => {
|
||||
t2(bar);
|
||||
t3(bar);
|
||||
};
|
||||
$[2] = bar;
|
||||
$[3] = t2;
|
||||
$[4] = t3;
|
||||
$[5] = t4;
|
||||
} else {
|
||||
t4 = $[5];
|
||||
}
|
||||
useEffect(t4);
|
||||
let t5;
|
||||
if ($[6] !== bar || $[7] !== t2) {
|
||||
t5 = () => {
|
||||
const t1 = useFire(foo);
|
||||
const t2 = useFire(baz);
|
||||
let t3;
|
||||
if ($[6] !== bar || $[7] !== t1 || $[8] !== t2) {
|
||||
t3 = () => {
|
||||
t1(bar);
|
||||
t2(bar);
|
||||
};
|
||||
$[6] = bar;
|
||||
$[7] = t2;
|
||||
$[8] = t5;
|
||||
$[7] = t1;
|
||||
$[8] = t2;
|
||||
$[9] = t3;
|
||||
} else {
|
||||
t5 = $[8];
|
||||
t3 = $[9];
|
||||
}
|
||||
useEffect(t5);
|
||||
useEffect(t3);
|
||||
let t4;
|
||||
if ($[10] !== bar || $[11] !== t1) {
|
||||
t4 = () => {
|
||||
t1(bar);
|
||||
};
|
||||
$[10] = bar;
|
||||
$[11] = t1;
|
||||
$[12] = t4;
|
||||
} else {
|
||||
t4 = $[12];
|
||||
}
|
||||
useEffect(t4);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user