mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[hir] infer reference effects for property call
--- This PR does not add inference for normal `CallExpression`s, since built-in functions for `Array` and `Object` are usually only valid if called with a correctly-typed `this`. If we want codegen to preserve source code semantics, Forget should only add inferred types it is confident about. This PR also adds `returnEffect` to FunctionSignature. `returnEffect = Store` if this function is known to always return a captured value from `receiver` or `args`.
This commit is contained in:
@@ -128,6 +128,19 @@ export class CompilerError extends Error {
|
||||
throw errors;
|
||||
}
|
||||
|
||||
static invalidInput(reason: string, loc: SourceLocation): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
codeframe: null,
|
||||
loc: typeof loc === "symbol" ? null : loc,
|
||||
reason,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
})
|
||||
);
|
||||
throw errors;
|
||||
}
|
||||
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
}
|
||||
|
||||
@@ -449,7 +449,7 @@ export type Phi = {
|
||||
* Type inference does not currently guarantee that {@link PropertyCall.property}
|
||||
* is a FunctionType.
|
||||
*/
|
||||
type PropertyCall = {
|
||||
export type PropertyCall = {
|
||||
kind: "PropertyCall";
|
||||
receiver: Place;
|
||||
property: Place;
|
||||
|
||||
@@ -564,7 +564,7 @@ function printScope(scope: ReactiveScope | null): string {
|
||||
return `${scope !== null ? `_@${scope.id}` : ""}`;
|
||||
}
|
||||
|
||||
function printType(type: Type): string {
|
||||
export function printType(type: Type): string {
|
||||
if (type.kind === "Type") return "";
|
||||
// TODO(mofeiZ): add debugName for generated ids
|
||||
if (type.kind === "Object" && type.shapeId != null) {
|
||||
|
||||
@@ -18,12 +18,17 @@ import {
|
||||
isObjectType,
|
||||
Phi,
|
||||
Place,
|
||||
PropertyCall,
|
||||
Type,
|
||||
ValueKind,
|
||||
} from "../HIR/HIR";
|
||||
import { FunctionSignature } from "../HIR/ObjectShape";
|
||||
import {
|
||||
printIdentifier,
|
||||
printMixedHIR,
|
||||
printPlace,
|
||||
printSourceLocation,
|
||||
printType,
|
||||
} from "../HIR/PrintHIR";
|
||||
import {
|
||||
eachInstructionOperand,
|
||||
@@ -263,6 +268,18 @@ class InferenceState {
|
||||
* value is already frozen or is immutable.
|
||||
*/
|
||||
reference(place: Place, effectKind: Effect): void {
|
||||
this.#referenceImpl(place, effectKind, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throwing version of {@link reference}, which throws with an error
|
||||
* if we record a mutate effect on an immutable value.
|
||||
*/
|
||||
referenceAndCheckError(place: Place, effectKind: Effect): void {
|
||||
this.#referenceImpl(place, effectKind, true);
|
||||
}
|
||||
|
||||
#referenceImpl(place: Place, effectKind: Effect, shouldError: boolean): void {
|
||||
const values = this.#variables.get(place.identifier.id);
|
||||
if (values === undefined) {
|
||||
place.effect = effectKind === Effect.Mutate ? Effect.Mutate : Effect.Read;
|
||||
@@ -292,6 +309,14 @@ class InferenceState {
|
||||
) {
|
||||
effect = Effect.Mutate;
|
||||
} else {
|
||||
if (shouldError) {
|
||||
CompilerError.invalidInput(
|
||||
`InferReferenceEffects: inferred mutation of known immutable value ${printIdentifier(
|
||||
place.identifier
|
||||
)}${printType(place.identifier.type)} (${valueKind})`,
|
||||
place.loc
|
||||
);
|
||||
}
|
||||
effect = Effect.Read;
|
||||
}
|
||||
break;
|
||||
@@ -554,7 +579,7 @@ function mergeValues(a: ValueKind, b: ValueKind): ValueKind {
|
||||
* recording references on the @param state according to JS semantics.
|
||||
*/
|
||||
function inferBlock(
|
||||
_env: Environment,
|
||||
env: Environment,
|
||||
state: InferenceState,
|
||||
block: BasicBlock
|
||||
): void {
|
||||
@@ -660,25 +685,39 @@ function inferBlock(
|
||||
continue;
|
||||
}
|
||||
case "PropertyCall": {
|
||||
if (!state.isDefined(instrValue.receiver)) {
|
||||
// TODO @josephsavona: improve handling of globals
|
||||
const value: InstructionValue = {
|
||||
kind: "Primitive",
|
||||
loc: instrValue.loc,
|
||||
value: undefined,
|
||||
};
|
||||
state.initialize(value, ValueKind.Frozen);
|
||||
state.define(instrValue.receiver, value);
|
||||
}
|
||||
invariant(
|
||||
state.isDefined(instrValue.receiver),
|
||||
"[InferReferenceEffects] Internal error: receiver of PropertyCall should have been defined by corresponding PropertyLoad"
|
||||
);
|
||||
|
||||
state.reference(instrValue.receiver, Effect.Mutate);
|
||||
state.reference(instrValue.property, Effect.Read);
|
||||
for (const arg of instrValue.args) {
|
||||
if (arg.kind === "Identifier") {
|
||||
state.reference(arg, Effect.Mutate);
|
||||
} else {
|
||||
state.reference(arg.place, Effect.Mutate);
|
||||
|
||||
const signature = getFunctionCallSignature(
|
||||
env,
|
||||
instrValue.property.identifier.type
|
||||
);
|
||||
if (signature !== null) {
|
||||
const effects = getFunctionCallEffects(
|
||||
instrValue,
|
||||
signature,
|
||||
Effect.Mutate
|
||||
);
|
||||
for (const [place, effect] of effects) {
|
||||
state.referenceAndCheckError(place, effect);
|
||||
}
|
||||
state.referenceAndCheckError(
|
||||
instrValue.receiver,
|
||||
signature.calleeEffect
|
||||
);
|
||||
} else {
|
||||
for (const arg of instrValue.args) {
|
||||
if (arg.kind === "Identifier") {
|
||||
state.reference(arg, Effect.Mutate);
|
||||
} else {
|
||||
state.reference(arg.place, Effect.Mutate);
|
||||
}
|
||||
}
|
||||
state.reference(instrValue.receiver, Effect.Mutate);
|
||||
}
|
||||
state.initialize(instrValue, ValueKind.Mutable);
|
||||
state.define(instr.lvalue, instrValue);
|
||||
@@ -904,3 +943,61 @@ function hasContextRefOperand(
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getFunctionCallSignature(
|
||||
env: Environment,
|
||||
type: Type
|
||||
): FunctionSignature | null {
|
||||
if (type.kind !== "Function") {
|
||||
return null;
|
||||
}
|
||||
return env.getFunctionSignature(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a best attempt at matching arguments of a PropertyCall to its FunctionSignature,
|
||||
* calling back to `defaultEffect` when we are unable to.
|
||||
*
|
||||
* @param fn
|
||||
* @param sig
|
||||
* @param defaultEffect In the case that inference fails, all arguments will be inferred
|
||||
* as defaultEffect
|
||||
* @returns Inferred effects of function arguments
|
||||
*/
|
||||
function getFunctionCallEffects(
|
||||
fn: PropertyCall,
|
||||
sig: FunctionSignature,
|
||||
defaultEffect: Effect
|
||||
): Array<[Place, Effect]> {
|
||||
const inferredEffects: Array<[Place, Effect | null]> = fn.args.map(
|
||||
(arg, idx) => {
|
||||
const argPlace = arg.kind === "Identifier" ? arg : arg.place;
|
||||
if (idx < sig.positionalParams.length) {
|
||||
// Only infer effects when there is a direct mapping positional arg --> positional param
|
||||
// Otherwise, return null to indicate inference failed
|
||||
if (arg.kind === "Identifier") {
|
||||
return [argPlace, sig.positionalParams[idx]];
|
||||
} else {
|
||||
return [argPlace, null];
|
||||
}
|
||||
} else if (sig.restParam !== null) {
|
||||
return [argPlace, sig.restParam];
|
||||
} else {
|
||||
// If there are more arguments than positional arguments, we'll also assume
|
||||
// that inference failed
|
||||
return [argPlace, null];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let results: Array<[Place, Effect]>;
|
||||
if (inferredEffects.some(([_, effect]) => effect === null)) {
|
||||
// If inference failed for any argument, give up on inference for all arguments
|
||||
results = inferredEffects.map(([arg, _]) => {
|
||||
return [arg, defaultEffect];
|
||||
});
|
||||
} else {
|
||||
results = inferredEffects as Array<[Place, Effect]>;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = foo(props.x);
|
||||
const fn = function () {
|
||||
const arr = [...bar(props)];
|
||||
return arr.at(x);
|
||||
};
|
||||
const fnResult = fn();
|
||||
return fnResult;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(5);
|
||||
const c_0 = $[0] !== props.x;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
t0 = foo(props.x);
|
||||
$[0] = props.x;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const x = t0;
|
||||
const c_2 = $[2] !== props;
|
||||
const c_3 = $[3] !== x;
|
||||
let t1;
|
||||
if (c_2 || c_3) {
|
||||
const fn = function () {
|
||||
const arr = [...bar(props)];
|
||||
return arr.at(x);
|
||||
};
|
||||
t1 = fn();
|
||||
$[2] = props;
|
||||
$[3] = x;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
const fnResult = t1;
|
||||
return fnResult;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
function Component(props) {
|
||||
const x = foo(props.x);
|
||||
const fn = function () {
|
||||
const arr = [...bar(props)];
|
||||
return arr.at(x);
|
||||
};
|
||||
const fnResult = fn();
|
||||
return fnResult;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// arrayInstance.at should have the following effects:
|
||||
// - read on arg0
|
||||
// - read on receiver
|
||||
// - mutate on lvalue
|
||||
function ArrayAtTest(props) {
|
||||
const arr = [foo(props.x)];
|
||||
const result = arr.at(bar(props.y));
|
||||
return result;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// arrayInstance.at should have the following effects:
|
||||
// - read on arg0
|
||||
// - read on receiver
|
||||
// - mutate on lvalue
|
||||
function ArrayAtTest(props) {
|
||||
const $ = React.unstable_useMemoCache(9);
|
||||
const c_0 = $[0] !== props.x;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
t0 = foo(props.x);
|
||||
$[0] = props.x;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const c_2 = $[2] !== t0;
|
||||
let t1;
|
||||
if (c_2) {
|
||||
t1 = [t0];
|
||||
$[2] = t0;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const arr = t1;
|
||||
const c_4 = $[4] !== props.y;
|
||||
let t2;
|
||||
if (c_4) {
|
||||
t2 = bar(props.y);
|
||||
$[4] = props.y;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
const c_6 = $[6] !== arr;
|
||||
const c_7 = $[7] !== t2;
|
||||
let t3;
|
||||
if (c_6 || c_7) {
|
||||
t3 = arr.at(t2);
|
||||
$[6] = arr;
|
||||
$[7] = t2;
|
||||
$[8] = t3;
|
||||
} else {
|
||||
t3 = $[8];
|
||||
}
|
||||
const result = t3;
|
||||
return result;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// arrayInstance.at should have the following effects:
|
||||
// - read on arg0
|
||||
// - read on receiver
|
||||
// - mutate on lvalue
|
||||
function ArrayAtTest(props) {
|
||||
const arr = [foo(props.x)];
|
||||
const result = arr.at(bar(props.y));
|
||||
return result;
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// x's mutable range should extend to `mutate(y)`
|
||||
|
||||
function Component(props) {
|
||||
let x = [42, {}];
|
||||
const idx = foo(props.b);
|
||||
let y = x.at(idx);
|
||||
mutate(y);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// x's mutable range should extend to `mutate(y)`
|
||||
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = {};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = [42, t0];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const x = t1;
|
||||
const idx = foo(props.b);
|
||||
const y = x.at(idx);
|
||||
mutate(y);
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// x's mutable range should extend to `mutate(y)`
|
||||
|
||||
function Component(props) {
|
||||
let x = [42, {}];
|
||||
const idx = foo(props.b);
|
||||
let y = x.at(idx);
|
||||
mutate(y);
|
||||
|
||||
return x;
|
||||
}
|
||||
@@ -16,43 +16,49 @@ function Component(props) {
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(10);
|
||||
const $ = React.unstable_useMemoCache(11);
|
||||
const c_0 = $[0] !== props.a;
|
||||
const c_1 = $[1] !== props.b;
|
||||
const c_2 = $[2] !== props.c;
|
||||
let t0;
|
||||
let a;
|
||||
let x;
|
||||
if (c_0 || c_1 || c_2) {
|
||||
if (c_0 || c_1) {
|
||||
a = [props.a, props.b, "hello"];
|
||||
x = a.push(42);
|
||||
t0 = a.at(props.c);
|
||||
t0 = a.push(42);
|
||||
$[0] = props.a;
|
||||
$[1] = props.b;
|
||||
$[2] = props.c;
|
||||
$[3] = t0;
|
||||
$[4] = a;
|
||||
$[5] = x;
|
||||
$[2] = t0;
|
||||
$[3] = a;
|
||||
} else {
|
||||
t0 = $[3];
|
||||
a = $[4];
|
||||
x = $[5];
|
||||
t0 = $[2];
|
||||
a = $[3];
|
||||
}
|
||||
const y = t0;
|
||||
const c_6 = $[6] !== a;
|
||||
const c_7 = $[7] !== x;
|
||||
const c_8 = $[8] !== y;
|
||||
const x = t0;
|
||||
const c_4 = $[4] !== a;
|
||||
const c_5 = $[5] !== props.c;
|
||||
let t1;
|
||||
if (c_6 || c_7 || c_8) {
|
||||
t1 = { a, x, y };
|
||||
$[6] = a;
|
||||
$[7] = x;
|
||||
$[8] = y;
|
||||
$[9] = t1;
|
||||
if (c_4 || c_5) {
|
||||
t1 = a.at(props.c);
|
||||
$[4] = a;
|
||||
$[5] = props.c;
|
||||
$[6] = t1;
|
||||
} else {
|
||||
t1 = $[9];
|
||||
t1 = $[6];
|
||||
}
|
||||
return t1;
|
||||
const y = t1;
|
||||
const c_7 = $[7] !== a;
|
||||
const c_8 = $[8] !== x;
|
||||
const c_9 = $[9] !== y;
|
||||
let t2;
|
||||
if (c_7 || c_8 || c_9) {
|
||||
t2 = { a, x, y };
|
||||
$[7] = a;
|
||||
$[8] = x;
|
||||
$[9] = y;
|
||||
$[10] = t2;
|
||||
} else {
|
||||
t2 = $[10];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// arrayInstance.push should have the following effects:
|
||||
// - read on all args (rest parameter)
|
||||
// - mutate on receiver
|
||||
function Component(props) {
|
||||
const x = foo(props.x);
|
||||
const y = { y: props.y };
|
||||
const arr = [];
|
||||
arr.push({});
|
||||
arr.push(x, y);
|
||||
return arr;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// arrayInstance.push should have the following effects:
|
||||
// - read on all args (rest parameter)
|
||||
// - mutate on receiver
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(8);
|
||||
const c_0 = $[0] !== props.x;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
t0 = foo(props.x);
|
||||
$[0] = props.x;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const x = t0;
|
||||
const c_2 = $[2] !== props.y;
|
||||
let t1;
|
||||
if (c_2) {
|
||||
t1 = { y: props.y };
|
||||
$[2] = props.y;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const y = t1;
|
||||
const c_4 = $[4] !== x;
|
||||
const c_5 = $[5] !== y;
|
||||
let arr;
|
||||
if (c_4 || c_5) {
|
||||
arr = [];
|
||||
let t2;
|
||||
if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = {};
|
||||
$[7] = t2;
|
||||
} else {
|
||||
t2 = $[7];
|
||||
}
|
||||
arr.push(t2);
|
||||
arr.push(x, y);
|
||||
$[4] = x;
|
||||
$[5] = y;
|
||||
$[6] = arr;
|
||||
} else {
|
||||
arr = $[6];
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// arrayInstance.push should have the following effects:
|
||||
// - read on all args (rest parameter)
|
||||
// - mutate on receiver
|
||||
function Component(props) {
|
||||
const x = foo(props.x);
|
||||
const y = { y: props.y };
|
||||
const arr = [];
|
||||
arr.push({});
|
||||
arr.push(x, y);
|
||||
return arr;
|
||||
}
|
||||
+15
-7
@@ -15,16 +15,24 @@ function foo() {
|
||||
|
||||
```javascript
|
||||
function foo() {
|
||||
const $ = React.unstable_useMemoCache(1);
|
||||
let a;
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
a = [[1]];
|
||||
const first = a.at(0);
|
||||
first.set(0, 2);
|
||||
$[0] = a;
|
||||
t0 = [1];
|
||||
$[0] = t0;
|
||||
} else {
|
||||
a = $[0];
|
||||
t0 = $[0];
|
||||
}
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = [t0];
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const a = t1;
|
||||
const first = a.at(0);
|
||||
first.set(0, 2);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let x = [];
|
||||
let y = x;
|
||||
|
||||
if (props.p1) {
|
||||
x = [];
|
||||
}
|
||||
|
||||
let _ = <Component x={x} />;
|
||||
|
||||
// y is MaybeFrozen at this point, since it may alias to x
|
||||
// (which is the above line freezes)
|
||||
y.push(props.p2);
|
||||
|
||||
return <Component x={x} y={y} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value $42:TObject<Array> (frozen) (13:13)
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
function Component(props) {
|
||||
let x = [];
|
||||
let y = x;
|
||||
|
||||
if (props.p1) {
|
||||
x = [];
|
||||
}
|
||||
|
||||
let _ = <Component x={x} />;
|
||||
|
||||
// y is MaybeFrozen at this point, since it may alias to x
|
||||
// (which is the above line freezes)
|
||||
y.push(props.p2);
|
||||
|
||||
return <Component x={x} y={y} />;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let x = [];
|
||||
|
||||
let _ = <Component x={x} />;
|
||||
|
||||
// x is Frozen at this point
|
||||
x.push(props.p2);
|
||||
|
||||
return <div>{_}</div>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value $25:TObject<Array> (frozen) (7:7)
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
function Component(props) {
|
||||
let x = [];
|
||||
|
||||
let _ = <Component x={x} />;
|
||||
|
||||
// x is Frozen at this point
|
||||
x.push(props.p2);
|
||||
|
||||
return <div>{_}</div>;
|
||||
}
|
||||
+14
-4
@@ -21,7 +21,7 @@ function foo(a, b, c) {
|
||||
|
||||
```javascript
|
||||
function foo(a, b, c) {
|
||||
const $ = React.unstable_useMemoCache(4);
|
||||
const $ = React.unstable_useMemoCache(7);
|
||||
const c_0 = $[0] !== a;
|
||||
const c_1 = $[1] !== b;
|
||||
const c_2 = $[2] !== c;
|
||||
@@ -29,9 +29,19 @@ function foo(a, b, c) {
|
||||
if (c_0 || c_1 || c_2) {
|
||||
x = [];
|
||||
if (a) {
|
||||
const y = [];
|
||||
if (b) {
|
||||
y.push(c);
|
||||
const c_4 = $[4] !== b;
|
||||
const c_5 = $[5] !== c;
|
||||
let y;
|
||||
if (c_4 || c_5) {
|
||||
y = [];
|
||||
if (b) {
|
||||
y.push(c);
|
||||
}
|
||||
$[4] = b;
|
||||
$[5] = c;
|
||||
$[6] = y;
|
||||
} else {
|
||||
y = $[6];
|
||||
}
|
||||
|
||||
x.push(y);
|
||||
|
||||
@@ -18,14 +18,21 @@ function foo() {
|
||||
|
||||
```javascript
|
||||
function foo() {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const $ = React.unstable_useMemoCache(3);
|
||||
let x;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
x = {};
|
||||
let y;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
y = [];
|
||||
const z = {};
|
||||
let t0;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = {};
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
const z = t0;
|
||||
y.push(z);
|
||||
$[1] = y;
|
||||
} else {
|
||||
|
||||
+32
-26
@@ -11,7 +11,6 @@ function Component(props) {
|
||||
x = [];
|
||||
}
|
||||
|
||||
let _ = <Component x={x} />;
|
||||
y.push(props.p2);
|
||||
|
||||
return <Component x={x} y={y} />;
|
||||
@@ -23,40 +22,47 @@ function Component(props) {
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(6);
|
||||
const $ = React.unstable_useMemoCache(9);
|
||||
const c_0 = $[0] !== props.p0;
|
||||
const c_1 = $[1] !== props.p1;
|
||||
const c_2 = $[2] !== props.p2;
|
||||
let x;
|
||||
if (c_0) {
|
||||
let y;
|
||||
if (c_0 || c_1 || c_2) {
|
||||
x = [];
|
||||
x.push(props.p0);
|
||||
$[0] = props.p0;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
const y = x;
|
||||
if (props.p1) {
|
||||
let t0;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = [];
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
y = x;
|
||||
if (props.p1) {
|
||||
let t0;
|
||||
if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = [];
|
||||
$[5] = t0;
|
||||
} else {
|
||||
t0 = $[5];
|
||||
}
|
||||
x = t0;
|
||||
}
|
||||
x = t0;
|
||||
}
|
||||
|
||||
y.push(props.p2);
|
||||
const c_3 = $[3] !== x;
|
||||
const c_4 = $[4] !== y;
|
||||
let t1;
|
||||
if (c_3 || c_4) {
|
||||
t1 = <Component x={x} y={y} />;
|
||||
y.push(props.p2);
|
||||
$[0] = props.p0;
|
||||
$[1] = props.p1;
|
||||
$[2] = props.p2;
|
||||
$[3] = x;
|
||||
$[4] = y;
|
||||
$[5] = t1;
|
||||
} else {
|
||||
t1 = $[5];
|
||||
x = $[3];
|
||||
y = $[4];
|
||||
}
|
||||
const c_6 = $[6] !== x;
|
||||
const c_7 = $[7] !== y;
|
||||
let t1;
|
||||
if (c_6 || c_7) {
|
||||
t1 = <Component x={x} y={y} />;
|
||||
$[6] = x;
|
||||
$[7] = y;
|
||||
$[8] = t1;
|
||||
} else {
|
||||
t1 = $[8];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ function Component(props) {
|
||||
x = [];
|
||||
}
|
||||
|
||||
let _ = <Component x={x} />;
|
||||
y.push(props.p2);
|
||||
|
||||
return <Component x={x} y={y} />;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = foo(props.x);
|
||||
const fn = function () {
|
||||
const arr = [...bar(props)];
|
||||
return arr.at(x);
|
||||
};
|
||||
const fnResult = fn();
|
||||
return fnResult;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## HIR
|
||||
|
||||
```javascript
|
||||
bb0 (block):
|
||||
[1] mutate $31:TFunction = Global foo
|
||||
[2] mutate $32 = LoadLocal read props$30
|
||||
[3] mutate $33 = PropertyLoad read $32.x
|
||||
[4] mutate $34 = Call read $31:TFunction(read $33)
|
||||
[5] store $36 = StoreLocal Const mutate x$35 = capture $34
|
||||
[6] mutate $37 = LoadLocal read props$30
|
||||
[7] mutate $38 = LoadLocal capture x$35
|
||||
[8] store $39[8:12]:TFunction = Function @deps[read $37,read $38]:
|
||||
bb0 (block):
|
||||
[1] mutate $49:TFunction = Global bar
|
||||
[2] mutate $50[2:4] = LoadLocal capture props$47[0:4]
|
||||
[3] mutate $51 = Call read $49:TFunction(mutate $50[2:4])
|
||||
[4] store $52:TObject<Array> = Array [...capture $51]
|
||||
[5] store $54:TObject<Array> = StoreLocal Const store arr$53:TObject<Array> = capture $52:TObject<Array>
|
||||
[6] mutate $55:TObject<Array> = LoadLocal capture arr$53:TObject<Array>
|
||||
[7] mutate $56 = LoadLocal capture x$48
|
||||
[8] mutate $57:TFunction<<generated_0>> = PropertyLoad read $55:TObject<Array>.at
|
||||
[9] mutate $58 = PropertyCall read $55:TObject<Array>.read $57:TFunction<<generated_0>>(read $56)
|
||||
[10] Return freeze $58
|
||||
[9] store $41[9:12]:TFunction = StoreLocal Const mutate fn$40[9:12]:TFunction = capture $39[8:12]:TFunction
|
||||
[10] mutate $42[10:12]:TFunction = LoadLocal capture fn$40[9:12]:TFunction
|
||||
[11] mutate $43 = Call mutate $42[10:12]:TFunction()
|
||||
[12] store $45 = StoreLocal Const mutate fnResult$44 = capture $43
|
||||
[13] mutate $46 = LoadLocal capture fnResult$44
|
||||
[14] Return freeze $46
|
||||
```
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
function Component(props) {
|
||||
const x = foo(props.x);
|
||||
const fn = function () {
|
||||
const arr = [...bar(props)];
|
||||
return arr.at(x);
|
||||
};
|
||||
const fnResult = fn();
|
||||
return fnResult;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// arrayInstance.at should have the following effects:
|
||||
// - read on arg0
|
||||
// - read on receiver
|
||||
// - mutate on lvalue
|
||||
function ArrayAtTest(props) {
|
||||
const arr = [foo(props.x)];
|
||||
const result = arr.at(bar(props.y));
|
||||
return result;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## HIR
|
||||
|
||||
```javascript
|
||||
bb0 (block):
|
||||
[1] mutate $20:TFunction = Global foo
|
||||
[2] mutate $21 = LoadLocal read props$19
|
||||
[3] mutate $22 = PropertyLoad read $21.x
|
||||
[4] mutate $23 = Call read $20:TFunction(read $22)
|
||||
[5] store $24:TObject<Array> = Array [capture $23]
|
||||
[6] store $26:TObject<Array> = StoreLocal Const store arr$25:TObject<Array> = capture $24:TObject<Array>
|
||||
[7] mutate $27:TObject<Array> = LoadLocal capture arr$25:TObject<Array>
|
||||
[8] mutate $28:TFunction = Global bar
|
||||
[9] mutate $29 = LoadLocal read props$19
|
||||
[10] mutate $30 = PropertyLoad read $29.y
|
||||
[11] mutate $31 = Call read $28:TFunction(read $30)
|
||||
[12] mutate $32:TFunction<<generated_0>> = PropertyLoad read $27:TObject<Array>.at
|
||||
[13] mutate $33 = PropertyCall read $27:TObject<Array>.read $32:TFunction<<generated_0>>(read $31)
|
||||
[14] store $35 = StoreLocal Const mutate result$34 = capture $33
|
||||
[15] mutate $36 = LoadLocal capture result$34
|
||||
[16] Return freeze $36
|
||||
```
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// arrayInstance.at should have the following effects:
|
||||
// - read on arg0
|
||||
// - read on receiver
|
||||
// - mutate on lvalue
|
||||
function ArrayAtTest(props) {
|
||||
const arr = [foo(props.x)];
|
||||
const result = arr.at(bar(props.y));
|
||||
return result;
|
||||
}
|
||||
@@ -21,20 +21,20 @@ bb0 (block):
|
||||
[3] mutate $30 = LoadLocal read props$27
|
||||
[4] mutate $31 = PropertyLoad read $30.b
|
||||
[5] mutate $32:TPrimitive = "hello"
|
||||
[6] store $33[6:18]:TObject<Array> = Array [read $29, read $31, read $32:TPrimitive]
|
||||
[7] store $35[7:18]:TObject<Array> = StoreLocal Const store a$34[7:18]:TObject<Array> = capture $33[6:18]:TObject<Array>
|
||||
[8] mutate $36[8:18]:TObject<Array> = LoadLocal capture a$34[7:18]:TObject<Array>
|
||||
[6] store $33[6:12]:TObject<Array> = Array [read $29, read $31, read $32:TPrimitive]
|
||||
[7] store $35[7:12]:TObject<Array> = StoreLocal Const store a$34[7:12]:TObject<Array> = capture $33[6:12]:TObject<Array>
|
||||
[8] mutate $36[8:12]:TObject<Array> = LoadLocal capture a$34[7:12]:TObject<Array>
|
||||
[9] mutate $37:TPrimitive = 42
|
||||
[10] mutate $38[10:18]:TFunction<<generated_2>> = PropertyLoad read $36[8:18]:TObject<Array>.push
|
||||
[11] mutate $39:TPrimitive = PropertyCall mutate $36[8:18]:TObject<Array>.read $38[10:18]:TFunction<<generated_2>>(read $37:TPrimitive)
|
||||
[10] mutate $38[10:12]:TFunction<<generated_2>> = PropertyLoad read $36[8:12]:TObject<Array>.push
|
||||
[11] mutate $39:TPrimitive = PropertyCall mutate $36[8:12]:TObject<Array>.read $38[10:12]:TFunction<<generated_2>>(read $37:TPrimitive)
|
||||
[12] store $41:TPrimitive = StoreLocal Const mutate x$40:TPrimitive = capture $39:TPrimitive
|
||||
[13] mutate $42[13:18]:TObject<Array> = LoadLocal capture a$34[7:18]:TObject<Array>
|
||||
[13] mutate $42:TObject<Array> = LoadLocal capture a$34[7:12]:TObject<Array>
|
||||
[14] mutate $43 = LoadLocal read props$27
|
||||
[15] mutate $44 = PropertyLoad read $43.c
|
||||
[16] mutate $45[16:18]:TFunction<<generated_0>> = PropertyLoad read $42[13:18]:TObject<Array>.at
|
||||
[17] mutate $46 = PropertyCall mutate $42[13:18]:TObject<Array>.read $45[16:18]:TFunction<<generated_0>>(read $44)
|
||||
[16] mutate $45:TFunction<<generated_0>> = PropertyLoad read $42:TObject<Array>.at
|
||||
[17] mutate $46 = PropertyCall read $42:TObject<Array>.read $45:TFunction<<generated_0>>(read $44)
|
||||
[18] store $48 = StoreLocal Const mutate y$47 = capture $46
|
||||
[19] mutate $49:TObject<Array> = LoadLocal capture a$34[7:18]:TObject<Array>
|
||||
[19] mutate $49:TObject<Array> = LoadLocal capture a$34[7:12]:TObject<Array>
|
||||
[20] mutate $50:TPrimitive = LoadLocal capture x$40:TPrimitive
|
||||
[21] mutate $51 = LoadLocal capture y$47
|
||||
[22] store $52:TObject<Object> = Object { a: capture $49:TObject<Array>, x: capture $50:TPrimitive, y: capture $51 }
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// arrayInstance.push should have the following effects:
|
||||
// - read on all args (rest parameter)
|
||||
// - mutate on receiver
|
||||
function Component(props) {
|
||||
const x = foo(props.x);
|
||||
const y = { y: props.y };
|
||||
const arr = [];
|
||||
arr.push({});
|
||||
arr.push(x, y);
|
||||
return arr;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## HIR
|
||||
|
||||
```javascript
|
||||
bb0 (block):
|
||||
[1] mutate $27:TFunction = Global foo
|
||||
[2] mutate $28 = LoadLocal read props$26
|
||||
[3] mutate $29 = PropertyLoad read $28.x
|
||||
[4] mutate $30 = Call read $27:TFunction(read $29)
|
||||
[5] store $32 = StoreLocal Const mutate x$31 = capture $30
|
||||
[6] mutate $33 = LoadLocal read props$26
|
||||
[7] mutate $34 = PropertyLoad read $33.y
|
||||
[8] store $35:TObject<Object> = Object { y: read $34 }
|
||||
[9] store $37:TObject<Object> = StoreLocal Const store y$36:TObject<Object> = capture $35:TObject<Object>
|
||||
[10] store $38[10:21]:TObject<Array> = Array []
|
||||
[11] store $40[11:21]:TObject<Array> = StoreLocal Const store arr$39[11:21]:TObject<Array> = capture $38[10:21]:TObject<Array>
|
||||
[12] mutate $41[12:21]:TObject<Array> = LoadLocal capture arr$39[11:21]:TObject<Array>
|
||||
[13] store $42:TObject<Object> = Object { }
|
||||
[14] mutate $43[14:21]:TFunction<<generated_2>> = PropertyLoad read $41[12:21]:TObject<Array>.push
|
||||
[15] mutate $44:TPrimitive = PropertyCall mutate $41[12:21]:TObject<Array>.read $43[14:21]:TFunction<<generated_2>>(capture $42:TObject<Object>)
|
||||
[16] mutate $45[16:21]:TObject<Array> = LoadLocal capture arr$39[11:21]:TObject<Array>
|
||||
[17] mutate $46 = LoadLocal capture x$31
|
||||
[18] mutate $47:TObject<Object> = LoadLocal capture y$36:TObject<Object>
|
||||
[19] mutate $48[19:21]:TFunction<<generated_2>> = PropertyLoad read $45[16:21]:TObject<Array>.push
|
||||
[20] mutate $49:TPrimitive = PropertyCall mutate $45[16:21]:TObject<Array>.read $48[19:21]:TFunction<<generated_2>>(capture $46, capture $47:TObject<Object>)
|
||||
[21] mutate $50:TObject<Array> = LoadLocal capture arr$39[11:21]:TObject<Array>
|
||||
[22] Return freeze $50:TObject<Array>
|
||||
```
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// arrayInstance.push should have the following effects:
|
||||
// - read on all args (rest parameter)
|
||||
// - mutate on receiver
|
||||
function Component(props) {
|
||||
const x = foo(props.x);
|
||||
const y = { y: props.y };
|
||||
const arr = [];
|
||||
arr.push({});
|
||||
arr.push(x, y);
|
||||
return arr;
|
||||
}
|
||||
Reference in New Issue
Block a user