StoreLocal instruction

Adds a new `StoreLocal <kind> <place> = <value>` instruction which stores 
<value> into <place>. With this change, Instruction.lvalue is _always_ a `const` 
temporary, and never a named identifier (there's a new validation pass to assert 
this). StoreLocal is the only way to declare or update a named identifier: the 
instructionKind property says whether it's a const/let declaration or a 
reassignment. Naturally a _lot_ of passes had to be updated to make this work, 
but the existing Effect.Store variant that @gsathya added made this overall 
straightforward. 

Note that as of this PR several passes still have code to handle the possibility 
of an instruction lvalue being something other than a temporary. When we clean 
that up in a follow-up, there will be a lot less of the duplication that appears 
here. For example, CodegenReactiveFunction has two places to handle variable 
declarations in this PR. However, one of them is to handle lvalues, which should 
now _always_ be temporaries and never emit a regular variable declaration. 
Similarly, several passes have to build up a table of identifier -> identifier 
(because of LoadLocal). Longer-term, we should update the Place abstraction so 
that it directly specifies the instruction which created that temporary, so we 
can look it up on demand instead of needing an extra mapping.
This commit is contained in:
Joe Savona
2023-03-02 14:19:17 -08:00
parent 440fd1f24a
commit 487786f7d4
90 changed files with 1259 additions and 772 deletions
+3
View File
@@ -13,6 +13,7 @@ import {
ReactiveFunction,
} from "./HIR";
import { EnvironmentOptions, mergeOptions } from "./HIR/Environment";
import { validateConsistentIdentifiers } from "./HIR/ValidateConsistentIdentifiers";
import {
analyseFunctions,
dropMemoCalls,
@@ -60,6 +61,8 @@ export function* run(
enterSSA(hir);
yield log({ kind: "hir", name: "SSA", value: hir });
validateConsistentIdentifiers(hir);
eliminateRedundantPhi(hir);
yield log({ kind: "hir", name: "EliminateRedundantPhi", value: hir });
+119 -65
View File
@@ -110,7 +110,7 @@ export function lower(
param.node.loc ?? GeneratedSource,
InstructionKind.Let,
param,
{ kind: "LoadLocal", place, loc: place.loc }
place
);
} else {
builder.errors.push({
@@ -590,7 +590,7 @@ function lowerStatement(
}
case "VariableDeclaration": {
const stmt = stmtPath as NodePath<t.VariableDeclaration>;
const nodeKind: string = stmt.node.kind;
const nodeKind: t.VariableDeclaration["kind"] = stmt.node.kind;
if (nodeKind === "var") {
builder.errors.push({
reason: `(BuildHIR::lowerStatement) Handle ${nodeKind} kinds in VariableDeclaration`,
@@ -604,15 +604,24 @@ function lowerStatement(
for (const declaration of stmt.get("declarations")) {
const id = declaration.get("id");
const init = declaration.get("init");
let value: InstructionValue;
let value: Place;
if (init.node != null) {
value = lowerExpression(builder, init as NodePath<t.Expression>);
value = lowerExpressionToTemporary(
builder,
init as NodePath<t.Expression>
);
} else {
value = {
kind: "Primitive",
value: undefined,
loc: id.node.loc ?? GeneratedSource,
};
value = buildTemporaryPlace(builder, id.node.loc ?? GeneratedSource);
builder.push({
id: makeInstructionId(0),
lvalue: { place: { ...value }, kind: InstructionKind.Const },
value: {
kind: "Primitive",
value: undefined,
loc: id.node.loc ?? GeneratedSource,
},
loc: value.loc,
});
}
lowerAssignment(
builder,
@@ -1177,7 +1186,7 @@ function lowerExpression(
left.node.loc ?? GeneratedSource,
InstructionKind.Reassign,
left,
lowerExpression(builder, expr.get("right"))
lowerExpressionToTemporary(builder, expr.get("right"))
);
}
@@ -1209,21 +1218,41 @@ function lowerExpression(
switch (leftNode.type) {
case "Identifier": {
const leftExpr = left as NodePath<t.Identifier>;
const place = lowerIdentifier(builder, leftExpr);
const identifier = lowerIdentifier(builder, leftExpr);
const leftPlace = lowerExpressionToTemporary(builder, leftExpr);
const right = lowerExpressionToTemporary(builder, expr.get("right"));
const binaryPlace = buildTemporaryPlace(builder, exprLoc);
builder.push({
id: makeInstructionId(0),
lvalue: { place: { ...place }, kind: InstructionKind.Reassign },
lvalue: {
place: { ...binaryPlace },
kind: InstructionKind.Const,
},
value: {
kind: "BinaryExpression",
operator: binaryOperator,
left: { ...place },
left: leftPlace,
right,
loc: exprLoc,
},
loc: exprLoc,
});
return { kind: "LoadLocal", place, loc: exprLoc };
const result = buildTemporaryPlace(builder, exprLoc);
builder.push({
id: makeInstructionId(0),
lvalue: { place: { ...result }, kind: InstructionKind.Const },
value: {
kind: "StoreLocal",
lvalue: {
place: { ...identifier },
kind: InstructionKind.Reassign,
},
value: { ...binaryPlace },
loc: exprLoc,
},
loc: exprLoc,
});
return { kind: "LoadLocal", place: identifier, loc: exprLoc };
}
case "MemberExpression": {
// a.b.c += <right>
@@ -1546,13 +1575,13 @@ function lowerExpression(
});
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
}
const temp = buildTemporaryPlace(
const primitiveTemp = buildTemporaryPlace(
builder,
expr.node.loc ?? GeneratedSource
);
builder.push({
id: makeInstructionId(0),
lvalue: { place: { ...temp }, kind: InstructionKind.Const },
lvalue: { place: { ...primitiveTemp }, kind: InstructionKind.Const },
value: {
kind: "Primitive",
value: 1,
@@ -1560,18 +1589,39 @@ function lowerExpression(
},
loc: expr.node.loc ?? GeneratedSource,
});
const temp = buildTemporaryPlace(
builder,
expr.node.loc ?? GeneratedSource
);
const identifier = lowerIdentifier(
builder,
argument as NodePath<t.Identifier>
);
builder.push({
id: makeInstructionId(0),
lvalue: { place: { ...identifier }, kind: InstructionKind.Reassign },
lvalue: { place: { ...temp }, kind: InstructionKind.Const },
value: {
kind: "BinaryExpression",
operator: expr.node.operator === "++" ? "+" : "-",
left: { ...identifier },
right: { ...temp },
right: { ...primitiveTemp },
loc: exprLoc,
},
loc: exprLoc,
});
builder.push({
id: makeInstructionId(0),
lvalue: {
place: buildTemporaryPlace(builder, exprLoc),
kind: InstructionKind.Const,
},
value: {
kind: "StoreLocal",
lvalue: {
place: { ...identifier },
kind: InstructionKind.Reassign,
},
value: { ...temp },
loc: exprLoc,
},
loc: exprLoc,
@@ -1883,7 +1933,7 @@ function lowerAssignment(
loc: SourceLocation,
kind: InstructionKind,
lvaluePath: NodePath<t.LVal>,
value: InstructionValue
value: Place
): InstructionValue {
const lvalueNode = lvaluePath.node;
switch (lvalueNode.type) {
@@ -1913,6 +1963,7 @@ function lowerAssignment(
};
}
const temporary = buildTemporaryPlace(builder, loc);
const place: Place = {
kind: "Identifier",
identifier: identifier,
@@ -1921,28 +1972,24 @@ function lowerAssignment(
};
builder.push({
id: makeInstructionId(0),
lvalue: { place: { ...place }, kind },
value,
lvalue: { place: { ...temporary }, kind },
value: {
kind: "StoreLocal",
lvalue: {
place: { ...place },
kind,
},
value,
loc,
},
loc,
});
return { kind: "LoadLocal", place, loc: place.loc };
return { kind: "LoadLocal", place, loc: temporary.loc };
}
case "MemberExpression": {
const lvalue = lvaluePath as NodePath<t.MemberExpression>;
const property = lvalue.get("property");
const object = lowerExpressionToTemporary(builder, lvalue.get("object"));
let valuePlace: Place;
if (value.kind === "LoadLocal") {
valuePlace = value.place;
} else {
valuePlace = buildTemporaryPlace(builder, loc);
builder.push({
id: makeInstructionId(0),
lvalue: { place: { ...valuePlace }, kind: InstructionKind.Const },
value,
loc,
});
}
if (!lvalue.node.computed) {
if (!property.isIdentifier()) {
builder.errors.push({
@@ -1956,7 +2003,7 @@ function lowerAssignment(
kind: "PropertyStore",
object,
property: property.node.name,
value: valuePlace,
value,
loc,
};
} else {
@@ -1974,20 +2021,13 @@ function lowerAssignment(
kind: "ComputedStore",
object,
property: propertyPlace,
value: valuePlace,
value,
loc,
};
}
}
case "ArrayPattern": {
const lvalue = lvaluePath as NodePath<t.ArrayPattern>;
const arrayPlace = buildTemporaryPlace(builder, loc);
builder.push({
id: makeInstructionId(0),
lvalue: { place: { ...arrayPlace }, kind: InstructionKind.Const },
value,
loc,
});
const elements = lvalue.get("elements");
let hasError = false;
for (let i = 0; i < elements.length; i++) {
@@ -2018,27 +2058,32 @@ function lowerAssignment(
},
loc: element.node.loc ?? GeneratedSource,
});
const value: InstructionValue = {
kind: "ComputedLoad",
const propertyPlace = buildTemporaryPlace(builder, property.loc);
builder.push({
id: makeInstructionId(0),
lvalue: { place: { ...propertyPlace }, kind: InstructionKind.Const },
value: {
kind: "ComputedLoad",
loc,
object: { ...value },
property,
},
loc,
object: { ...arrayPlace },
property,
};
lowerAssignment(builder, loc, kind, element as NodePath<t.LVal>, value);
});
lowerAssignment(
builder,
loc,
kind,
element as NodePath<t.LVal>,
propertyPlace
);
}
return hasError
? { kind: "UnsupportedNode", node: lvalueNode, loc }
: { kind: "LoadLocal", place: arrayPlace, loc: arrayPlace.loc };
: { kind: "LoadLocal", place: value, loc: value.loc };
}
case "ObjectPattern": {
const lvalue = lvaluePath as NodePath<t.ObjectPattern>;
const objectPlace = buildTemporaryPlace(builder, loc);
builder.push({
id: makeInstructionId(0),
lvalue: { place: { ...objectPlace }, kind },
value,
loc,
});
const properties = lvalue.get("properties");
let hasError = false;
for (let i = 0; i < properties.length; i++) {
@@ -2072,18 +2117,27 @@ function lowerAssignment(
hasError = true;
continue;
}
const value: InstructionValue = {
kind: "PropertyLoad",
const propertyPlace = buildTemporaryPlace(
builder,
property.node.loc ?? GeneratedSource
);
builder.push({
id: makeInstructionId(0),
lvalue: { place: { ...propertyPlace }, kind: InstructionKind.Const },
value: {
kind: "PropertyLoad",
loc,
object: { ...value },
property: key.node.name,
optional: false, // Key of ObjectPattern (evaluation of LVal) cannot be optional.
},
loc,
object: { ...objectPlace },
property: key.node.name,
optional: false, // Key of ObjectPattern (evaluation of LVal) cannot be optional.
};
lowerAssignment(builder, loc, kind, element, value);
});
lowerAssignment(builder, loc, kind, element, propertyPlace);
}
return hasError
? { kind: "UnsupportedNode", node: lvalueNode, loc }
: { kind: "LoadLocal", place: objectPlace, loc: objectPlace.loc };
: { kind: "LoadLocal", place: value, loc: value.loc };
}
default: {
builder.errors.push({
+6
View File
@@ -418,6 +418,12 @@ export type InstructionValue =
place: Place;
loc: SourceLocation;
}
| {
kind: "StoreLocal";
lvalue: LValue;
value: Place;
loc: SourceLocation;
}
| {
kind: "Primitive";
value: number | boolean | string | null | undefined;
+20
View File
@@ -315,6 +315,12 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
value = `LoadLocal ${printPlace(instrValue.place)}`;
break;
}
case "StoreLocal": {
value = `StoreLocal ${instrValue.lvalue.kind} ${printPlace(
instrValue.lvalue.place
)} = ${printPlace(instrValue.value)}`;
break;
}
case "PropertyLoad": {
value = `PropertyLoad ${printPlace(instrValue.object)}.${
instrValue.property
@@ -412,6 +418,20 @@ function isMutable(range: MutableRange): boolean {
}
function printMutableRange(identifier: Identifier): string {
invariant(
(identifier.mutableRange.start === 0 &&
identifier.mutableRange.end === 0) ||
identifier.mutableRange.end > identifier.mutableRange.start,
"Identifier mutableRange was invalid: [%s:%s]",
identifier.mutableRange.start,
identifier.mutableRange.end
);
if (identifier.scope !== null) {
invariant(
identifier.scope.range.end > identifier.scope.range.start,
"Identifier scope mutableRange was invalid"
);
}
const range =
identifier.scope !== null
? identifier.scope.range
@@ -0,0 +1,69 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "../CompilerError";
import {
GeneratedSource,
HIRFunction,
Identifier,
IdentifierId,
SourceLocation,
} from "./HIR";
import { eachInstructionValueOperand, eachTerminalOperand } from "./visitors";
/**
* Validation pass to check that there is a 1:1 mapping between Identifier objects and IdentifierIds,
* ie there can only be one Identifier instance per IdentifierId.
*/
export function validateConsistentIdentifiers(fn: HIRFunction): void {
const identifiers: Identifiers = new Map();
for (const [, block] of fn.body.blocks) {
for (const phi of block.phis) {
validate(identifiers, phi.id);
for (const [, operand] of phi.operands) {
validate(identifiers, operand);
}
}
for (const instr of block.instructions) {
if (instr.lvalue.place.identifier.name !== null) {
CompilerError.invariant(
`Expected all lvalues to be temporaries, found '${instr.lvalue.place.identifier.name}'`,
instr.lvalue.place.loc
);
}
validate(
identifiers,
instr.lvalue.place.identifier,
instr.lvalue.place.loc
);
for (const operand of eachInstructionValueOperand(instr.value)) {
validate(identifiers, operand.identifier, operand.loc);
}
}
for (const operand of eachTerminalOperand(block.terminal)) {
validate(identifiers, operand.identifier, operand.loc);
}
}
}
type Identifiers = Map<IdentifierId, Identifier>;
function validate(
identifiers: Identifiers,
identifier: Identifier,
loc: SourceLocation | null = null
): void {
const previous = identifiers.get(identifier.id);
if (previous === undefined) {
identifiers.set(identifier.id, identifier);
} else if (identifier !== previous) {
CompilerError.invariant(
`Duplicate identifier for id ${identifier.id}`,
loc ?? GeneratedSource
);
}
}
+10
View File
@@ -52,6 +52,11 @@ export function* eachInstructionValueOperand(
yield instrValue.place;
break;
}
case "StoreLocal": {
yield instrValue.lvalue.place;
yield instrValue.value;
break;
}
case "PropertyLoad": {
yield instrValue.object;
break;
@@ -181,6 +186,11 @@ export function mapInstructionOperands(
instrValue.place = fn(instrValue.place);
break;
}
case "StoreLocal": {
instrValue.lvalue.place = fn(instrValue.lvalue.place);
instrValue.value = fn(instrValue.value);
break;
}
case "NewExpression":
case "CallExpression": {
instrValue.callee = fn(instrValue.callee);
+10 -14
View File
@@ -3,33 +3,24 @@ import {
Identifier,
Instruction,
isPrimitiveType,
LValue,
Place,
} from "../HIR/HIR";
import DisjointSet from "../Utils/DisjointSet";
export type AliasSet = Set<Identifier>;
class AliasAnalyser {
aliases = new DisjointSet<Identifier>();
alias(lvalue: LValue, alias: Place) {
this.aliases.union([lvalue.place.identifier, alias.identifier]);
}
}
export function inferAliases(func: HIRFunction): DisjointSet<Identifier> {
const analyser = new AliasAnalyser();
const aliases = new DisjointSet<Identifier>();
for (const [_, block] of func.body.blocks) {
for (const instr of block.instructions) {
inferInstr(instr, analyser);
inferInstr(instr, aliases);
}
}
return analyser.aliases;
return aliases;
}
function inferInstr(instr: Instruction, state: AliasAnalyser) {
function inferInstr(instr: Instruction, aliases: DisjointSet<Identifier>) {
const { lvalue, value: instrValue } = instr;
let alias: Place | null = null;
switch (instrValue.kind) {
@@ -40,6 +31,11 @@ function inferInstr(instr: Instruction, state: AliasAnalyser) {
alias = instrValue.place;
break;
}
case "StoreLocal": {
// aliases.union([instrValue.place.identifier, instrValue.value.identifier]);
alias = instrValue.value;
break;
}
case "ComputedLoad":
case "PropertyLoad": {
alias = instrValue.object;
@@ -53,5 +49,5 @@ function inferInstr(instr: Instruction, state: AliasAnalyser) {
return;
}
state.alias(lvalue, alias);
aliases.union([lvalue.place.identifier, alias.identifier]);
}
@@ -24,6 +24,9 @@ export function inferAliasForStores(
if (lvalue.place.effect !== Effect.Store) {
continue;
}
if (value.kind === "StoreLocal") {
maybeAlias(aliases, value.lvalue.place, value.value, instr.id);
}
for (const operand of eachInstructionValueOperand(value)) {
if (
operand.effect === Effect.Capture ||
@@ -118,8 +118,15 @@ export function inferMutableLifetimes(
}
for (const instr of block.instructions) {
for (const input of eachInstructionOperand(instr)) {
inferPlace(input, instr, inferMutableRangeForStores);
if (instr.value.kind === "StoreLocal") {
inferPlace(instr.value.value, instr, inferMutableRangeForStores);
instr.value.lvalue.place.identifier.mutableRange.start = instr.id;
instr.value.lvalue.place.identifier.mutableRange.end =
makeInstructionId(instr.id + 1);
} else {
for (const input of eachInstructionOperand(instr)) {
inferPlace(input, instr, inferMutableRangeForStores);
}
}
const lvalueId = instr.lvalue.place.identifier;
@@ -307,9 +307,9 @@ class InferenceState {
}
case Effect.Capture: {
if (
valueKind === ValueKind.Immutable ||
valueKind === ValueKind.Frozen ||
valueKind === ValueKind.MaybeFrozen ||
valueKind === ValueKind.Immutable
valueKind === ValueKind.MaybeFrozen
) {
effect = Effect.Read;
} else {
@@ -790,6 +790,21 @@ function inferBlock(
state.alias(lvalue.place, instrValue.place);
continue;
}
case "StoreLocal": {
const effect =
state.isDefined(instrValue.lvalue.place) &&
state.kind(instrValue.lvalue.place) === ValueKind.Context
? Effect.Mutate
: Effect.Capture;
state.reference(instrValue.value, effect);
const lvalue = instr.lvalue;
state.alias(lvalue.place, instrValue.value);
lvalue.place.effect = Effect.Store;
state.alias(instrValue.lvalue.place, instrValue.value);
instrValue.lvalue.place.effect = Effect.Store;
continue;
}
default: {
assertExhaustive(instrValue, "Unexpected instruction kind");
}
@@ -313,6 +313,14 @@ function evaluateInstruction(
case "LoadLocal": {
return read(constants, value.place);
}
case "StoreLocal": {
const placeValue = read(constants, value.value);
if (placeValue !== null) {
constants.set(value.lvalue.place.identifier.id, placeValue);
}
// NOTE: always return null to avoid replacing the StoreLocal with its value
return null;
}
default: {
// TODO: handle more cases
return null;
@@ -40,7 +40,7 @@ export function deadCodeElimination(fn: HIRFunction): void {
const instr = block.instructions[i]!;
if (
!used.has(instr.lvalue.place.identifier) &&
pruneableValue(instr.value) &&
pruneableValue(instr.value, used) &&
// Can't prune the last value of a value block, that's its value!
!(block.kind !== "block" && i === block.instructions.length - 1)
) {
@@ -76,8 +76,15 @@ export function deadCodeElimination(fn: HIRFunction): void {
* Returns true if it is safe to prune an instruction with the given value.
* Functions which may have side-
*/
function pruneableValue(value: InstructionValue): boolean {
function pruneableValue(
value: InstructionValue,
used: Set<Identifier>
): boolean {
switch (value.kind) {
case "StoreLocal": {
// Stores are pruneable only if the identifier being stored to is never read later
return !used.has(value.lvalue.place.identifier);
}
case "CallExpression":
case "ComputedCall":
case "ComputedStore":
@@ -99,11 +99,7 @@ function codegenBlock(cx: Context, block: ReactiveBlock): t.BlockStatement {
for (const item of block) {
switch (item.kind) {
case "instruction": {
const statement = codegenInstructionNullable(
cx,
item.instruction,
codegenInstructionValue(cx, item.instruction.value)
);
const statement = codegenInstructionNullable(cx, item.instruction);
if (statement !== null) {
statements.push(statement);
}
@@ -359,23 +355,47 @@ function codegenTerminal(
function codegenInstructionNullable(
cx: Context,
instr: ReactiveInstruction,
value: t.Expression
instr: ReactiveInstruction
): t.Statement | null {
let statement;
if (instr.lvalue !== null && cx.hasDeclared(instr.lvalue.place.identifier)) {
statement = codegenInstruction(
cx,
{
...instr,
lvalue: {
...instr.lvalue,
kind: InstructionKind.Reassign,
},
},
value
);
if (instr.value.kind === "StoreLocal") {
const kind = cx.hasDeclared(instr.value.lvalue.place.identifier)
? InstructionKind.Reassign
: instr.value.lvalue.kind;
const value = codegenPlace(cx, instr.value.value);
switch (kind) {
case InstructionKind.Const: {
return createVariableDeclaration(instr.loc, "const", [
t.variableDeclarator(
convertIdentifier(instr.value.lvalue.place.identifier),
value
),
]);
}
case InstructionKind.Let: {
return createVariableDeclaration(instr.loc, "let", [
t.variableDeclarator(
convertIdentifier(instr.value.lvalue.place.identifier),
value
),
]);
}
case InstructionKind.Reassign: {
return createExpressionStatement(
instr.loc,
t.assignmentExpression(
"=",
convertIdentifier(instr.value.lvalue.place.identifier),
value
)
);
}
default: {
assertExhaustive(kind, `Unexpected instruction kind '${kind}'`);
}
}
} else {
const value = codegenInstructionValue(cx, instr.value);
statement = codegenInstruction(cx, instr, value);
}
if (statement.type === "EmptyStatement") {
@@ -473,7 +493,10 @@ function codegenInstruction(
cx.temp.set(instr.lvalue.place.identifier.id, value);
return t.emptyStatement();
} else {
switch (instr.lvalue.kind) {
const kind = cx.hasDeclared(instr.lvalue.place.identifier)
? InstructionKind.Reassign
: instr.lvalue.kind;
switch (kind) {
case InstructionKind.Const: {
return createVariableDeclaration(instr.loc, "const", [
t.variableDeclarator(codegenLVal(instr.lvalue), value),
@@ -492,7 +515,7 @@ function codegenInstruction(
}
default: {
assertExhaustive(
instr.lvalue.kind,
kind,
`Unexpected instruction kind '${instr.lvalue.kind}'`
);
}
@@ -704,6 +727,12 @@ function codegenInstructionValue(
value = codegenPlace(cx, instrValue.place);
break;
}
case "StoreLocal": {
CompilerError.invariant(
`Unexpected StoreLocal in codegenInstructionValue`,
instrValue.loc
);
}
case "FunctionExpression": {
value = instrValue.expr;
break;
@@ -21,27 +21,34 @@ import {
} from "./visitors";
type IdentifierReactivity = Map<IdentifierId, boolean>;
class Visitor extends ReactiveFunctionVisitor<IdentifierReactivity> {
override visitInstruction(
instr: ReactiveInstruction,
reactivityMap: IdentifierReactivity
) {
this.traverseInstruction(instr, reactivityMap);
class State {
reactivityMap: IdentifierReactivity = new Map();
temporaries: Map<IdentifierId, IdentifierId> = new Map();
}
class Visitor extends ReactiveFunctionVisitor<State> {
override visitInstruction(instr: ReactiveInstruction, state: State) {
this.traverseInstruction(instr, state);
const lval = instr.lvalue;
if (lval == null) {
return;
}
const { value } = instr;
let hasReactiveInput = reactivityMap.get(lval.place.identifier.id) === true;
if (!hasReactiveInput && value.kind !== "LoadGlobal") {
for (const operand of eachReactiveValueOperand(value)) {
// We currently treat free variables (from module or global scope) as
// non-reactive. We may later want type information about specific
// free variables, or a toggle `treatFreeVarsAsReactive`.
if (reactivityMap.get(operand.identifier.id)) {
hasReactiveInput = true;
break;
}
let hasReactiveInput = false;
// Globals are currently treated as non-reactive this happens implicitly because LoadGlobal
// has no operands which can be registered as reactive.
// Consider adding an option to declare whether a given global can be reactive or not, or
// a more general "treat all globals as reactive" flag.
for (const operand of eachReactiveValueOperand(value)) {
if (operand.effect === Effect.Store) {
continue;
}
const resolvedId: IdentifierId =
state.temporaries.get(operand.identifier.id) ?? operand.identifier.id;
if (state.reactivityMap.get(resolvedId)) {
hasReactiveInput = true;
break;
}
}
if (
@@ -56,7 +63,7 @@ class Visitor extends ReactiveFunctionVisitor<IdentifierReactivity> {
// allow treating safe hooks as non-reactive.
hasReactiveInput = true;
}
reactivityMap.set(lval.place.identifier.id, hasReactiveInput);
state.reactivityMap.set(lval.place.identifier.id, hasReactiveInput);
if (hasReactiveInput) {
// all mutating effects must also be marked as reactive
@@ -65,12 +72,10 @@ class Visitor extends ReactiveFunctionVisitor<IdentifierReactivity> {
case Effect.Capture:
case Effect.Store:
case Effect.Mutate: {
// Explicitly compare to `false` here, since absence from the
// map indicates a free variable
// todo [@mofeiZ] add knowledge about free variables
if (reactivityMap.get(operand.identifier.id) === false) {
reactivityMap.set(operand.identifier.id, true);
}
const resolvedId: IdentifierId =
state.temporaries.get(operand.identifier.id) ??
operand.identifier.id;
state.reactivityMap.set(resolvedId, true);
break;
}
case Effect.Freeze:
@@ -90,6 +95,14 @@ class Visitor extends ReactiveFunctionVisitor<IdentifierReactivity> {
}
}
}
if (instr.lvalue !== null) {
if (instr.value.kind === "LoadLocal") {
state.temporaries.set(
instr.lvalue.place.identifier.id,
instr.value.place.identifier.id
);
}
}
}
}
/**
@@ -134,18 +147,14 @@ export function inferReactiveIdentifiers(
fn: ReactiveFunction
): Set<IdentifierId> {
const visitor = new Visitor();
const reactivityMap: IdentifierReactivity = new Map();
const state = new State();
for (const param of fn.params) {
reactivityMap.set(param.identifier.id, true);
state.reactivityMap.set(param.identifier.id, true);
}
let size: number;
do {
size = reactivityMap.size;
visitReactiveFunction(fn, visitor, reactivityMap);
} while (reactivityMap.size > size);
visitReactiveFunction(fn, visitor, state);
const result = new Set<IdentifierId>();
reactivityMap.forEach((isReactive, id) => {
state.reactivityMap.forEach((isReactive, id) => {
if (isReactive) result.add(id);
});
return result;
@@ -102,14 +102,29 @@ export function inferReactiveScopeVariables(fn: HIRFunction): void {
if (range.end > range.start + 1 || mayAllocate(instr.value)) {
operands.push(instr.lvalue!.place.identifier);
}
for (const operand of eachInstructionOperand(instr)) {
if (instr.value.kind === "StoreLocal") {
if (
isMutable(instr, operand) &&
// exclude global variables from being added to scopes, we can't recreate them!
// TODO: improve handling of module-scoped variables and globals
operand.identifier.mutableRange.start > 0
instr.value.lvalue.place.identifier.mutableRange.end >
instr.value.lvalue.place.identifier.mutableRange.start + 1
) {
operands.push(operand.identifier);
operands.push(instr.value.lvalue.place.identifier);
}
if (
isMutable(instr, instr.value.value) &&
instr.value.value.identifier.mutableRange.start > 0
) {
operands.push(instr.value.value.identifier);
}
} else {
for (const operand of eachInstructionOperand(instr)) {
if (
isMutable(instr, operand) &&
// exclude global variables from being added to scopes, we can't recreate them!
// TODO: improve handling of module-scoped variables and globals
operand.identifier.mutableRange.start > 0
) {
operands.push(operand.identifier);
}
}
}
if (operands.length !== 0) {
@@ -168,6 +183,7 @@ function isMutable({ id }: Instruction, place: Place): boolean {
function mayAllocate(value: InstructionValue): boolean {
switch (value.kind) {
case "StoreLocal":
case "LoadGlobal":
case "TypeCastExpression":
case "BinaryExpression":
@@ -522,18 +522,15 @@ class Context {
* Record a variable that is declared in some other scope and that is being reassigned in the
* current one as a {@link ReactiveScope.reassignments}
*/
visitReassignment(lvalue: LValue): void {
if (lvalue.kind !== InstructionKind.Reassign) {
return;
}
const declaration = this.#declarations.get(lvalue.place.identifier.id);
visitReassignment(place: Place): void {
const declaration = this.#declarations.get(place.identifier.id);
if (
this.currentScope != null &&
lvalue.place.identifier.scope != null &&
place.identifier.scope != null &&
declaration !== undefined &&
declaration.scope !== lvalue.place.identifier.scope
declaration.scope !== place.identifier.scope
) {
this.currentScope.reassignments.add(lvalue.place.identifier);
this.currentScope.reassignments.add(place.identifier);
}
}
}
@@ -570,21 +567,21 @@ function visit(context: Context, block: ReactiveBlock): void {
break;
}
case "for": {
visitReactiveValue(context, terminal.init);
visitReactiveValue(context, terminal.test);
visitReactiveValue(context, terminal.id, terminal.init);
visitReactiveValue(context, terminal.id, terminal.test);
context.enterConditional(() => {
visitReactiveValue(context, terminal.update);
visitReactiveValue(context, terminal.id, terminal.update);
visit(context, terminal.loop);
});
break;
}
case "do-while": {
visit(context, terminal.loop);
visitReactiveValue(context, terminal.test);
visitReactiveValue(context, terminal.id, terminal.test);
break;
}
case "while": {
visitReactiveValue(context, terminal.test);
visitReactiveValue(context, terminal.id, terminal.test);
context.enterConditional(() => {
visit(context, terminal.loop);
});
@@ -642,22 +639,25 @@ function visit(context: Context, block: ReactiveBlock): void {
}
}
function visitReactiveValue(context: Context, value: ReactiveValue): void {
function visitReactiveValue(
context: Context,
id: InstructionId,
value: ReactiveValue
): void {
switch (value.kind) {
case "LogicalExpression": {
visitReactiveValue(context, value.left);
visitReactiveValue(context, id, value.left);
context.enterConditional(() => {
visitReactiveValue(context, value.right);
visitReactiveValue(context, id, value.right);
});
break;
}
case "ConditionalExpression": {
visitReactiveValue(context, value.test);
visitReactiveValue(context, id, value.test);
context.enterConditional(() => {
visitReactiveValue(context, value.consequent);
visitReactiveValue(context, value.alternate);
visitReactiveValue(context, id, value.consequent);
visitReactiveValue(context, id, value.alternate);
});
break;
}
@@ -665,7 +665,7 @@ function visitReactiveValue(context: Context, value: ReactiveValue): void {
for (const instr of value.instructions) {
visitInstruction(context, instr);
}
visitInstructionValue(context, value.value, null);
visitInstructionValue(context, id, value.value, null);
break;
}
default: {
@@ -678,6 +678,7 @@ function visitReactiveValue(context: Context, value: ReactiveValue): void {
function visitInstructionValue(
context: Context,
id: InstructionId,
value: ReactiveValue,
lvalue: LValue | null
): void {
@@ -696,20 +697,26 @@ function visitInstructionValue(
} else {
context.visitProperty(value.object, value.property);
}
} else if (value.kind === "StoreLocal") {
context.visitOperand(value.value);
if (value.lvalue.kind === InstructionKind.Reassign) {
context.visitReassignment(value.lvalue.place);
}
context.declare(value.lvalue.place.identifier, {
id,
scope: context.currentScope,
});
} else {
visitReactiveValue(context, value);
visitReactiveValue(context, id, value);
}
}
function visitInstruction(context: Context, instr: ReactiveInstruction): void {
const { lvalue } = instr;
visitInstructionValue(context, instr.value, lvalue);
visitInstructionValue(context, instr.id, instr.value, lvalue);
if (lvalue == null) {
return;
}
if (lvalue.kind === InstructionKind.Reassign) {
context.visitReassignment(lvalue);
}
context.declare(lvalue.place.identifier, {
id: instr.id,
scope: context.currentScope,
+10 -1
View File
@@ -212,7 +212,16 @@ export default function enterSSA(func: HIRFunction): void {
}
for (const instr of block.instructions) {
mapInstructionOperands(instr, (place) => builder.getPlace(place));
if (instr.value.kind === "StoreLocal") {
const oldPlace = instr.value.lvalue.place;
const newPlace = builder.definePlace(oldPlace);
instr.lvalue.kind = InstructionKind.Const;
instr.value.lvalue.place = newPlace;
instr.value.value = builder.getPlace(instr.value.value);
} else {
mapInstructionOperands(instr, (place) => builder.getPlace(place));
}
const oldPlace = instr.lvalue.place;
const newPlace = builder.definePlace(oldPlace);
+89 -26
View File
@@ -110,7 +110,7 @@ export function leaveSSA(fn: HIRFunction): void {
for (const instr of block.instructions) {
// Iterate the instructions and perform any rewrites as well as promoting SSA variables to
// `let` or `reassign` where possible.
const { lvalue } = instr;
const { lvalue, value } = instr;
if (
lvalue.kind === InstructionKind.Const &&
rewrites.has(lvalue.place.identifier)
@@ -122,16 +122,30 @@ export function leaveSSA(fn: HIRFunction): void {
rewrites.get(lvalue.place.identifier) === lvalue.place.identifier
? InstructionKind.Let
: InstructionKind.Reassign;
} else if (lvalue.place.identifier.name != null) {
const originalLVal = declarations.get(lvalue.place.identifier.name);
} else if (
value.kind === "StoreLocal" &&
value.lvalue.place.identifier.name != null
) {
const originalLVal = declarations.get(
value.lvalue.place.identifier.name
);
if (originalLVal === undefined) {
declarations.set(lvalue.place.identifier.name, lvalue);
declarations.set(value.lvalue.place.identifier.name, value.lvalue);
value.lvalue.kind = InstructionKind.Const;
} else {
// This is an instance of the original id, so we need to promote the original declaration
// to a `let` and the current lval to a `reassign`
originalLVal.kind = InstructionKind.Let;
lvalue.kind = InstructionKind.Reassign;
}
} else if (
value.kind === "StoreLocal" &&
rewrites.has(value.lvalue.place.identifier)
) {
value.lvalue.kind =
rewrites.get(value.lvalue.place.identifier) ===
value.lvalue.place.identifier
? InstructionKind.Let
: InstructionKind.Reassign;
}
rewritePlace(lvalue.place, rewrites, declarations);
for (const operand of eachInstructionValueOperand(instr.value)) {
@@ -223,6 +237,58 @@ export function leaveSSA(fn: HIRFunction): void {
);
const declaration = declarations.get(phi.id.name);
if (declaration === undefined) {
let initValue: Place;
if (initOperand === null) {
initValue = {
effect: Effect.Read,
kind: "Identifier",
loc: GeneratedSource,
identifier: {
id: fn.env.nextIdentifierId,
name: null,
mutableRange: {
// TODO: this is technically the wrong start range; we do this because the instruction to create the
// undefined and the instruction to store it to the identifier share an InstructionId, which makes
// this value otherwise appear mutable when stored. All that matters is that the range end prior
// to the StoreLocal's instruction id, so we decrement by one.
start: makeInstructionId(block.terminal.id - 1),
end: makeInstructionId(block.terminal.id),
},
scope: null,
type: { kind: "Primitive" },
},
};
block.instructions.push({
id: block.terminal.id,
lvalue: {
place: { ...initValue, effect: Effect.Mutate },
kind: InstructionKind.Const,
},
value: {
kind: "Primitive",
// TODO: consider leaving the variable uninitialized rather than explicitly undefined.
value: undefined,
loc: GeneratedSource,
},
loc: GeneratedSource,
});
} else {
initValue = {
kind: "Identifier",
identifier: initOperand,
effect: Effect.Capture,
loc: GeneratedSource,
};
}
const lvalue: LValue = {
place: {
kind: "Identifier",
identifier: phi.id,
effect: Effect.Mutate,
loc: GeneratedSource,
},
kind: InstructionKind.Let,
};
const instr: Instruction = {
// NOTE: reuse the terminal id since these lets must be scoped with the terminal anyway.
// the mutable range of this canonical id must by definition span from the binding (before
@@ -231,34 +297,31 @@ export function leaveSSA(fn: HIRFunction): void {
lvalue: {
place: {
kind: "Identifier",
identifier: phi.id,
identifier: {
id: fn.env.nextIdentifierId,
mutableRange: {
start: block.terminal.id,
end: makeInstructionId(block.terminal.id + 1),
},
name: null,
scope: null,
type: phi.id.type,
},
effect: Effect.Mutate,
loc: GeneratedSource,
},
kind: InstructionKind.Let,
kind: InstructionKind.Const,
},
value: {
kind: "StoreLocal",
lvalue,
value: initValue,
loc: GeneratedSource,
},
value:
initOperand !== null
? {
kind: "LoadLocal",
place: {
kind: "Identifier",
identifier: initOperand,
effect: Effect.Read,
loc: GeneratedSource,
},
loc: GeneratedSource,
}
: {
kind: "Primitive",
// TODO: consider leaving the variable uninitialized rather than explicitly undefined.
value: undefined,
loc: GeneratedSource,
},
loc: GeneratedSource,
};
block.instructions.push(instr);
declarations.set(phi.id.name, instr.lvalue);
declarations.set(phi.id.name, lvalue);
phi.id.mutableRange.start = terminal.id;
if (!isPhiMutatedAfterCreation) {
phi.id.mutableRange.end = makeInstructionId(terminal.id + 1);
@@ -112,6 +112,15 @@ function* generateInstructionTypes(
break;
}
case "StoreLocal": {
yield equation(left, value.value.identifier.type);
yield equation(
value.lvalue.place.identifier.type,
value.value.identifier.type
);
break;
}
case "BinaryExpression": {
if (isPrimitiveBinaryOp(value.operator)) {
yield equation(value.left.identifier.type, { kind: "Primitive" });
@@ -20,14 +20,15 @@ function component(a) {
function component(a) {
const $ = React.unstable_useMemoCache(2);
const c_0 = $[0] !== a;
let x;
let t0;
if (c_0) {
x = { a: a };
t0 = { a: a };
$[0] = a;
$[1] = x;
$[1] = t0;
} else {
x = $[1];
t0 = $[1];
}
const x = t0;
(function () {
y = x;
@@ -18,13 +18,14 @@ function component() {
```javascript
function component() {
const $ = React.unstable_useMemoCache(3);
let z;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
z = [];
$[0] = z;
t0 = [];
$[0] = t0;
} else {
z = $[0];
t0 = $[0];
}
const z = t0;
let y;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
y = {};
@@ -41,63 +41,68 @@ function AllocatingPrimitiveAsDep(props) {
}
const t1 = t0.b + 1;
const c_2 = $[2] !== t1;
let y;
let t2;
if (c_2) {
y = foo(t1);
t2 = foo(t1);
$[2] = t1;
$[3] = y;
$[3] = t2;
} else {
y = $[3];
t2 = $[3];
}
const y = t2;
return y;
}
function PrimitiveAsDepNested(props) {
const $ = React.unstable_useMemoCache(10);
const $ = React.unstable_useMemoCache(11);
const c_0 = $[0] !== props.b;
const c_1 = $[1] !== props.a;
let x;
let y;
if (c_0 || c_1) {
x = {};
mutate(x);
const c_3 = $[3] !== props.b;
const c_4 = $[4] !== props.b;
let t0;
if (c_3) {
if (c_4) {
t0 = bar(props.b);
$[3] = props.b;
$[4] = t0;
$[4] = props.b;
$[5] = t0;
} else {
t0 = $[4];
t0 = $[5];
}
const t1 = t0 + 1;
const c_5 = $[5] !== t1;
let y;
if (c_5) {
y = foo(t1);
$[5] = t1;
$[6] = y;
const c_6 = $[6] !== t1;
let t2;
if (c_6) {
t2 = foo(t1);
$[6] = t1;
$[7] = t2;
} else {
y = $[6];
t2 = $[7];
}
y = t2;
mutate(x, props.a);
$[0] = props.b;
$[1] = props.a;
$[2] = x;
$[3] = y;
} else {
x = $[2];
y = $[3];
}
const c_7 = $[7] !== x;
const c_8 = $[8] !== y;
let t2;
if (c_7 || c_8) {
t2 = [x, y];
$[7] = x;
$[8] = y;
$[9] = t2;
const c_8 = $[8] !== x;
const c_9 = $[9] !== y;
let t3;
if (c_8 || c_9) {
t3 = [x, y];
$[8] = x;
$[9] = y;
$[10] = t3;
} else {
t2 = $[9];
t3 = $[10];
}
return t2;
return t3;
}
```
@@ -26,14 +26,15 @@ function foo(a, b, c) {
if (c_0 || c_1 || c_2) {
x = [a];
const c_5 = $[5] !== b;
let y;
let t0;
if (c_5) {
y = [null, b];
t0 = [null, b];
$[5] = b;
$[6] = y;
$[6] = t0;
} else {
y = $[6];
t0 = $[6];
}
const y = t0;
z = [[], [], [c]];
x[0] = y[1];
z[0][0] = x[0];
@@ -48,16 +49,16 @@ function foo(a, b, c) {
}
const c_7 = $[7] !== x;
const c_8 = $[8] !== z;
let t0;
let t1;
if (c_7 || c_8) {
t0 = [x, z];
t1 = [x, z];
$[7] = x;
$[8] = z;
$[9] = t0;
$[9] = t1;
} else {
t0 = $[9];
t1 = $[9];
}
return t0;
return t1;
}
```
@@ -24,12 +24,14 @@ function useBar(props) {
let z = undefined;
if (props.a) {
if (props.b) {
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
z = baz();
$[0] = z;
t0 = baz();
$[0] = t0;
} else {
z = $[0];
t0 = $[0];
}
z = t0;
}
}
return z;
@@ -16,8 +16,7 @@ function f() {
```javascript
function f() {
let x = 3;
x = x >>> 1;
const x = 3 >>> 1;
return x;
}
@@ -17,14 +17,15 @@ function component(t) {
const $ = React.unstable_useMemoCache(2);
const a = t.a;
const c_0 = $[0] !== a;
let y;
let t0;
if (c_0) {
y = { a: a };
t0 = { a: a };
$[0] = a;
$[1] = y;
$[1] = t0;
} else {
y = $[1];
t0 = $[1];
}
const y = t0;
return y;
}
@@ -22,46 +22,50 @@ function component(a, b) {
function component(a, b) {
const $ = React.unstable_useMemoCache(9);
const c_0 = $[0] !== b;
let y;
let t0;
if (c_0) {
y = { b: b };
t0 = { b: b };
$[0] = b;
$[1] = y;
$[1] = t0;
} else {
y = $[1];
t0 = $[1];
}
const y = t0;
const c_2 = $[2] !== a;
let z;
let t1;
if (c_2) {
z = { a: a };
t1 = { a: a };
$[2] = a;
$[3] = z;
$[3] = t1;
} else {
z = $[3];
t1 = $[3];
}
const z = t1;
const c_4 = $[4] !== z.a;
const c_5 = $[5] !== y.b;
let x;
let t2;
if (c_4 || c_5) {
x = function () {
t2 = function () {
z.a = 2;
y.b;
};
$[4] = z.a;
$[5] = y.b;
$[6] = x;
$[6] = t2;
} else {
x = $[6];
t2 = $[6];
}
const x = t2;
const c_7 = $[7] !== x;
let t;
let t3;
if (c_7) {
t = <Foo x={x}></Foo>;
t3 = <Foo x={x}></Foo>;
$[7] = x;
$[8] = t;
$[8] = t3;
} else {
t = $[8];
t3 = $[8];
}
const t = t3;
mutate(x);
return t;
}
@@ -18,25 +18,27 @@ function component(a) {
function component(a) {
const $ = React.unstable_useMemoCache(4);
const c_0 = $[0] !== a;
let z;
let t0;
if (c_0) {
z = { a: a };
t0 = { a: a };
$[0] = a;
$[1] = z;
$[1] = t0;
} else {
z = $[1];
t0 = $[1];
}
const z = t0;
const c_2 = $[2] !== z;
let x;
let t1;
if (c_2) {
x = () => {
t1 = () => {
z;
};
$[2] = z;
$[3] = x;
$[3] = t1;
} else {
x = $[3];
t1 = $[3];
}
const x = t1;
return x;
}
@@ -21,14 +21,15 @@ function component(a, b) {
function component(a, b) {
const $ = React.unstable_useMemoCache(5);
const c_0 = $[0] !== b;
let y;
let t0;
if (c_0) {
y = { b: b };
t0 = { b: b };
$[0] = b;
$[1] = y;
$[1] = t0;
} else {
y = $[1];
t0 = $[1];
}
const y = t0;
const c_2 = $[2] !== a;
const c_3 = $[3] !== y.b;
let x;
@@ -20,37 +20,40 @@ function component(a, b) {
function component(a, b) {
const $ = React.unstable_useMemoCache(7);
const c_0 = $[0] !== b;
let y;
let t0;
if (c_0) {
y = { b: b };
t0 = { b: b };
$[0] = b;
$[1] = y;
$[1] = t0;
} else {
y = $[1];
t0 = $[1];
}
const y = t0;
const c_2 = $[2] !== a;
let z;
let t1;
if (c_2) {
z = { a: a };
t1 = { a: a };
$[2] = a;
$[3] = z;
$[3] = t1;
} else {
z = $[3];
t1 = $[3];
}
const z = t1;
const c_4 = $[4] !== z.a;
const c_5 = $[5] !== y.b;
let x;
let t2;
if (c_4 || c_5) {
x = function () {
t2 = function () {
z.a = 2;
y.b;
};
$[4] = z.a;
$[5] = y.b;
$[6] = x;
$[6] = t2;
} else {
x = $[6];
t2 = $[6];
}
const x = t2;
return x;
}
@@ -26,14 +26,15 @@ function component(a, b) {
if (c_0 || c_1) {
const z = { a: a };
const c_3 = $[3] !== b;
let y;
let t0;
if (c_3) {
y = { b: b };
t0 = { b: b };
$[3] = b;
$[4] = y;
$[4] = t0;
} else {
y = $[4];
t0 = $[4];
}
const y = t0;
x = function () {
z.a = 2;
y.b;
@@ -18,25 +18,27 @@ function component(a) {
function component(a) {
const $ = React.unstable_useMemoCache(4);
const c_0 = $[0] !== a;
let z;
let t0;
if (c_0) {
z = { a: a };
t0 = { a: a };
$[0] = a;
$[1] = z;
$[1] = t0;
} else {
z = $[1];
t0 = $[1];
}
const z = t0;
const c_2 = $[2] !== z;
let x;
let t1;
if (c_2) {
x = function () {
t1 = function () {
z;
};
$[2] = z;
$[3] = x;
$[3] = t1;
} else {
x = $[3];
t1 = $[3];
}
const x = t1;
return x;
}
@@ -19,44 +19,45 @@ function component({ mutator }) {
## Code
```javascript
function component(t23) {
function component(t28) {
const $ = React.unstable_useMemoCache(7);
const t0 = t23;
const mutator = t0.mutator;
const mutator = t28.mutator;
const c_0 = $[0] !== mutator;
let poke;
let t0;
if (c_0) {
poke = () => {
t0 = () => {
mutator.poke();
};
$[0] = mutator;
$[1] = poke;
$[1] = t0;
} else {
poke = $[1];
t0 = $[1];
}
const c_2 = $[2] !== t0.mutator.user;
let hide;
const poke = t0;
const c_2 = $[2] !== mutator.user;
let t1;
if (c_2) {
hide = () => {
t1 = () => {
mutator.user.hide();
};
$[2] = t0.mutator.user;
$[3] = hide;
$[2] = mutator.user;
$[3] = t1;
} else {
hide = $[3];
t1 = $[3];
}
const hide = t1;
const c_4 = $[4] !== poke;
const c_5 = $[5] !== hide;
let t1;
let t2;
if (c_4 || c_5) {
t1 = <Foo poke={poke} hide={hide}></Foo>;
t2 = <Foo poke={poke} hide={hide}></Foo>;
$[4] = poke;
$[5] = hide;
$[6] = t1;
$[6] = t2;
} else {
t1 = $[6];
t2 = $[6];
}
return t1;
return t2;
}
```
@@ -21,25 +21,27 @@ function component(a) {
function component(a) {
const $ = React.unstable_useMemoCache(4);
const c_0 = $[0] !== a;
let z;
let t0;
if (c_0) {
z = { a: a };
t0 = { a: a };
$[0] = a;
$[1] = z;
$[1] = t0;
} else {
z = $[1];
t0 = $[1];
}
const z = t0;
const c_2 = $[2] !== z;
let x;
let t1;
if (c_2) {
x = function () {
t1 = function () {
z;
};
$[2] = z;
$[3] = x;
$[3] = t1;
} else {
x = $[3];
t1 = $[3];
}
const x = t1;
return x;
}
@@ -18,25 +18,27 @@ function component(a) {
function component(a) {
const $ = React.unstable_useMemoCache(4);
const c_0 = $[0] !== a;
let z;
let t0;
if (c_0) {
z = { a: a };
t0 = { a: a };
$[0] = a;
$[1] = z;
$[1] = t0;
} else {
z = $[1];
t0 = $[1];
}
const z = t0;
const c_2 = $[2] !== z.a;
let x;
let t1;
if (c_2) {
x = function () {
t1 = function () {
z.a;
};
$[2] = z.a;
$[3] = x;
$[3] = t1;
} else {
x = $[3];
t1 = $[3];
}
const x = t1;
return x;
}
@@ -27,25 +27,27 @@ function component(a) {
t0 = $[1];
}
const c_2 = $[2] !== t0;
let z;
let t1;
if (c_2) {
z = { a: t0 };
t1 = { a: t0 };
$[2] = t0;
$[3] = z;
$[3] = t1;
} else {
z = $[3];
t1 = $[3];
}
const z = t1;
const c_4 = $[4] !== z.a;
let x;
let t2;
if (c_4) {
x = function () {
t2 = function () {
z.a.a();
};
$[4] = z.a;
$[5] = x;
$[5] = t2;
} else {
x = $[5];
t2 = $[5];
}
const x = t2;
return x;
}
@@ -29,27 +29,29 @@ function component(a) {
t0 = $[1];
}
const c_2 = $[2] !== t0;
let z;
let t1;
if (c_2) {
z = { a: t0 };
t1 = { a: t0 };
$[2] = t0;
$[3] = z;
$[3] = t1;
} else {
z = $[3];
t1 = $[3];
}
const z = t1;
const c_4 = $[4] !== z.a.a;
let x;
let t2;
if (c_4) {
x = function () {
t2 = function () {
(function () {
z.a.a;
})();
};
$[4] = z.a.a;
$[5] = x;
$[5] = t2;
} else {
x = $[5];
t2 = $[5];
}
const x = t2;
return x;
}
@@ -27,25 +27,27 @@ function component(a) {
t0 = $[1];
}
const c_2 = $[2] !== t0;
let z;
let t1;
if (c_2) {
z = { a: t0 };
t1 = { a: t0 };
$[2] = t0;
$[3] = z;
$[3] = t1;
} else {
z = $[3];
t1 = $[3];
}
const z = t1;
const c_4 = $[4] !== z.a.a;
let x;
let t2;
if (c_4) {
x = function () {
t2 = function () {
z.a.a;
};
$[4] = z.a.a;
$[5] = x;
$[5] = t2;
} else {
x = $[5];
t2 = $[5];
}
const x = t2;
return x;
}
@@ -20,27 +20,29 @@ function component(a) {
function component(a) {
const $ = React.unstable_useMemoCache(4);
const c_0 = $[0] !== a;
let z;
let t0;
if (c_0) {
z = { a: a };
t0 = { a: a };
$[0] = a;
$[1] = z;
$[1] = t0;
} else {
z = $[1];
t0 = $[1];
}
const z = t0;
const c_2 = $[2] !== z;
let x;
let t1;
if (c_2) {
x = function () {
t1 = function () {
{
z;
}
};
$[2] = z;
$[3] = x;
$[3] = t1;
} else {
x = $[3];
t1 = $[3];
}
const x = t1;
return x;
}
@@ -20,27 +20,29 @@ function component(a) {
function component(a) {
const $ = React.unstable_useMemoCache(4);
const c_0 = $[0] !== a;
let z;
let t0;
if (c_0) {
z = { a: a };
t0 = { a: a };
$[0] = a;
$[1] = z;
$[1] = t0;
} else {
z = $[1];
t0 = $[1];
}
const z = t0;
const c_2 = $[2] !== z;
let x;
let t1;
if (c_2) {
x = function () {
t1 = function () {
(function () {
z;
})();
};
$[2] = z;
$[3] = x;
$[3] = t1;
} else {
x = $[3];
t1 = $[3];
}
const x = t1;
return x;
}
@@ -45,14 +45,15 @@ function Component(props) {
renderedItems = [];
const seen = new Set();
const c_3 = $[3] !== maxItems;
let max;
let t0;
if (c_3) {
max = Math.max(0, maxItems);
t0 = Math.max(0, maxItems);
$[3] = maxItems;
$[4] = max;
$[4] = t0;
} else {
max = $[4];
t0 = $[4];
}
const max = t0;
for (let i = 0; i < items.length; i = i + 1, i) {
const item = items.at(i);
if (item == null || seen.has(item)) {
@@ -74,31 +75,31 @@ function Component(props) {
const count = renderedItems.length;
const c_5 = $[5] !== count;
let t0;
if (c_5) {
t0 = <h1>{count} Items</h1>;
$[5] = count;
$[6] = t0;
} else {
t0 = $[6];
}
const c_7 = $[7] !== t0;
const c_8 = $[8] !== renderedItems;
let t1;
if (c_5) {
t1 = <h1>{count} Items</h1>;
$[5] = count;
$[6] = t1;
} else {
t1 = $[6];
}
const c_7 = $[7] !== t1;
const c_8 = $[8] !== renderedItems;
let t2;
if (c_7 || c_8) {
t1 = (
t2 = (
<div>
{t0}
{t1}
{renderedItems}
</div>
);
$[7] = t0;
$[7] = t1;
$[8] = renderedItems;
$[9] = t1;
$[9] = t2;
} else {
t1 = $[9];
t2 = $[9];
}
return t1;
return t2;
}
```
@@ -17,24 +17,25 @@ function component() {
const $ = React.unstable_useMemoCache(4);
const setX = useState(0)[1];
const c_0 = $[0] !== setX;
let handler;
if (c_0) {
handler = (v) => setX(v);
$[0] = setX;
$[1] = handler;
} else {
handler = $[1];
}
const c_2 = $[2] !== handler;
let t0;
if (c_2) {
t0 = <Foo handler={handler}></Foo>;
$[2] = handler;
$[3] = t0;
if (c_0) {
t0 = (v) => setX(v);
$[0] = setX;
$[1] = t0;
} else {
t0 = $[3];
t0 = $[1];
}
return t0;
const handler = t0;
const c_2 = $[2] !== handler;
let t1;
if (c_2) {
t1 = <Foo handler={handler}></Foo>;
$[2] = handler;
$[3] = t1;
} else {
t1 = $[3];
}
return t1;
}
```
@@ -15,30 +15,31 @@ function component() {
```javascript
function component() {
const $ = React.unstable_useMemoCache(5);
const t1 = useState(0);
const x = t1[0];
const setX = t1[1];
const t2 = useState(0);
const x = t2[0];
const setX = t2[1];
const c_0 = $[0] !== setX;
let handler;
let t0;
if (c_0) {
handler = (event) => setX(event.target.value);
t0 = (event) => setX(event.target.value);
$[0] = setX;
$[1] = handler;
$[1] = t0;
} else {
handler = $[1];
t0 = $[1];
}
const handler = t0;
const c_2 = $[2] !== handler;
const c_3 = $[3] !== x;
let t0;
let t1;
if (c_2 || c_3) {
t0 = <input onChange={handler} value={x}></input>;
t1 = <input onChange={handler} value={x}></input>;
$[2] = handler;
$[3] = x;
$[4] = t0;
$[4] = t1;
} else {
t0 = $[4];
t1 = $[4];
}
return t0;
return t1;
}
```
@@ -17,14 +17,15 @@ let someGlobal = {};
function component(a) {
const $ = React.unstable_useMemoCache(2);
const c_0 = $[0] !== a;
let x;
let t0;
if (c_0) {
x = { a: a, someGlobal: someGlobal };
t0 = { a: a, someGlobal: someGlobal };
$[0] = a;
$[1] = x;
$[1] = t0;
} else {
x = $[1];
t0 = $[1];
}
const x = t0;
return x;
}
@@ -32,9 +32,9 @@ function foo(props) {
for (
let x = 0;
x > props.min && x < props.max;
x = x$0 + (props.cond ? props.increment : 2), x
x = x + (props.cond ? props.increment : 2), x
) {
const x$0 = x * 2;
x = x * 2;
}
return y;
}
@@ -20,13 +20,14 @@ function foo(x) {}
```javascript
function Component() {
const $ = React.unstable_useMemoCache(1);
let a;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
a = [];
$[0] = a;
t0 = [];
$[0] = t0;
} else {
a = $[0];
t0 = $[0];
}
const a = t0;
const b = a;
useFreeze(a);
foo(b);
@@ -27,30 +27,31 @@ function foo() {}
function Component(props) {
const $ = React.unstable_useMemoCache(3);
let x;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
x = [];
$[0] = x;
t0 = [];
$[0] = t0;
} else {
x = $[0];
t0 = $[0];
}
const x = t0;
const y = useFreeze(x);
foo(y, x);
const c_1 = $[1] !== y;
let t0;
let t1;
if (c_1) {
t0 = (
t1 = (
<Component>
{x}
{y}
</Component>
);
$[1] = y;
$[2] = t0;
$[2] = t1;
} else {
t0 = $[2];
t1 = $[2];
}
return t0;
return t1;
}
```
@@ -20,13 +20,14 @@ function call(x) {}
```javascript
function Component() {
const $ = React.unstable_useMemoCache(1);
let a;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
a = [];
$[0] = a;
t0 = [];
$[0] = t0;
} else {
a = $[0];
t0 = $[0];
}
const a = t0;
useFreeze(a);
useFreeze(a);
call(a);
@@ -33,12 +33,14 @@ function Component(props) {
if (cond) {
a = x;
} else {
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
a = [];
$[0] = a;
t0 = [];
$[0] = t0;
} else {
a = $[0];
t0 = $[0];
}
a = t0;
}
useFreeze(a);
@@ -40,35 +40,37 @@ function Foo() {}
function Component(props) {
const $ = React.unstable_useMemoCache(7);
const c_0 = $[0] !== props.a;
let a;
let t0;
if (c_0) {
a = compute(props.a);
t0 = compute(props.a);
$[0] = props.a;
$[1] = a;
$[1] = t0;
} else {
a = $[1];
t0 = $[1];
}
const a = t0;
const c_2 = $[2] !== props.b;
let b;
let t1;
if (c_2) {
b = compute(props.b);
t1 = compute(props.b);
$[2] = props.b;
$[3] = b;
$[3] = t1;
} else {
b = $[3];
t1 = $[3];
}
const b = t1;
const c_4 = $[4] !== a;
const c_5 = $[5] !== b;
let t0;
let t2;
if (c_4 || c_5) {
t0 = <Foo a={a} b={b}></Foo>;
t2 = <Foo a={a} b={b}></Foo>;
$[4] = a;
$[5] = b;
$[6] = t0;
$[6] = t2;
} else {
t0 = $[6];
t2 = $[6];
}
return t0;
return t2;
}
function compute() {}
@@ -24,36 +24,39 @@ function foo(a, b, c) {
function foo(a, b, c) {
const $ = React.unstable_useMemoCache(8);
const c_0 = $[0] !== a;
let x;
let t0;
if (c_0) {
x = makeObject(a);
t0 = makeObject(a);
$[0] = a;
$[1] = x;
$[1] = t0;
} else {
x = $[1];
t0 = $[1];
}
const x = t0;
const c_2 = $[2] !== a;
let y;
let t1;
if (c_2) {
y = makeObject(a);
t1 = makeObject(a);
$[2] = a;
$[3] = y;
$[3] = t1;
} else {
y = $[3];
t1 = $[3];
}
const y = t1;
const c_4 = $[4] !== x;
const c_5 = $[5] !== y.method;
const c_6 = $[6] !== b;
let z;
let t2;
if (c_4 || c_5 || c_6) {
z = x[y.method](b);
t2 = x[y.method](b);
$[4] = x;
$[5] = y.method;
$[6] = b;
$[7] = z;
$[7] = t2;
} else {
z = $[7];
t2 = $[7];
}
const z = t2;
return z;
}
@@ -21,29 +21,31 @@ function foo(a, b, c) {
function foo(a, b, c) {
const $ = React.unstable_useMemoCache(6);
const c_0 = $[0] !== a;
let x;
let t0;
if (c_0) {
x = makeObject(a);
t0 = makeObject(a);
$[0] = a;
$[1] = x;
$[1] = t0;
} else {
x = $[1];
t0 = $[1];
}
const x = t0;
const method = x.method;
const c_2 = $[2] !== method;
const c_3 = $[3] !== x;
const c_4 = $[4] !== b;
let y;
let t1;
if (c_2 || c_3 || c_4) {
y = method.call(x, b);
t1 = method.call(x, b);
$[2] = method;
$[3] = x;
$[4] = b;
$[5] = y;
$[5] = t1;
} else {
y = $[5];
t1 = $[5];
}
const y = t1;
return y;
}
@@ -20,25 +20,27 @@ function foo(a, b, c) {
function foo(a, b, c) {
const $ = React.unstable_useMemoCache(5);
const c_0 = $[0] !== a;
let x;
let t0;
if (c_0) {
x = makeObject(a);
t0 = makeObject(a);
$[0] = a;
$[1] = x;
$[1] = t0;
} else {
x = $[1];
t0 = $[1];
}
const x = t0;
const c_2 = $[2] !== x;
const c_3 = $[3] !== b;
let y;
let t1;
if (c_2 || c_3) {
y = x.foo(b);
t1 = x.foo(b);
$[2] = x;
$[3] = b;
$[4] = y;
$[4] = t1;
} else {
y = $[4];
t1 = $[4];
}
const y = t1;
return y;
}
@@ -44,13 +44,14 @@ function Component(props) {
const $ = React.unstable_useMemoCache(1);
const a = {};
const b = {};
let c;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
c = {};
$[0] = c;
t0 = {};
$[0] = t0;
} else {
c = $[0];
t0 = $[0];
}
const c = t0;
const d = {};
while (true) {
mutate(a, b);
@@ -23,22 +23,26 @@ function foo(a, b, c, d) {
let x = undefined;
if (someVal) {
const c_0 = $[0] !== b;
let t0;
if (c_0) {
x = { b: b };
t0 = { b: b };
$[0] = b;
$[1] = x;
$[1] = t0;
} else {
x = $[1];
t0 = $[1];
}
x = t0;
} else {
const c_2 = $[2] !== c;
let t1;
if (c_2) {
x = { c: c };
t1 = { c: c };
$[2] = c;
$[3] = x;
$[3] = t1;
} else {
x = $[3];
t1 = $[3];
}
x = t1;
}
return x;
}
@@ -23,10 +23,11 @@ function foo(a, b, c, d) {
```javascript
function foo(a, b, c, d) {
const $ = React.unstable_useMemoCache(2);
someObj();
const c_0 = $[0] !== a;
let x;
if (c_0) {
x = someObj();
x = undefined;
if (a) {
const y = someObj();
const z = y;
@@ -21,10 +21,11 @@ function foo(a, b, c, d) {
```javascript
function foo(a, b, c, d) {
const $ = React.unstable_useMemoCache(2);
someObj();
const c_0 = $[0] !== a;
let x;
if (c_0) {
x = someObj();
x = undefined;
if (a) {
x = someObj();
} else {
@@ -29,11 +29,12 @@ function foo(a, b, c, d) {
```javascript
function foo(a, b, c, d) {
const $ = React.unstable_useMemoCache(3);
someObj();
const c_0 = $[0] !== a;
const c_1 = $[1] !== b;
let x;
if (c_0 || c_1) {
x = someObj();
x = undefined;
if (a) {
let z = undefined;
if (b) {
@@ -13,40 +13,42 @@ function component({ a, b }) {
## Code
```javascript
function component(t12) {
function component(t19) {
const $ = React.unstable_useMemoCache(7);
const a = t12.a;
const b = t12.b;
const a = t19.a;
const b = t19.b;
const c_0 = $[0] !== a;
let y;
let t0;
if (c_0) {
y = { a: a };
t0 = { a: a };
$[0] = a;
$[1] = y;
$[1] = t0;
} else {
y = $[1];
t0 = $[1];
}
const y = t0;
const c_2 = $[2] !== b;
let z;
let t1;
if (c_2) {
z = { b: b };
t1 = { b: b };
$[2] = b;
$[3] = z;
$[3] = t1;
} else {
z = $[3];
t1 = $[3];
}
const z = t1;
const c_4 = $[4] !== y;
const c_5 = $[5] !== z;
let t0;
let t2;
if (c_4 || c_5) {
t0 = { y: y, z: z };
t2 = { y: y, z: z };
$[4] = y;
$[5] = z;
$[6] = t0;
$[6] = t2;
} else {
t0 = $[6];
t2 = $[6];
}
return t0;
return t2;
}
```
@@ -34,54 +34,59 @@ function PrimitiveAsDep(props) {
const $ = React.unstable_useMemoCache(2);
const t0 = props.b + 1;
const c_0 = $[0] !== t0;
let y;
let t1;
if (c_0) {
y = foo(t0);
t1 = foo(t0);
$[0] = t0;
$[1] = y;
$[1] = t1;
} else {
y = $[1];
t1 = $[1];
}
const y = t1;
return y;
}
function PrimitiveAsDepNested(props) {
const $ = React.unstable_useMemoCache(8);
const $ = React.unstable_useMemoCache(9);
const c_0 = $[0] !== props.b;
const c_1 = $[1] !== props.a;
let x;
let y;
if (c_0 || c_1) {
x = {};
mutate(x);
const t0 = props.b + 1;
const c_3 = $[3] !== t0;
let y;
if (c_3) {
y = foo(t0);
$[3] = t0;
$[4] = y;
const c_4 = $[4] !== t0;
let t1;
if (c_4) {
t1 = foo(t0);
$[4] = t0;
$[5] = t1;
} else {
y = $[4];
t1 = $[5];
}
y = t1;
mutate(x, props.a);
$[0] = props.b;
$[1] = props.a;
$[2] = x;
$[3] = y;
} else {
x = $[2];
y = $[3];
}
const c_5 = $[5] !== x;
const c_6 = $[6] !== y;
let t1;
if (c_5 || c_6) {
t1 = [x, y];
$[5] = x;
$[6] = y;
$[7] = t1;
const c_6 = $[6] !== x;
const c_7 = $[7] !== y;
let t2;
if (c_6 || c_7) {
t2 = [x, y];
$[6] = x;
$[7] = y;
$[8] = t2;
} else {
t1 = $[7];
t2 = $[8];
}
return t1;
return t2;
}
```
@@ -19,26 +19,28 @@ function component(a, b) {
const $ = React.unstable_useMemoCache(5);
const c_0 = $[0] !== a;
const c_1 = $[1] !== b;
let z;
let t0;
if (c_0 || c_1) {
z = { a: a, b: b };
t0 = { a: a, b: b };
$[0] = a;
$[1] = b;
$[2] = z;
$[2] = t0;
} else {
z = $[2];
t0 = $[2];
}
const z = t0;
const c_3 = $[3] !== z;
let x;
let t1;
if (c_3) {
x = function () {
t1 = function () {
z;
};
$[3] = z;
$[4] = x;
$[4] = t1;
} else {
x = $[4];
t1 = $[4];
}
const x = t1;
return x;
}
@@ -36,27 +36,29 @@ function Component(props) {
}
const y = x;
if (props.p1) {
let t0;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
x = [];
$[2] = x;
t0 = [];
$[2] = t0;
} else {
x = $[2];
t0 = $[2];
}
x = t0;
}
y.push(props.p2);
const c_3 = $[3] !== x;
const c_4 = $[4] !== y;
let t0;
let t1;
if (c_3 || c_4) {
t0 = <Component x={x} y={y}></Component>;
t1 = <Component x={x} y={y}></Component>;
$[3] = x;
$[4] = y;
$[5] = t0;
$[5] = t1;
} else {
t0 = $[5];
t1 = $[5];
}
return t0;
return t1;
}
```
@@ -48,14 +48,15 @@ function foo(a, b, c) {
x = $[1];
}
const c_2 = $[2] !== x;
let y;
let t0;
if (c_2) {
y = <div>{x}</div>;
t0 = <div>{x}</div>;
$[2] = x;
$[3] = y;
$[3] = t0;
} else {
y = $[3];
t0 = $[3];
}
const y = t0;
bb3: switch (b) {
case 0: {
const c_4 = $[4] !== b;
@@ -83,9 +84,9 @@ function foo(a, b, c) {
}
const c_8 = $[8] !== y;
const c_9 = $[9] !== x;
let t0;
let t1;
if (c_8 || c_9) {
t0 = (
t1 = (
<div>
{y}
{x}
@@ -93,11 +94,11 @@ function foo(a, b, c) {
);
$[8] = y;
$[9] = x;
$[10] = t0;
$[10] = t1;
} else {
t0 = $[10];
t1 = $[10];
}
return t0;
return t1;
}
```
@@ -30,12 +30,14 @@ function Component(props) {
x = [];
x.push(props.p0);
y = x;
let t0;
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
x = [];
$[4] = x;
t0 = [];
$[4] = t0;
} else {
x = $[4];
t0 = $[4];
}
x = t0;
y.push(props.p1);
$[0] = props.p0;
@@ -48,16 +50,16 @@ function Component(props) {
}
const c_5 = $[5] !== x;
const c_6 = $[6] !== y;
let t0;
let t1;
if (c_5 || c_6) {
t0 = <Component x={x} y={y}></Component>;
t1 = <Component x={x} y={y}></Component>;
$[5] = x;
$[6] = y;
$[7] = t0;
$[7] = t1;
} else {
t0 = $[7];
t1 = $[7];
}
return t0;
return t1;
}
```
@@ -60,14 +60,15 @@ function TestReactiveDepsInCondScope(props) {
x = {};
if (foo) {
const c_2 = $[2] !== props.a.b;
let tmp;
let t0;
if (c_2) {
tmp = bar(props.a.b);
t0 = bar(props.a.b);
$[2] = props.a.b;
$[3] = tmp;
$[3] = t0;
} else {
tmp = $[3];
t0 = $[3];
}
const tmp = t0;
x.a = tmp;
}
$[0] = props;
@@ -89,9 +89,9 @@ function foo(props) {
}
const c_10 = $[10] !== x;
const c_11 = $[11] !== y;
let content;
let t1;
if (c_10 || c_11) {
content = (
t1 = (
<div>
{x}
{y}
@@ -99,15 +99,16 @@ function foo(props) {
);
$[10] = x;
$[11] = y;
$[12] = content;
$[12] = t1;
} else {
content = $[12];
t1 = $[12];
}
const content = t1;
const c_13 = $[13] !== header;
const c_14 = $[14] !== content;
let t1;
let t2;
if (c_13 || c_14) {
t1 = (
t2 = (
<>
{header}
{content}
@@ -115,11 +116,11 @@ function foo(props) {
);
$[13] = header;
$[14] = content;
$[15] = t1;
$[15] = t2;
} else {
t1 = $[15];
t2 = $[15];
}
return t1;
return t2;
}
```
@@ -58,14 +58,15 @@ function foo(props) {
x = $[1];
}
const c_2 = $[2] !== x;
let header;
let t0;
if (c_2) {
header = <div>{x}</div>;
t0 = <div>{x}</div>;
$[2] = x;
$[3] = header;
$[3] = t0;
} else {
header = $[3];
t0 = $[3];
}
const header = t0;
const c_4 = $[4] !== x;
const c_5 = $[5] !== props.b;
const c_6 = $[6] !== props.c;
@@ -86,9 +87,9 @@ function foo(props) {
}
const c_9 = $[9] !== x;
const c_10 = $[10] !== y;
let content;
let t1;
if (c_9 || c_10) {
content = (
t1 = (
<div>
{x}
{y}
@@ -96,15 +97,16 @@ function foo(props) {
);
$[9] = x;
$[10] = y;
$[11] = content;
$[11] = t1;
} else {
content = $[11];
t1 = $[11];
}
const content = t1;
const c_12 = $[12] !== header;
const c_13 = $[13] !== content;
let t0;
let t2;
if (c_12 || c_13) {
t0 = (
t2 = (
<>
{header}
{content}
@@ -112,11 +114,11 @@ function foo(props) {
);
$[12] = header;
$[13] = content;
$[14] = t0;
$[14] = t2;
} else {
t0 = $[14];
t2 = $[14];
}
return t0;
return t2;
}
```
@@ -16,15 +16,16 @@ function component() {
```javascript
function component() {
const $ = React.unstable_useMemoCache(1);
let x;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
x = function (a) {
t0 = function (a) {
a.foo();
};
$[0] = x;
$[0] = t0;
} else {
x = $[0];
t0 = $[0];
}
const x = t0;
return x;
}
@@ -15,14 +15,15 @@ function foo(a) {
function foo(a) {
const $ = React.unstable_useMemoCache(2);
const c_0 = $[0] !== a.b;
let x;
let t0;
if (c_0) {
x = [a.b];
t0 = [a.b];
$[0] = a.b;
$[1] = x;
$[1] = t0;
} else {
x = $[1];
t0 = $[1];
}
const x = t0;
return x;
}
@@ -16,13 +16,14 @@ function Component(props) {
```javascript
function Component(props) {
const $ = React.unstable_useMemoCache(1);
let x;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
x = [1, 2];
$[0] = x;
t0 = [1, 2];
$[0] = t0;
} else {
x = $[0];
t0 = $[0];
}
const x = t0;
return x;
}
@@ -20,15 +20,16 @@ function Foo() {}
function Component(props) {
const $ = React.unstable_useMemoCache(1);
let c;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const a = [];
const b = {};
c = new Foo(a, b);
$[0] = c;
t0 = new Foo(a, b);
$[0] = t0;
} else {
c = $[0];
t0 = $[0];
}
const c = t0;
return c;
}
@@ -19,13 +19,14 @@ function foo(a, b) {
```javascript
function foo(a, b) {
const $ = React.unstable_useMemoCache(1);
let x;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
x = [];
$[0] = x;
t0 = [];
$[0] = t0;
} else {
x = $[0];
t0 = $[0];
}
let x = t0;
if (a) {
x = 1;
}
@@ -26,13 +26,14 @@ function foo() {
const x = 1;
const y = 3;
let t;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t = { x: x, y: y };
$[0] = t;
t0 = { x: x, y: y };
$[0] = t0;
} else {
t = $[0];
t0 = $[0];
}
const t = t0;
return t;
}
@@ -16,13 +16,14 @@ function Component(props) {
```javascript
function Component(props) {
const $ = React.unstable_useMemoCache(1);
let x;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
x = { a: 1, b: 2 };
$[0] = x;
t0 = { a: 1, b: 2 };
$[0] = t0;
} else {
x = $[0];
t0 = $[0];
}
const x = t0;
return x;
}
@@ -26,22 +26,24 @@ function foo(a) {
if (c_0) {
x = {};
if (a) {
let y;
let t0;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
y = {};
$[2] = y;
t0 = {};
$[2] = t0;
} else {
y = $[2];
t0 = $[2];
}
const y = t0;
x.y = y;
} else {
let z;
let t1;
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
z = {};
$[3] = z;
t1 = {};
$[3] = t1;
} else {
z = $[3];
t1 = $[3];
}
const z = t1;
x.z = z;
}
$[0] = a;
@@ -32,13 +32,14 @@ function foo(a) {
mutate(y);
} else {
let z;
let t0;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
z = {};
$[2] = z;
t0 = {};
$[2] = t0;
} else {
z = $[2];
t0 = $[2];
}
const z = t0;
x.z = z;
}
$[0] = a;
@@ -16,13 +16,14 @@ function foo() {
```javascript
function foo() {
const $ = React.unstable_useMemoCache(2);
let x;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
x = [];
$[0] = x;
t0 = [];
$[0] = t0;
} else {
x = $[0];
t0 = $[0];
}
const x = t0;
let y;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
y = {};
@@ -45,12 +45,14 @@ function Component(props) {
}
case true: {
x.push(props.p2);
let t0;
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
y = [];
$[3] = y;
t0 = [];
$[3] = t0;
} else {
y = $[3];
t0 = $[3];
}
y = t0;
break bb1;
}
default: {
@@ -68,27 +70,28 @@ function Component(props) {
y = $[2];
}
const c_4 = $[4] !== x;
let child;
let t1;
if (c_4) {
child = <Component data={x}></Component>;
t1 = <Component data={x}></Component>;
$[4] = x;
$[5] = child;
$[5] = t1;
} else {
child = $[5];
t1 = $[5];
}
const child = t1;
y.push(props.p4);
const c_6 = $[6] !== y;
const c_7 = $[7] !== child;
let t0;
let t2;
if (c_6 || c_7) {
t0 = <Component data={y}>{child}</Component>;
t2 = <Component data={y}>{child}</Component>;
$[6] = y;
$[7] = child;
$[8] = t0;
$[8] = t2;
} else {
t0 = $[8];
t2 = $[8];
}
return t0;
return t2;
}
```
@@ -51,27 +51,28 @@ function Component(props) {
y = $[2];
}
const c_3 = $[3] !== x;
let child;
let t0;
if (c_3) {
child = <Component data={x}></Component>;
t0 = <Component data={x}></Component>;
$[3] = x;
$[4] = child;
$[4] = t0;
} else {
child = $[4];
t0 = $[4];
}
const child = t0;
y.push(props.p4);
const c_5 = $[5] !== y;
const c_6 = $[6] !== child;
let t0;
let t1;
if (c_5 || c_6) {
t0 = <Component data={y}>{child}</Component>;
t1 = <Component data={y}>{child}</Component>;
$[5] = y;
$[6] = child;
$[7] = t0;
$[7] = t1;
} else {
t0 = $[7];
t1 = $[7];
}
return t0;
return t1;
}
```
@@ -26,9 +26,9 @@ function component() {
```javascript
function component() {
const $ = React.unstable_useMemoCache(1);
let t;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t = graphql`
t0 = graphql`
fragment List_viewer on Viewer
@argumentDefinitions(
count: {
@@ -40,10 +40,11 @@ function component() {
)
`;
$[0] = t;
$[0] = t0;
} else {
t = $[0];
t0 = $[0];
}
const t = t0;
return t;
}
@@ -20,40 +20,42 @@ function Component(props) {
```javascript
function Component(props) {
const $ = React.unstable_useMemoCache(4);
let start;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
start = performance.now();
$[0] = start;
} else {
start = $[0];
}
let now;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
now = Date.now();
$[1] = now;
} else {
now = $[1];
}
let t0;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = performance.now();
$[2] = t0;
$[0] = t0;
} else {
t0 = $[2];
t0 = $[0];
}
const time = t0 - start;
const start = t0;
let t1;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
t1 = Date.now();
$[1] = t1;
} else {
t1 = $[1];
}
const now = t1;
let t2;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
t2 = performance.now();
$[2] = t2;
} else {
t2 = $[2];
}
const time = t2 - start;
let t3;
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
t1 = (
t3 = (
<div>
rendering took {time} at {now}
</div>
);
$[3] = t1;
$[3] = t3;
} else {
t1 = $[3];
t3 = $[3];
}
return t1;
return t3;
}
```
@@ -17,20 +17,22 @@ function component() {
```javascript
function component() {
const $ = React.unstable_useMemoCache(2);
let a;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
a = some();
$[0] = a;
t0 = some();
$[0] = t0;
} else {
a = $[0];
t0 = $[0];
}
let b;
const a = t0;
let t1;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
b = someOther();
$[1] = b;
t1 = someOther();
$[1] = t1;
} else {
b = $[1];
t1 = $[1];
}
const b = t1;
if (a > b) {
}
}
@@ -15,13 +15,14 @@ function component() {
```javascript
function component() {
const $ = React.unstable_useMemoCache(1);
let x;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
x = { t: 1 };
$[0] = x;
t0 = { t: 1 };
$[0] = t0;
} else {
x = $[0];
t0 = $[0];
}
const x = t0;
const p = x.t;
return p;
}
@@ -35,13 +35,14 @@ function component() {
} else {
t1 = $[1];
}
let x;
let t2;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
x = { u: t0, v: t1 };
$[2] = x;
t2 = { u: t0, v: t1 };
$[2] = t2;
} else {
x = $[2];
t2 = $[2];
}
const x = t2;
const u = x.u;
const v = x.v;
if (u > v) {
@@ -20,13 +20,14 @@ function component() {
let x;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
x = {};
let q;
let t0;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
q = {};
$[1] = q;
t0 = {};
$[1] = t0;
} else {
q = $[1];
t0 = $[1];
}
const q = t0;
x.t = q;
$[0] = x;
} else {
@@ -24,20 +24,22 @@ function component() {
```javascript
function component() {
const $ = React.unstable_useMemoCache(3);
let p;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
p = makePrimitive();
$[0] = p;
t0 = makePrimitive();
$[0] = t0;
} else {
p = $[0];
t0 = $[0];
}
let o;
const p = t0;
let t1;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
o = {};
$[1] = o;
t1 = {};
$[1] = t1;
} else {
o = $[1];
t1 = $[1];
}
const o = t1;
let x;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
x = {};
@@ -20,29 +20,32 @@ function component() {
```javascript
function component() {
const $ = React.unstable_useMemoCache(3);
let x;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
x = foo();
$[0] = x;
t0 = foo();
$[0] = t0;
} else {
x = $[0];
t0 = $[0];
}
let y;
const x = t0;
let t1;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
y = foo();
$[1] = y;
t1 = foo();
$[1] = t1;
} else {
y = $[1];
t1 = $[1];
}
const y = t1;
if (x > y) {
}
let z_0;
let t2;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
z_0 = foo();
$[2] = z_0;
t2 = foo();
$[2] = t2;
} else {
z_0 = $[2];
t2 = $[2];
}
const z_0 = t2;
return z_0;
}
@@ -22,14 +22,15 @@ function component(a) {
function component(a) {
const $ = React.unstable_useMemoCache(10);
const c_0 = $[0] !== a;
let t;
let t0;
if (c_0) {
t = { t: a };
t0 = { t: a };
$[0] = a;
$[1] = t;
$[1] = t0;
} else {
t = $[1];
t0 = $[1];
}
const t = t0;
const z = +t.t;
const q = -t.t;
const p = void t.t;
@@ -44,9 +45,9 @@ function component(a) {
const c_6 = $[6] !== m;
const c_7 = $[7] !== e;
const c_8 = $[8] !== f;
let t0;
let t1;
if (c_2 || c_3 || c_4 || c_5 || c_6 || c_7 || c_8) {
t0 = { z: z, p: p, q: q, n: n, m: m, e: e, f: f };
t1 = { z: z, p: p, q: q, n: n, m: m, e: e, f: f };
$[2] = z;
$[3] = p;
$[4] = q;
@@ -54,11 +55,11 @@ function component(a) {
$[6] = m;
$[7] = e;
$[8] = f;
$[9] = t0;
$[9] = t1;
} else {
t0 = $[9];
t1 = $[9];
}
return t0;
return t1;
}
```
@@ -15,24 +15,25 @@ function component(a) {
function component(a) {
const $ = React.unstable_useMemoCache(4);
const c_0 = $[0] !== a;
let x;
if (c_0) {
x = (() => [a])();
$[0] = a;
$[1] = x;
} else {
x = $[1];
}
const c_2 = $[2] !== x;
let t0;
if (c_2) {
t0 = <Foo x={x}></Foo>;
$[2] = x;
$[3] = t0;
if (c_0) {
t0 = (() => [a])();
$[0] = a;
$[1] = t0;
} else {
t0 = $[3];
t0 = $[1];
}
return t0;
const x = t0;
const c_2 = $[2] !== x;
let t1;
if (c_2) {
t1 = <Foo x={x}></Foo>;
$[2] = x;
$[3] = t1;
} else {
t1 = $[3];
}
return t1;
}
```
+53 -18
View File
@@ -83,13 +83,14 @@
"@jridgewell/gen-mapping" "^0.3.2"
jsesc "^2.5.1"
"@babel/generator@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a"
integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==
"@babel/generator@^7.21.1":
version "7.21.1"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.1.tgz#951cc626057bc0af2c35cd23e9c64d384dea83dd"
integrity sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==
dependencies:
"@babel/types" "^7.20.7"
"@babel/types" "^7.21.0"
"@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
"@babel/helper-annotate-as-pure@^7.18.6":
@@ -135,6 +136,14 @@
"@babel/template" "^7.18.10"
"@babel/types" "^7.19.0"
"@babel/helper-function-name@^7.21.0":
version "7.21.0"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4"
integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==
dependencies:
"@babel/template" "^7.20.7"
"@babel/types" "^7.21.0"
"@babel/helper-hoist-variables@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
@@ -260,10 +269,10 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a"
integrity sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==
"@babel/parser@^7.20.13":
version "7.20.13"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.13.tgz#ddf1eb5a813588d2fb1692b70c6fce75b945c088"
integrity sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==
"@babel/parser@^7.20.7", "@babel/parser@^7.21.2":
version "7.21.2"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.2.tgz#dacafadfc6d7654c3051a66d6fe55b6cb2f2a0b3"
integrity sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==
"@babel/plugin-syntax-async-generators@^7.8.4":
version "7.8.4"
@@ -459,6 +468,15 @@
"@babel/parser" "^7.18.10"
"@babel/types" "^7.18.10"
"@babel/template@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==
dependencies:
"@babel/code-frame" "^7.18.6"
"@babel/parser" "^7.20.7"
"@babel/types" "^7.20.7"
"@babel/template@^7.3.3":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31"
@@ -484,18 +502,18 @@
lodash "^4.17.10"
"@babel/traverse@^7.19.1":
version "7.20.13"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473"
integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==
version "7.21.2"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.2.tgz#ac7e1f27658750892e815e60ae90f382a46d8e75"
integrity sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==
dependencies:
"@babel/code-frame" "^7.18.6"
"@babel/generator" "^7.20.7"
"@babel/generator" "^7.21.1"
"@babel/helper-environment-visitor" "^7.18.9"
"@babel/helper-function-name" "^7.19.0"
"@babel/helper-function-name" "^7.21.0"
"@babel/helper-hoist-variables" "^7.18.6"
"@babel/helper-split-export-declaration" "^7.18.6"
"@babel/parser" "^7.20.13"
"@babel/types" "^7.20.7"
"@babel/parser" "^7.21.2"
"@babel/types" "^7.21.2"
debug "^4.1.0"
globals "^11.1.0"
@@ -543,6 +561,15 @@
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@babel/types@^7.21.0", "@babel/types@^7.21.2":
version "7.21.2"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.2.tgz#92246f6e00f91755893c2876ad653db70c8310d1"
integrity sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==
dependencies:
"@babel/helper-string-parser" "^7.19.4"
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -831,7 +858,7 @@
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/resolve-uri@^3.0.3":
"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
@@ -841,7 +868,7 @@
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/sourcemap-codec@^1.4.10":
"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.14"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
@@ -862,6 +889,14 @@
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping@^0.3.17":
version "0.3.17"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==
dependencies:
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"