Fixtures for control values that become reactive due to interleaving

See the previous PR, interleaved mutation can cause values that were not 
reactive to become reactive. I swear I had a case where this was observable, but 
I came up with it before reordering the PRs in this stack. I think my repro 
relied on an immutable reference to a mutable value, which is now handled in 
InferReactivePlaces. So here i'm just adding fixtures, and allowing this case 
since it's unobservable.
This commit is contained in:
Joe Savona
2024-01-23 08:59:20 -08:00
parent 4d84bee172
commit a023a2da72
18 changed files with 838 additions and 4 deletions
@@ -0,0 +1,73 @@
## Input
```javascript
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(false);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x = 0;
do {
x += 1;
} while (c[0][0]);
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(1);
const a = [];
const b = [];
b.push(props.cond);
a.push(false);
const c = [a];
let x = 0;
do {
x = x + 1;
} while (c[0][0]);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [x];
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
### Eval output
(kind: ok) [1]
@@ -0,0 +1,29 @@
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(false);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x = 0;
do {
x += 1;
} while (c[0][0]);
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
@@ -0,0 +1,73 @@
## Input
```javascript
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push({ a: false });
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
for (const i in c[0][0]) {
x = 1;
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(1);
const a = [];
const b = [];
b.push(props.cond);
a.push({ a: false });
const c = [a];
let x;
for (const i in c[0][0]) {
x = 1;
}
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [x];
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
### Eval output
(kind: ok) [1]
@@ -0,0 +1,29 @@
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push({ a: false });
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
for (const i in c[0][0]) {
x = 1;
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
@@ -0,0 +1,73 @@
## Input
```javascript
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(0);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
for (let i = c[0][0]; i < 10; i++) {
x = 1;
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(1);
const a = [];
const b = [];
b.push(props.cond);
a.push(0);
const c = [a];
let x;
for (let i = c[0][0]; i < 10; i++) {
x = 1;
}
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [x];
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
### Eval output
(kind: ok) [1]
@@ -0,0 +1,29 @@
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(0);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
for (let i = c[0][0]; i < 10; i++) {
x = 1;
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
@@ -0,0 +1,73 @@
## Input
```javascript
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(null);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
for (const i of c[0]) {
x = 1;
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(1);
const a = [];
const b = [];
b.push(props.cond);
a.push(null);
const c = [a];
let x;
for (const i of c[0]) {
x = 1;
}
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [x];
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
### Eval output
(kind: ok) [1]
@@ -0,0 +1,29 @@
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(null);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
for (const i of c[0]) {
x = 1;
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
@@ -0,0 +1,73 @@
## Input
```javascript
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(10);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
for (let i = 0; i < c[0][0]; i++) {
x = 1;
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(1);
const a = [];
const b = [];
b.push(props.cond);
a.push(10);
const c = [a];
let x;
for (let i = 0; i < c[0][0]; i++) {
x = 1;
}
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [x];
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
### Eval output
(kind: ok) [1]
@@ -0,0 +1,29 @@
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(10);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
for (let i = 0; i < c[0][0]; i++) {
x = 1;
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
@@ -0,0 +1,73 @@
## Input
```javascript
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(10);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
for (let i = 0; i < 10; i += c[0][0]) {
x = 1;
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(1);
const a = [];
const b = [];
b.push(props.cond);
a.push(10);
const c = [a];
let x;
for (let i = 0; i < 10; i = i + c[0][0], i) {
x = 1;
}
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [x];
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
### Eval output
(kind: ok) [1]
@@ -0,0 +1,29 @@
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(10);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
for (let i = 0; i < 10; i += c[0][0]) {
x = 1;
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
@@ -17,7 +17,7 @@ function Component(props) {
const c = [a];
let x;
if (c[0]) {
if (c[0][0]) {
x = 1;
} else {
x = 2;
@@ -51,7 +51,7 @@ function Component(props) {
const c = [a];
let x;
if (c[0]) {
if (c[0][0]) {
x = 1;
} else {
x = 2;
@@ -74,4 +74,4 @@ export const FIXTURE_ENTRYPOINT = {
```
### Eval output
(kind: ok) [1]
(kind: ok) [2]
@@ -13,7 +13,7 @@ function Component(props) {
const c = [a];
let x;
if (c[0]) {
if (c[0][0]) {
x = 1;
} else {
x = 2;
@@ -0,0 +1,85 @@
## Input
```javascript
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(null);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
switch (c[0][0]) {
case true: {
x = 1;
break;
}
default: {
x = 2;
}
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(1);
const a = [];
const b = [];
b.push(props.cond);
a.push(null);
const c = [a];
let x;
bb1: switch (c[0][0]) {
case true: {
x = 1;
break bb1;
}
default: {
x = 2;
}
}
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [x];
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
### Eval output
(kind: ok) [2]
@@ -0,0 +1,35 @@
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(null);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
switch (c[0][0]) {
case true: {
x = 1;
break;
}
default: {
x = 2;
}
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
@@ -0,0 +1,73 @@
## Input
```javascript
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(null);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
while (c[0][0]) {
x = 1;
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(1);
const a = [];
const b = [];
b.push(props.cond);
a.push(null);
const c = [a];
let x;
while (c[0][0]) {
x = 1;
}
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [x];
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};
```
### Eval output
(kind: ok) [null]
@@ -0,0 +1,29 @@
function Component(props) {
// a and b are independent but their mutations are interleaved, so
// they get grouped in a reactive scope. this means that a becomes
// reactive since it will effectively re-evaluate based on a reactive
// input
const a = [];
const b = [];
b.push(props.cond);
a.push(null);
// Downstream consumer of a, which initially seems non-reactive except
// that a becomes reactive, per above
const c = [a];
let x;
while (c[0][0]) {
x = 1;
}
// The values assigned to `x` are non-reactive, but the value of `x`
// depends on the "control" value `c[0]` which becomes reactive via
// being interleaved with `b`.
// Therefore x should be treated as reactive too.
return [x];
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ cond: true }],
};