mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
More tests for effect enforcement
Adds test cases to ensure we're correctly inferring mutative builtin operations — property store, computed property store, property deletion, and computed property deletion — as definite mutation and that we're rejecting inputs where these operations are used on immutable/frozen values.
This commit is contained in:
@@ -863,7 +863,10 @@ export enum Effect {
|
||||
Store = "store",
|
||||
}
|
||||
|
||||
export function isMutableEffect(effect: Effect): boolean {
|
||||
export function isMutableEffect(
|
||||
effect: Effect,
|
||||
location: SourceLocation
|
||||
): boolean {
|
||||
switch (effect) {
|
||||
case Effect.Capture:
|
||||
case Effect.Store:
|
||||
@@ -874,11 +877,11 @@ export function isMutableEffect(effect: Effect): boolean {
|
||||
// All conditional mutations should be resolved into some other effect after InferReferenceEffects
|
||||
CompilerError.invariant(
|
||||
"Unexpected conditional mutation effect",
|
||||
GeneratedSource
|
||||
location
|
||||
);
|
||||
}
|
||||
case Effect.Unknown: {
|
||||
CompilerError.invariant("Unexpected unknown effect", GeneratedSource);
|
||||
CompilerError.invariant("Unexpected unknown effect", location);
|
||||
}
|
||||
case Effect.Read:
|
||||
case Effect.Freeze: {
|
||||
|
||||
@@ -699,7 +699,7 @@ function inferBlock(
|
||||
operand,
|
||||
operand.effect === Effect.Unknown ? Effect.Read : operand.effect
|
||||
);
|
||||
hasMutableOperand ||= isMutableEffect(operand.effect);
|
||||
hasMutableOperand ||= isMutableEffect(operand.effect, operand.loc);
|
||||
}
|
||||
// If a closure did not capture any mutable values, then we can consider it to be
|
||||
// frozen, which allows it to be independently memoized.
|
||||
@@ -808,7 +808,7 @@ function inferBlock(
|
||||
case "PropertyDelete": {
|
||||
// `delete` returns a boolean (immutable) and modifies the object
|
||||
valueKind = ValueKind.Immutable;
|
||||
effectKind = Effect.ConditionallyMutate;
|
||||
effectKind = Effect.Mutate;
|
||||
break;
|
||||
}
|
||||
case "PropertyLoad": {
|
||||
@@ -837,7 +837,7 @@ function inferBlock(
|
||||
state.reference(instrValue.object, Effect.Mutate);
|
||||
state.reference(instrValue.property, Effect.Read);
|
||||
state.initialize(instrValue, ValueKind.Immutable);
|
||||
state.reference(instr.lvalue, Effect.ConditionallyMutate);
|
||||
state.reference(instr.lvalue, Effect.Mutate);
|
||||
continue;
|
||||
}
|
||||
case "ComputedLoad": {
|
||||
@@ -927,7 +927,10 @@ function inferBlock(
|
||||
state.alias(lvalue, instrValue.value);
|
||||
lvalue.effect = Effect.Store;
|
||||
state.alias(instrValue.lvalue.place, instrValue.value);
|
||||
// state.reference(instrValue.lvalue.place, Effect.Store);
|
||||
// NOTE: *not* using state.reference since this is an assignment.
|
||||
// reference() checks if the effect is valid given the value kind,
|
||||
// but here the previous value kind doesn't matter since we are
|
||||
// replacing it
|
||||
instrValue.lvalue.place.effect = Effect.Store;
|
||||
continue;
|
||||
}
|
||||
@@ -958,7 +961,10 @@ function inferBlock(
|
||||
lvalue.effect = Effect.Store;
|
||||
for (const place of eachPatternOperand(instrValue.lvalue.pattern)) {
|
||||
state.alias(place, instrValue.value);
|
||||
// state.reference(place, Effect.Store);
|
||||
// NOTE: *not* using state.reference since this is an assignment.
|
||||
// reference() checks if the effect is valid given the value kind,
|
||||
// but here the previous value kind doesn't matter since we are
|
||||
// replacing it
|
||||
place.effect = Effect.Store;
|
||||
}
|
||||
continue;
|
||||
|
||||
@@ -594,7 +594,7 @@ function computeMemoizationInputs(
|
||||
// reachable from a return value. Any mutable rvalue may alias any other rvalue
|
||||
const operands = [...eachReactiveValueOperand(value)];
|
||||
const lvalues = operands
|
||||
.filter((operand) => isMutableEffect(operand.effect))
|
||||
.filter((operand) => isMutableEffect(operand.effect, operand.loc))
|
||||
.map((place) => ({ place, level: MemoizationLevel.Memoized }));
|
||||
if (lvalue !== null) {
|
||||
lvalues.push({ place: lvalue, level: MemoizationLevel.Memoized });
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = makeObject();
|
||||
// freeze
|
||||
<div>{x}</div>;
|
||||
x[0] = true;
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $22 (frozen) (5:5)
|
||||
```
|
||||
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
function Component(props) {
|
||||
const x = makeObject();
|
||||
// freeze
|
||||
<div>{x}</div>;
|
||||
x[0] = true;
|
||||
return x;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = makeObject();
|
||||
// freeze
|
||||
<div>{x}</div>;
|
||||
delete x[y];
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $20 (frozen) (5:5)
|
||||
```
|
||||
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
function Component(props) {
|
||||
const x = makeObject();
|
||||
// freeze
|
||||
<div>{x}</div>;
|
||||
delete x[y];
|
||||
return x;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = makeObject();
|
||||
// freeze
|
||||
<div>{x}</div>;
|
||||
delete x.y;
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $19 (frozen) (5:5)
|
||||
```
|
||||
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
function Component(props) {
|
||||
const x = makeObject();
|
||||
// freeze
|
||||
<div>{x}</div>;
|
||||
delete x.y;
|
||||
return x;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = makeObject();
|
||||
// freeze
|
||||
<div>{x}</div>;
|
||||
x.y = true;
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $21 (frozen) (5:5)
|
||||
```
|
||||
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
function Component(props) {
|
||||
const x = makeObject();
|
||||
// freeze
|
||||
<div>{x}</div>;
|
||||
x.y = true;
|
||||
return x;
|
||||
}
|
||||
Reference in New Issue
Block a user