mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
New destructuring representation modeled on StoreLocal
Changes to explicitly model destructuring (array and object patterns), expanding
support to include rest elements and preserving destructuring through the
output. The new "Destructure" instruction is similar to "StoreLocal" but has a
pattern instead of a place. For now each level of nested array/object patterns
creates a separate destructure instruction, which ensures we have a temporary
Place to talk about the intermediate array/object and its type/effects etc.
Example:
```
// INPUT
const [x, {y}, ...z] = a; // yay rest elements work now!
// HIR
[1] <unknown> $2 = LoadLocal a$1
[2] <unknown> $6 = Destructure Const [ <unknown> x$3, <unknown> $4, ...<unknown>
z$5 ] = <unknown> $2
[3] <unknown> $8 = Destructure Const { y: <unknown> y$8 } = <unknown> $4
// OUTPUT
const [x, t0, ...z] = a;
const {y} = t0;
```
Note that we can still collapse to a single destructure statement during
codegen, independently of whether we have separate instructions internally. For
now i'm going w the simple approach of emitting multiple statements in codegen
(the code will very likely get further rewritten by downstream babel passes
anyway).
Also, I don't love the "if StoreLocal/Destructure else ..." pattern that the
StoreLocal created and that this PR entrenches. As discussed w @gsathya offline,
the long-term direction will be to add a separate visitor, roughly
`eachLValue()` and `eachOperand()` so that we can treat all instructions the
same. Existing Instruction.lvalue will go away and become a property of the
other types of instructions.
This commit is contained in:
@@ -14,6 +14,7 @@ import { Err, Ok, Result } from "../Utils/Result";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
import { Environment, EnvironmentOptions } from "./Environment";
|
||||
import {
|
||||
ArrayPattern,
|
||||
BlockId,
|
||||
BranchTerminal,
|
||||
Case,
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
InstructionValue,
|
||||
JsxAttribute,
|
||||
makeInstructionId,
|
||||
ObjectPattern,
|
||||
Place,
|
||||
ReturnTerminal,
|
||||
SourceLocation,
|
||||
@@ -1967,6 +1969,11 @@ function lowerAssignment(
|
||||
return { kind: "LoadLocal", place, loc: temporary.loc };
|
||||
}
|
||||
case "MemberExpression": {
|
||||
// This can only occur because of a coding error, parsers enforce this condition
|
||||
invariant(
|
||||
kind === InstructionKind.Reassign,
|
||||
"MemberExpression may only appear in an assignment expression"
|
||||
);
|
||||
const lvalue = lvaluePath as NodePath<t.MemberExpression>;
|
||||
const property = lvalue.get("property");
|
||||
const object = lowerExpressionToTemporary(builder, lvalue.get("object"));
|
||||
@@ -2009,115 +2016,156 @@ function lowerAssignment(
|
||||
case "ArrayPattern": {
|
||||
const lvalue = lvaluePath as NodePath<t.ArrayPattern>;
|
||||
const elements = lvalue.get("elements");
|
||||
let hasError = false;
|
||||
const items: ArrayPattern["items"] = [];
|
||||
const followups: Array<{ place: Place; path: NodePath<t.LVal> }> = [];
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const element = elements[i];
|
||||
if (element.node == null) {
|
||||
continue;
|
||||
}
|
||||
if (element.node.type === "RestElement") {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${element.type} in ArrayPattern`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
nodePath: element,
|
||||
if (element.isRestElement()) {
|
||||
const argument = element.get("argument");
|
||||
if (!argument.isIdentifier()) {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${argument.node.type} rest element in ArrayPattern`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
nodePath: element,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const identifier = lowerIdentifier(builder, argument);
|
||||
items.push({
|
||||
kind: "Spread",
|
||||
place: identifier,
|
||||
});
|
||||
hasError = true;
|
||||
continue;
|
||||
} else if (element.isIdentifier()) {
|
||||
const identifier = lowerIdentifier(builder, element);
|
||||
items.push(identifier);
|
||||
} else {
|
||||
const temp = buildTemporaryPlace(
|
||||
builder,
|
||||
element.node.loc ?? GeneratedSource
|
||||
);
|
||||
items.push({ ...temp });
|
||||
followups.push({ place: temp, path: element as NodePath<t.LVal> }); // TODO remove type cast
|
||||
}
|
||||
const property = buildTemporaryPlace(
|
||||
builder,
|
||||
element.node.loc ?? GeneratedSource
|
||||
);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...property },
|
||||
value: {
|
||||
kind: "Primitive",
|
||||
value: i,
|
||||
loc: element.node.loc ?? GeneratedSource,
|
||||
},
|
||||
loc: element.node.loc ?? GeneratedSource,
|
||||
});
|
||||
const propertyPlace = buildTemporaryPlace(builder, property.loc);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...propertyPlace },
|
||||
value: {
|
||||
kind: "ComputedLoad",
|
||||
loc,
|
||||
object: { ...value },
|
||||
property,
|
||||
},
|
||||
loc,
|
||||
});
|
||||
lowerAssignment(
|
||||
builder,
|
||||
loc,
|
||||
kind,
|
||||
element as NodePath<t.LVal>,
|
||||
propertyPlace
|
||||
);
|
||||
}
|
||||
return hasError
|
||||
? { kind: "UnsupportedNode", node: lvalueNode, loc }
|
||||
: { kind: "LoadLocal", place: value, loc: value.loc };
|
||||
const temp = buildTemporaryPlace(builder, loc);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...temp },
|
||||
value: {
|
||||
kind: "Destructure",
|
||||
lvalue: {
|
||||
kind,
|
||||
pattern: {
|
||||
kind: "ArrayPattern",
|
||||
items,
|
||||
},
|
||||
},
|
||||
value,
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
});
|
||||
for (const { place, path } of followups) {
|
||||
lowerAssignment(builder, path.node.loc ?? loc, kind, path, place);
|
||||
}
|
||||
return { kind: "LoadLocal", place: value, loc: value.loc };
|
||||
}
|
||||
case "ObjectPattern": {
|
||||
const lvalue = lvaluePath as NodePath<t.ObjectPattern>;
|
||||
const properties = lvalue.get("properties");
|
||||
let hasError = false;
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
const property = properties[i];
|
||||
if (!property.isObjectProperty()) {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in ObjectPattern`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
nodePath: property,
|
||||
const propertiesPaths = lvalue.get("properties");
|
||||
const properties: ObjectPattern["properties"] = [];
|
||||
const followups: Array<{ place: Place; path: NodePath<t.LVal> }> = [];
|
||||
for (let i = 0; i < propertiesPaths.length; i++) {
|
||||
const property = propertiesPaths[i];
|
||||
if (property.isRestElement()) {
|
||||
const argument = property.get("argument");
|
||||
if (!argument.isIdentifier()) {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${argument.node.type} rest element in ArrayPattern`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
nodePath: argument,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const identifier = lowerIdentifier(builder, argument);
|
||||
properties.push({
|
||||
kind: "Spread",
|
||||
place: identifier,
|
||||
});
|
||||
hasError = true;
|
||||
continue;
|
||||
} else {
|
||||
// TODO: this should always be true given the if/else
|
||||
if (!property.isObjectProperty()) {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in ObjectPattern`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
nodePath: property,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const key = property.get("key");
|
||||
if (!key.isIdentifier()) {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${key.type} keys in ObjectPattern`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
nodePath: key,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const element = property.get("value");
|
||||
if (!element.isLVal()) {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Expected object property value to be an LVal, got: ${element.type}`,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
nodePath: element,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (element.isIdentifier()) {
|
||||
const identifier = lowerIdentifier(builder, element);
|
||||
properties.push({
|
||||
kind: "ObjectProperty",
|
||||
name: key.node.name,
|
||||
place: identifier,
|
||||
});
|
||||
} else {
|
||||
const temp = buildTemporaryPlace(
|
||||
builder,
|
||||
element.node.loc ?? GeneratedSource
|
||||
);
|
||||
properties.push({
|
||||
kind: "ObjectProperty",
|
||||
name: key.node.name,
|
||||
place: { ...temp },
|
||||
});
|
||||
followups.push({ place: temp, path: element as NodePath<t.LVal> }); // TODO remove type cast
|
||||
}
|
||||
}
|
||||
const key = property.get("key");
|
||||
if (!key.isIdentifier()) {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${key.type} keys in ObjectPattern`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
nodePath: key,
|
||||
});
|
||||
hasError = true;
|
||||
continue;
|
||||
}
|
||||
const element = property.get("value");
|
||||
if (!element.isLVal()) {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerAssignment) Expected object property value to be an LVal, got: ${element.type}`,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
nodePath: element,
|
||||
});
|
||||
hasError = true;
|
||||
continue;
|
||||
}
|
||||
const propertyPlace = buildTemporaryPlace(
|
||||
builder,
|
||||
property.node.loc ?? GeneratedSource
|
||||
);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...propertyPlace },
|
||||
value: {
|
||||
kind: "PropertyLoad",
|
||||
loc,
|
||||
object: { ...value },
|
||||
property: key.node.name,
|
||||
optional: false, // Key of ObjectPattern (evaluation of LVal) cannot be optional.
|
||||
},
|
||||
loc,
|
||||
});
|
||||
lowerAssignment(builder, loc, kind, element, propertyPlace);
|
||||
}
|
||||
return hasError
|
||||
? { kind: "UnsupportedNode", node: lvalueNode, loc }
|
||||
: { kind: "LoadLocal", place: value, loc: value.loc };
|
||||
const temp = buildTemporaryPlace(builder, loc);
|
||||
builder.push({
|
||||
id: makeInstructionId(0),
|
||||
lvalue: { ...temp },
|
||||
value: {
|
||||
kind: "Destructure",
|
||||
lvalue: {
|
||||
kind,
|
||||
pattern: {
|
||||
kind: "ObjectPattern",
|
||||
properties,
|
||||
},
|
||||
},
|
||||
value,
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
});
|
||||
for (const { place, path } of followups) {
|
||||
lowerAssignment(builder, path.node.loc ?? loc, kind, path, place);
|
||||
}
|
||||
return { kind: "LoadLocal", place: value, loc: value.loc };
|
||||
}
|
||||
default: {
|
||||
builder.errors.push({
|
||||
|
||||
@@ -375,6 +375,34 @@ export type LValue = {
|
||||
kind: InstructionKind;
|
||||
};
|
||||
|
||||
export type LValuePattern = {
|
||||
pattern: Pattern;
|
||||
kind: InstructionKind;
|
||||
};
|
||||
|
||||
export type Pattern = ArrayPattern | ObjectPattern;
|
||||
|
||||
export type SpreadPattern = {
|
||||
kind: "Spread";
|
||||
place: Place;
|
||||
};
|
||||
|
||||
export type ArrayPattern = {
|
||||
kind: "ArrayPattern";
|
||||
items: Array<Place | SpreadPattern>;
|
||||
};
|
||||
|
||||
export type ObjectPattern = {
|
||||
kind: "ObjectPattern";
|
||||
properties: Array<ObjectProperty | SpreadPattern>;
|
||||
};
|
||||
|
||||
export type ObjectProperty = {
|
||||
kind: "ObjectProperty";
|
||||
name: string; // TODO: make a Place
|
||||
place: Place;
|
||||
};
|
||||
|
||||
export enum InstructionKind {
|
||||
/**
|
||||
* const declaration
|
||||
@@ -424,6 +452,12 @@ export type InstructionValue =
|
||||
value: Place;
|
||||
loc: SourceLocation;
|
||||
}
|
||||
| {
|
||||
kind: "Destructure";
|
||||
lvalue: LValuePattern;
|
||||
value: Place;
|
||||
loc: SourceLocation;
|
||||
}
|
||||
| {
|
||||
kind: "Primitive";
|
||||
value: number | boolean | string | null | undefined;
|
||||
|
||||
@@ -19,12 +19,14 @@ import {
|
||||
InstructionValue,
|
||||
LValue,
|
||||
MutableRange,
|
||||
Pattern,
|
||||
Phi,
|
||||
Place,
|
||||
ReactiveInstruction,
|
||||
ReactiveScope,
|
||||
ReactiveValue,
|
||||
SourceLocation,
|
||||
SpreadPattern,
|
||||
Terminal,
|
||||
Type,
|
||||
} from "./HIR";
|
||||
@@ -321,6 +323,12 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
|
||||
)} = ${printPlace(instrValue.value)}`;
|
||||
break;
|
||||
}
|
||||
case "Destructure": {
|
||||
value = `Destructure ${instrValue.lvalue.kind} ${printPattern(
|
||||
instrValue.lvalue.pattern
|
||||
)} = ${printPlace(instrValue.value)}`;
|
||||
break;
|
||||
}
|
||||
case "PropertyLoad": {
|
||||
value = `PropertyLoad ${printPlace(instrValue.object)}.${
|
||||
instrValue.property
|
||||
@@ -458,6 +466,49 @@ export function printLValue(lval: LValue): string {
|
||||
}
|
||||
}
|
||||
|
||||
export function printPattern(pattern: Pattern | Place | SpreadPattern): string {
|
||||
switch (pattern.kind) {
|
||||
case "ArrayPattern": {
|
||||
return (
|
||||
"[ " + pattern.items.map((item) => printPattern(item)).join(", ") + " ]"
|
||||
);
|
||||
}
|
||||
case "ObjectPattern": {
|
||||
return (
|
||||
"{ " +
|
||||
pattern.properties
|
||||
.map((item) => {
|
||||
switch (item.kind) {
|
||||
case "ObjectProperty": {
|
||||
return `${item.name}: ${printPattern(item.place)}`;
|
||||
}
|
||||
case "Spread": {
|
||||
return printPattern(item);
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(item, "Unexpected object property kind");
|
||||
}
|
||||
}
|
||||
})
|
||||
.join(", ") +
|
||||
" }"
|
||||
);
|
||||
}
|
||||
case "Spread": {
|
||||
return `...${printPlace(pattern.place)}`;
|
||||
}
|
||||
case "Identifier": {
|
||||
return printPlace(pattern);
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
pattern,
|
||||
`Unexpected pattern kind '${(pattern as any).kind}'`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function printPlace(place: Place): string {
|
||||
const items = [
|
||||
place.effect,
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
Instruction,
|
||||
InstructionValue,
|
||||
makeInstructionId,
|
||||
Pattern,
|
||||
Place,
|
||||
ReactiveScope,
|
||||
ScopeId,
|
||||
@@ -57,6 +58,11 @@ export function* eachInstructionValueOperand(
|
||||
yield instrValue.value;
|
||||
break;
|
||||
}
|
||||
case "Destructure": {
|
||||
yield* eachPatternOperand(instrValue.lvalue.pattern);
|
||||
yield instrValue.value;
|
||||
break;
|
||||
}
|
||||
case "PropertyLoad": {
|
||||
yield instrValue.object;
|
||||
break;
|
||||
@@ -151,6 +157,49 @@ export function* eachInstructionValueOperand(
|
||||
}
|
||||
}
|
||||
|
||||
export function* eachPatternOperand(pattern: Pattern): Iterable<Place> {
|
||||
switch (pattern.kind) {
|
||||
case "ArrayPattern": {
|
||||
for (const item of pattern.items) {
|
||||
if (item.kind === "Identifier") {
|
||||
yield item;
|
||||
} else if (item.kind === "Spread") {
|
||||
yield item.place;
|
||||
} else {
|
||||
assertExhaustive(
|
||||
item,
|
||||
`Unexpected item kind '${(item as any).kind}'`
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "ObjectPattern": {
|
||||
for (const property of pattern.properties) {
|
||||
if (property.kind === "ObjectProperty") {
|
||||
if (property.place.kind === "Identifier") {
|
||||
yield property.place;
|
||||
}
|
||||
} else if (property.kind === "Spread") {
|
||||
yield property.place;
|
||||
} else {
|
||||
assertExhaustive(
|
||||
property,
|
||||
`Unexpected item kind '${(property as any).kind}'`
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
pattern,
|
||||
`Unexpected pattern kind '${(pattern as any).kind}'`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function mapInstructionOperands(
|
||||
instr: Instruction,
|
||||
fn: (place: Place) => Place
|
||||
@@ -191,6 +240,11 @@ export function mapInstructionOperands(
|
||||
instrValue.value = fn(instrValue.value);
|
||||
break;
|
||||
}
|
||||
case "Destructure": {
|
||||
mapPatternOperands(instrValue.lvalue.pattern, fn);
|
||||
instrValue.value = fn(instrValue.value);
|
||||
break;
|
||||
}
|
||||
case "NewExpression":
|
||||
case "CallExpression": {
|
||||
instrValue.callee = fn(instrValue.callee);
|
||||
@@ -282,6 +336,37 @@ export function mapInstructionOperands(
|
||||
}
|
||||
}
|
||||
|
||||
export function mapPatternOperands(
|
||||
pattern: Pattern,
|
||||
fn: (place: Place) => Place
|
||||
): void {
|
||||
switch (pattern.kind) {
|
||||
case "ArrayPattern": {
|
||||
pattern.items = pattern.items.map((item) => {
|
||||
if (item.kind === "Identifier") {
|
||||
return fn(item);
|
||||
} else {
|
||||
item.place = fn(item.place);
|
||||
return item;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "ObjectPattern": {
|
||||
for (const property of pattern.properties) {
|
||||
property.place = fn(property.place);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
pattern,
|
||||
`Unexpected pattern kind '${(pattern as any).kind}'`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a terminal node's block assignments using the provided function.
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,10 @@ function inferInstr(instr: Instruction, aliases: DisjointSet<Identifier>) {
|
||||
break;
|
||||
}
|
||||
case "StoreLocal": {
|
||||
// aliases.union([instrValue.place.identifier, instrValue.value.identifier]);
|
||||
alias = instrValue.value;
|
||||
break;
|
||||
}
|
||||
case "Destructure": {
|
||||
alias = instrValue.value;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,10 @@ import {
|
||||
InstructionId,
|
||||
Place,
|
||||
} from "../HIR/HIR";
|
||||
import { eachInstructionValueOperand } from "../HIR/visitors";
|
||||
import {
|
||||
eachInstructionValueOperand,
|
||||
eachPatternOperand,
|
||||
} from "../HIR/visitors";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
|
||||
export function inferAliasForStores(
|
||||
@@ -26,6 +29,10 @@ export function inferAliasForStores(
|
||||
}
|
||||
if (value.kind === "StoreLocal") {
|
||||
maybeAlias(aliases, value.lvalue.place, value.value, instr.id);
|
||||
} else if (value.kind === "Destructure") {
|
||||
for (const place of eachPatternOperand(value.lvalue.pattern)) {
|
||||
maybeAlias(aliases, place, value.value, instr.id);
|
||||
}
|
||||
}
|
||||
for (const operand of eachInstructionValueOperand(value)) {
|
||||
if (
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
Place,
|
||||
} from "../HIR/HIR";
|
||||
import { printInstruction, printPlace } from "../HIR/PrintHIR";
|
||||
import { eachInstructionOperand } from "../HIR/visitors";
|
||||
import { eachInstructionOperand, eachPatternOperand } from "../HIR/visitors";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
|
||||
/**
|
||||
@@ -123,6 +123,12 @@ export function inferMutableLifetimes(
|
||||
instr.value.lvalue.place.identifier.mutableRange.start = instr.id;
|
||||
instr.value.lvalue.place.identifier.mutableRange.end =
|
||||
makeInstructionId(instr.id + 1);
|
||||
} else if (instr.value.kind === "Destructure") {
|
||||
inferPlace(instr.value.value, instr, inferMutableRangeForStores);
|
||||
for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
|
||||
place.identifier.mutableRange.start = instr.id;
|
||||
place.identifier.mutableRange.end = makeInstructionId(instr.id + 1);
|
||||
}
|
||||
} else {
|
||||
for (const input of eachInstructionOperand(instr)) {
|
||||
inferPlace(input, instr, inferMutableRangeForStores);
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
import {
|
||||
eachInstructionOperand,
|
||||
eachInstructionValueOperand,
|
||||
eachPatternOperand,
|
||||
eachTerminalOperand,
|
||||
eachTerminalSuccessor,
|
||||
} from "../HIR/visitors";
|
||||
@@ -806,7 +807,29 @@ function inferBlock(
|
||||
state.alias(lvalue, instrValue.value);
|
||||
lvalue.effect = Effect.Store;
|
||||
state.alias(instrValue.lvalue.place, instrValue.value);
|
||||
instrValue.lvalue.place.effect = Effect.Store;
|
||||
state.reference(instrValue.lvalue.place, Effect.Store);
|
||||
continue;
|
||||
}
|
||||
case "Destructure": {
|
||||
let effect: Effect = Effect.Capture;
|
||||
for (const place of eachPatternOperand(instrValue.lvalue.pattern)) {
|
||||
if (
|
||||
state.isDefined(place) &&
|
||||
state.kind(place) === ValueKind.Context
|
||||
) {
|
||||
effect = Effect.Mutate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
state.reference(instrValue.value, effect);
|
||||
|
||||
const lvalue = instr.lvalue;
|
||||
state.alias(lvalue, instrValue.value);
|
||||
lvalue.effect = Effect.Store;
|
||||
for (const place of eachPatternOperand(instrValue.lvalue.pattern)) {
|
||||
state.alias(place, instrValue.value);
|
||||
state.reference(place, Effect.Store);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
default: {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import { BlockId, HIRFunction, Identifier, InstructionValue } from "../HIR";
|
||||
import {
|
||||
eachInstructionValueOperand,
|
||||
eachPatternOperand,
|
||||
eachTerminalOperand,
|
||||
} from "../HIR/visitors";
|
||||
import { assertExhaustive, retainWhere } from "../Utils/utils";
|
||||
@@ -85,6 +86,16 @@ function pruneableValue(
|
||||
// Stores are pruneable only if the identifier being stored to is never read later
|
||||
return !used.has(value.lvalue.place.identifier);
|
||||
}
|
||||
case "Destructure": {
|
||||
// Destructure is pruneable only if none of the identifiers are read from later
|
||||
// TODO: as an optimization, prune unused properties where safe
|
||||
for (const place of eachPatternOperand(value.lvalue.pattern)) {
|
||||
if (used.has(place.identifier)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "CallExpression":
|
||||
case "ComputedCall":
|
||||
case "ComputedStore":
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
Identifier,
|
||||
IdentifierId,
|
||||
InstructionKind,
|
||||
LValue,
|
||||
Pattern,
|
||||
Place,
|
||||
ReactiveBlock,
|
||||
ReactiveFunction,
|
||||
@@ -24,7 +24,9 @@ import {
|
||||
ReactiveTerminal,
|
||||
ReactiveValue,
|
||||
SourceLocation,
|
||||
SpreadPattern,
|
||||
} from "../HIR/HIR";
|
||||
import { eachPatternOperand } from "../HIR/visitors";
|
||||
import { Err, Ok, Result } from "../Utils/Result";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
|
||||
@@ -358,36 +360,39 @@ function codegenInstructionNullable(
|
||||
instr: ReactiveInstruction
|
||||
): t.Statement | null {
|
||||
let statement;
|
||||
if (instr.value.kind === "StoreLocal") {
|
||||
const kind = cx.hasDeclared(instr.value.lvalue.place.identifier)
|
||||
? InstructionKind.Reassign
|
||||
: instr.value.lvalue.kind;
|
||||
if (instr.value.kind === "StoreLocal" || instr.value.kind === "Destructure") {
|
||||
let kind: InstructionKind = instr.value.lvalue.kind;
|
||||
let lvalue;
|
||||
if (instr.value.kind === "StoreLocal") {
|
||||
kind = cx.hasDeclared(instr.value.lvalue.place.identifier)
|
||||
? InstructionKind.Reassign
|
||||
: kind;
|
||||
lvalue = instr.value.lvalue.place;
|
||||
} else {
|
||||
lvalue = instr.value.lvalue.pattern;
|
||||
for (const place of eachPatternOperand(lvalue)) {
|
||||
if (cx.hasDeclared(place.identifier)) {
|
||||
kind = InstructionKind.Reassign;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
),
|
||||
t.variableDeclarator(codegenLValue(lvalue), value),
|
||||
]);
|
||||
}
|
||||
case InstructionKind.Let: {
|
||||
return createVariableDeclaration(instr.loc, "let", [
|
||||
t.variableDeclarator(
|
||||
convertIdentifier(instr.value.lvalue.place.identifier),
|
||||
value
|
||||
),
|
||||
t.variableDeclarator(codegenLValue(lvalue), value),
|
||||
]);
|
||||
}
|
||||
case InstructionKind.Reassign: {
|
||||
return createExpressionStatement(
|
||||
instr.loc,
|
||||
t.assignmentExpression(
|
||||
"=",
|
||||
convertIdentifier(instr.value.lvalue.place.identifier),
|
||||
value
|
||||
)
|
||||
t.assignmentExpression("=", codegenLValue(lvalue), value)
|
||||
);
|
||||
}
|
||||
default: {
|
||||
@@ -717,12 +722,6 @@ function codegenInstructionValue(
|
||||
value = codegenPlace(cx, instrValue.place);
|
||||
break;
|
||||
}
|
||||
case "StoreLocal": {
|
||||
CompilerError.invariant(
|
||||
`Unexpected StoreLocal in codegenInstructionValue`,
|
||||
instrValue.loc
|
||||
);
|
||||
}
|
||||
case "FunctionExpression": {
|
||||
value = instrValue.expr;
|
||||
break;
|
||||
@@ -814,6 +813,13 @@ function codegenInstructionValue(
|
||||
value = t.identifier(instrValue.name);
|
||||
break;
|
||||
}
|
||||
case "Destructure":
|
||||
case "StoreLocal": {
|
||||
CompilerError.invariant(
|
||||
`Unexpected StoreLocal in codegenInstructionValue`,
|
||||
instrValue.loc
|
||||
);
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
instrValue,
|
||||
@@ -844,8 +850,44 @@ function codegenJsxElement(
|
||||
}
|
||||
}
|
||||
|
||||
function codegenLVal(lval: LValue): t.LVal {
|
||||
return convertIdentifier(lval.place.identifier);
|
||||
function codegenLValue(
|
||||
pattern: Pattern | Place | SpreadPattern
|
||||
): t.ArrayPattern | t.ObjectPattern | t.RestElement | t.Identifier {
|
||||
switch (pattern.kind) {
|
||||
case "ArrayPattern": {
|
||||
return t.arrayPattern(pattern.items.map((item) => codegenLValue(item)));
|
||||
}
|
||||
case "ObjectPattern": {
|
||||
return t.objectPattern(
|
||||
pattern.properties.map((property) => {
|
||||
if (property.kind === "ObjectProperty") {
|
||||
const key = t.identifier(property.name);
|
||||
const value = codegenLValue(property.place);
|
||||
return t.objectProperty(
|
||||
key,
|
||||
value,
|
||||
false,
|
||||
value.type === "Identifier" && value.name === key.name
|
||||
);
|
||||
} else {
|
||||
return t.restElement(codegenLValue(property.place));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
case "Spread": {
|
||||
return t.restElement(codegenLValue(pattern.place));
|
||||
}
|
||||
case "Identifier": {
|
||||
return convertIdentifier(pattern.identifier);
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
pattern,
|
||||
`Unexpected pattern kind '${(pattern as any).kind}'`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function codegenValue(
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
Place,
|
||||
ReactiveScope,
|
||||
} from "../HIR/HIR";
|
||||
import { eachInstructionOperand } from "../HIR/visitors";
|
||||
import { eachInstructionOperand, eachPatternOperand } from "../HIR/visitors";
|
||||
import DisjointSet from "../Utils/DisjointSet";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
|
||||
@@ -115,6 +115,21 @@ export function inferReactiveScopeVariables(fn: HIRFunction): void {
|
||||
) {
|
||||
operands.push(instr.value.value.identifier);
|
||||
}
|
||||
} else if (instr.value.kind === "Destructure") {
|
||||
for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
|
||||
if (
|
||||
place.identifier.mutableRange.end >
|
||||
place.identifier.mutableRange.start + 1
|
||||
) {
|
||||
operands.push(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 (
|
||||
@@ -183,6 +198,7 @@ function isMutable({ id }: Instruction, place: Place): boolean {
|
||||
|
||||
function mayAllocate(value: InstructionValue): boolean {
|
||||
switch (value.kind) {
|
||||
case "Destructure":
|
||||
case "StoreLocal":
|
||||
case "LoadGlobal":
|
||||
case "TypeCastExpression":
|
||||
|
||||
@@ -20,7 +20,10 @@ import {
|
||||
ReactiveScopeDependency,
|
||||
ReactiveValue,
|
||||
} from "../HIR/HIR";
|
||||
import { eachInstructionValueOperand } from "../HIR/visitors";
|
||||
import {
|
||||
eachInstructionValueOperand,
|
||||
eachPatternOperand,
|
||||
} from "../HIR/visitors";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
|
||||
/**
|
||||
@@ -707,6 +710,17 @@ function visitInstructionValue(
|
||||
id,
|
||||
scope: context.currentScope,
|
||||
});
|
||||
} else if (value.kind === "Destructure") {
|
||||
context.visitOperand(value.value);
|
||||
for (const place of eachPatternOperand(value.lvalue.pattern)) {
|
||||
if (value.lvalue.kind === InstructionKind.Reassign) {
|
||||
context.visitReassignment(place);
|
||||
}
|
||||
context.declare(place.identifier, {
|
||||
id,
|
||||
scope: context.currentScope,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
visitReactiveValue(context, id, value);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { printIdentifier } from "../HIR/PrintHIR";
|
||||
import {
|
||||
eachTerminalSuccessor,
|
||||
mapInstructionOperands,
|
||||
mapPatternOperands,
|
||||
mapTerminalOperands,
|
||||
} from "../HIR/visitors";
|
||||
|
||||
@@ -228,6 +229,11 @@ export default function enterSSA(func: HIRFunction): void {
|
||||
const newPlace = builder.definePlace(oldPlace);
|
||||
instr.value.lvalue.place = newPlace;
|
||||
|
||||
instr.value.value = builder.getPlace(instr.value.value);
|
||||
} else if (instr.value.kind === "Destructure") {
|
||||
mapPatternOperands(instr.value.lvalue.pattern, (place) =>
|
||||
builder.definePlace(place)
|
||||
);
|
||||
instr.value.value = builder.getPlace(instr.value.value);
|
||||
} else {
|
||||
mapInstructionOperands(instr, (place) => builder.getPlace(place));
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
import invariant from "invariant";
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import {
|
||||
BasicBlock,
|
||||
Effect,
|
||||
@@ -16,12 +17,15 @@ import {
|
||||
InstructionId,
|
||||
InstructionKind,
|
||||
LValue,
|
||||
LValuePattern,
|
||||
makeInstructionId,
|
||||
Phi,
|
||||
Place,
|
||||
} from "../HIR/HIR";
|
||||
import { printPlace } from "../HIR/PrintHIR";
|
||||
import {
|
||||
eachInstructionValueOperand,
|
||||
eachPatternOperand,
|
||||
eachTerminalOperand,
|
||||
} from "../HIR/visitors";
|
||||
|
||||
@@ -89,7 +93,10 @@ import {
|
||||
*/
|
||||
export function leaveSSA(fn: HIRFunction): void {
|
||||
// Maps identifier names to their original declaration.
|
||||
const declarations: Map<string, LValue> = new Map();
|
||||
const declarations: Map<
|
||||
string,
|
||||
{ lvalue: LValue | LValuePattern; place: Place }
|
||||
> = new Map();
|
||||
|
||||
// For non-memoizable phis, this maps original identifiers to the identifier they should be
|
||||
// *rewritten* to. The keys are the original identifiers, and the value will be _either_ the
|
||||
@@ -111,30 +118,74 @@ export function leaveSSA(fn: HIRFunction): void {
|
||||
// Iterate the instructions and perform any rewrites as well as promoting SSA variables to
|
||||
// `let` or `reassign` where possible.
|
||||
const { lvalue, value } = instr;
|
||||
if (
|
||||
value.kind === "StoreLocal" &&
|
||||
value.lvalue.place.identifier.name != null
|
||||
) {
|
||||
const originalLVal = declarations.get(
|
||||
value.lvalue.place.identifier.name
|
||||
);
|
||||
if (originalLVal === undefined) {
|
||||
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;
|
||||
if (value.kind === "StoreLocal") {
|
||||
if (value.lvalue.place.identifier.name != null) {
|
||||
const originalLVal = declarations.get(
|
||||
value.lvalue.place.identifier.name
|
||||
);
|
||||
if (originalLVal === undefined) {
|
||||
declarations.set(value.lvalue.place.identifier.name, {
|
||||
lvalue: value.lvalue,
|
||||
place: value.lvalue.place,
|
||||
});
|
||||
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.lvalue.kind = InstructionKind.Let;
|
||||
}
|
||||
} else if (rewrites.has(value.lvalue.place.identifier)) {
|
||||
value.lvalue.kind =
|
||||
rewrites.get(value.lvalue.place.identifier) ===
|
||||
value.lvalue.place.identifier
|
||||
? InstructionKind.Let
|
||||
: 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;
|
||||
} else if (value.kind === "Destructure") {
|
||||
let kind: InstructionKind | null = null;
|
||||
for (const place of eachPatternOperand(value.lvalue.pattern)) {
|
||||
if (place.identifier.name == null) {
|
||||
if (kind !== null && kind !== InstructionKind.Const) {
|
||||
CompilerError.invariant(
|
||||
`Expected consistent kind for destructuring, other places were '${kind}' but '${printPlace(
|
||||
place
|
||||
)}' is const`,
|
||||
place.loc
|
||||
);
|
||||
}
|
||||
kind = InstructionKind.Const;
|
||||
} else {
|
||||
const originalLVal = declarations.get(place.identifier.name);
|
||||
if (originalLVal === undefined) {
|
||||
declarations.set(place.identifier.name, {
|
||||
lvalue: value.lvalue,
|
||||
place,
|
||||
});
|
||||
if (kind !== null && kind !== InstructionKind.Const) {
|
||||
CompilerError.invariant(
|
||||
`Expected consistent kind for destructuring, other places were '${kind}' but '${printPlace(
|
||||
place
|
||||
)}' is const`,
|
||||
place.loc
|
||||
);
|
||||
}
|
||||
kind = InstructionKind.Const;
|
||||
} else {
|
||||
if (kind !== null && kind !== InstructionKind.Reassign) {
|
||||
CompilerError.invariant(
|
||||
`Expected consistent kind for destructuring, other places were '${kind}' but '${printPlace(
|
||||
place
|
||||
)}' is reassigned`,
|
||||
place.loc
|
||||
);
|
||||
}
|
||||
kind = InstructionKind.Reassign;
|
||||
originalLVal.lvalue.kind = InstructionKind.Let;
|
||||
}
|
||||
}
|
||||
}
|
||||
invariant(kind !== null, "Expected at least one operand");
|
||||
value.lvalue.kind = kind;
|
||||
}
|
||||
rewritePlace(lvalue, rewrites, declarations);
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
@@ -304,7 +355,7 @@ export function leaveSSA(fn: HIRFunction): void {
|
||||
loc: GeneratedSource,
|
||||
};
|
||||
block.instructions.push(instr);
|
||||
declarations.set(phi.id.name, lvalue);
|
||||
declarations.set(phi.id.name, { lvalue, place: lvalue.place });
|
||||
phi.id.mutableRange.start = terminal.id;
|
||||
if (!isPhiMutatedAfterCreation) {
|
||||
phi.id.mutableRange.end = makeInstructionId(terminal.id + 1);
|
||||
@@ -343,7 +394,7 @@ export function leaveSSA(fn: HIRFunction): void {
|
||||
if (canonicalId.name !== null) {
|
||||
const declaration = declarations.get(canonicalId.name);
|
||||
if (declaration !== undefined) {
|
||||
declaration.kind = InstructionKind.Let;
|
||||
declaration.lvalue.kind = InstructionKind.Let;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,7 +421,7 @@ export function leaveSSA(fn: HIRFunction): void {
|
||||
function rewritePlace(
|
||||
place: Place,
|
||||
rewrites: Map<Identifier, Identifier>,
|
||||
declarations: Map<string, LValue>
|
||||
declarations: Map<string, { lvalue: LValue | LValuePattern; place: Place }>
|
||||
): void {
|
||||
const prevIdentifier = place.identifier;
|
||||
const nextIdentifier = rewrites.get(prevIdentifier);
|
||||
|
||||
@@ -15,7 +15,7 @@ function component(t) {
|
||||
```javascript
|
||||
function component(t) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const a = t.a;
|
||||
const { a } = t;
|
||||
const c_0 = $[0] !== a;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
|
||||
+2
-2
@@ -19,9 +19,9 @@ function component({ mutator }) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function component(t28) {
|
||||
function component(t27) {
|
||||
const $ = React.unstable_useMemoCache(7);
|
||||
const mutator = t28.mutator;
|
||||
const { mutator } = t27;
|
||||
const c_0 = $[0] !== mutator;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
|
||||
@@ -15,7 +15,7 @@ function component() {
|
||||
```javascript
|
||||
function component() {
|
||||
const $ = React.unstable_useMemoCache(4);
|
||||
const setX = useState(0)[1];
|
||||
const [x, setX] = useState(0);
|
||||
const c_0 = $[0] !== setX;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
|
||||
@@ -15,9 +15,7 @@ function component() {
|
||||
```javascript
|
||||
function component() {
|
||||
const $ = React.unstable_useMemoCache(5);
|
||||
const t2 = useState(0);
|
||||
const x = t2[0];
|
||||
const setX = t2[1];
|
||||
const [x, setX] = useState(0);
|
||||
const c_0 = $[0] !== setX;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function foo(props) {
|
||||
let x, y;
|
||||
({ x, y } = { x: props.a, y: props.b });
|
||||
x = props.c;
|
||||
return x + y;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function foo(props) {
|
||||
const $ = React.unstable_useMemoCache(3);
|
||||
const c_0 = $[0] !== props.a;
|
||||
const c_1 = $[1] !== props.b;
|
||||
let t0;
|
||||
if (c_0 || c_1) {
|
||||
t0 = { x: props.a, y: props.b };
|
||||
$[0] = props.a;
|
||||
$[1] = props.b;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
let { x, y } = t0;
|
||||
x = props.c;
|
||||
return x + y;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
function foo(props) {
|
||||
let x, y;
|
||||
({ x, y } = { x: props.a, y: props.b });
|
||||
x = props.c;
|
||||
return x + y;
|
||||
}
|
||||
@@ -29,11 +29,16 @@ function foo(a, b, c) {
|
||||
function foo(a, b, c) {
|
||||
const $ = React.unstable_useMemoCache(5);
|
||||
|
||||
const d = a[0];
|
||||
const g = a[1][0].e.f;
|
||||
const [d, t54] = a;
|
||||
|
||||
const n = b.l.m[0][0];
|
||||
const o = b.o;
|
||||
const [t56] = t54;
|
||||
const { e: t58 } = t56;
|
||||
const { f: g } = t58;
|
||||
|
||||
const { l: t63, o } = b;
|
||||
const { m: t66 } = t63;
|
||||
const [t68] = t66;
|
||||
const [n] = t68;
|
||||
const c_0 = $[0] !== d;
|
||||
const c_1 = $[1] !== g;
|
||||
const c_2 = $[2] !== n;
|
||||
|
||||
@@ -8,16 +8,18 @@ function foo(a, b, c) {
|
||||
[
|
||||
{
|
||||
e: { f },
|
||||
...g
|
||||
},
|
||||
],
|
||||
...h
|
||||
] = a;
|
||||
const {
|
||||
l: {
|
||||
m: [[n]],
|
||||
m: [[n], ...o],
|
||||
},
|
||||
o,
|
||||
p,
|
||||
} = b;
|
||||
return [d, f, n, o];
|
||||
return [d, f, g, h, n, o, p];
|
||||
}
|
||||
|
||||
```
|
||||
@@ -26,26 +28,37 @@ function foo(a, b, c) {
|
||||
|
||||
```javascript
|
||||
function foo(a, b, c) {
|
||||
const $ = React.unstable_useMemoCache(5);
|
||||
const d = a[0];
|
||||
const f = a[1][0].e.f;
|
||||
const $ = React.unstable_useMemoCache(8);
|
||||
const [d, t40, ...h] = a;
|
||||
|
||||
const n = b.l.m[0][0];
|
||||
const o = b.o;
|
||||
const [t43] = t40;
|
||||
const { e: t45, ...g } = t43;
|
||||
const { f } = t45;
|
||||
|
||||
const { l: t51, p } = b;
|
||||
const { m: t54 } = t51;
|
||||
const [t56, ...o] = t54;
|
||||
const [n] = t56;
|
||||
const c_0 = $[0] !== d;
|
||||
const c_1 = $[1] !== f;
|
||||
const c_2 = $[2] !== n;
|
||||
const c_3 = $[3] !== o;
|
||||
const c_2 = $[2] !== g;
|
||||
const c_3 = $[3] !== h;
|
||||
const c_4 = $[4] !== n;
|
||||
const c_5 = $[5] !== o;
|
||||
const c_6 = $[6] !== p;
|
||||
let t0;
|
||||
if (c_0 || c_1 || c_2 || c_3) {
|
||||
t0 = [d, f, n, o];
|
||||
if (c_0 || c_1 || c_2 || c_3 || c_4 || c_5 || c_6) {
|
||||
t0 = [d, f, g, h, n, o, p];
|
||||
$[0] = d;
|
||||
$[1] = f;
|
||||
$[2] = n;
|
||||
$[3] = o;
|
||||
$[4] = t0;
|
||||
$[2] = g;
|
||||
$[3] = h;
|
||||
$[4] = n;
|
||||
$[5] = o;
|
||||
$[6] = p;
|
||||
$[7] = t0;
|
||||
} else {
|
||||
t0 = $[4];
|
||||
t0 = $[7];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,16 @@ function foo(a, b, c) {
|
||||
[
|
||||
{
|
||||
e: { f },
|
||||
...g
|
||||
},
|
||||
],
|
||||
...h
|
||||
] = a;
|
||||
const {
|
||||
l: {
|
||||
m: [[n]],
|
||||
m: [[n], ...o],
|
||||
},
|
||||
o,
|
||||
p,
|
||||
} = b;
|
||||
return [d, f, n, o];
|
||||
return [d, f, g, h, n, o, p];
|
||||
}
|
||||
|
||||
@@ -13,14 +13,11 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
|
||||
}
|
||||
}
|
||||
|
||||
const g = { ...a, b() {}, c: () => {} };
|
||||
const h = [...b];
|
||||
const g = { b() {}, c: () => {} };
|
||||
new c(...args);
|
||||
c(...args);
|
||||
const [y, ...yy] = useState(0);
|
||||
const { z, aa = "aa", ...zz } = useCustom();
|
||||
const { z, aa = "aa" } = useCustom();
|
||||
|
||||
<Button {...args}></Button>;
|
||||
<Button xlink:href="localhost:3000"></Button>;
|
||||
<Button haha={1}></Button>;
|
||||
<Button>{/** empty */}</Button>;
|
||||
@@ -126,293 +123,257 @@ let moduleLocal = false;
|
||||
7 | constructor() {
|
||||
8 | console.log(this.#secretSauce);
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle SpreadElement properties in ObjectExpression
|
||||
10 | }
|
||||
11 |
|
||||
> 12 | const g = { ...a, b() {}, c: () => {} };
|
||||
| ^^^^
|
||||
13 | const h = [...b];
|
||||
14 | new c(...args);
|
||||
15 | c(...args);
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle ObjectMethod properties in ObjectExpression
|
||||
10 | }
|
||||
11 |
|
||||
> 12 | const g = { ...a, b() {}, c: () => {} };
|
||||
| ^^^^^^
|
||||
13 | const h = [...b];
|
||||
14 | new c(...args);
|
||||
15 | c(...args);
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle SpreadElement elements in ArrayExpression
|
||||
11 |
|
||||
12 | const g = { ...a, b() {}, c: () => {} };
|
||||
> 13 | const h = [...b];
|
||||
| ^^^^
|
||||
14 | new c(...args);
|
||||
15 | c(...args);
|
||||
16 | const [y, ...yy] = useState(0);
|
||||
> 12 | const g = { b() {}, c: () => {} };
|
||||
| ^^^^^^
|
||||
13 | new c(...args);
|
||||
14 | c(...args);
|
||||
15 | const { z, aa = "aa" } = useCustom();
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle SpreadElement arguments in NewExpression
|
||||
12 | const g = { ...a, b() {}, c: () => {} };
|
||||
13 | const h = [...b];
|
||||
> 14 | new c(...args);
|
||||
11 |
|
||||
12 | const g = { b() {}, c: () => {} };
|
||||
> 13 | new c(...args);
|
||||
| ^^^^^^^
|
||||
15 | c(...args);
|
||||
16 | const [y, ...yy] = useState(0);
|
||||
17 | const { z, aa = "aa", ...zz } = useCustom();
|
||||
14 | c(...args);
|
||||
15 | const { z, aa = "aa" } = useCustom();
|
||||
16 |
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle SpreadElement arguments in CallExpression
|
||||
13 | const h = [...b];
|
||||
14 | new c(...args);
|
||||
> 15 | c(...args);
|
||||
12 | const g = { b() {}, c: () => {} };
|
||||
13 | new c(...args);
|
||||
> 14 | c(...args);
|
||||
| ^^^^^^^
|
||||
16 | const [y, ...yy] = useState(0);
|
||||
17 | const { z, aa = "aa", ...zz } = useCustom();
|
||||
18 |
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle RestElement in ArrayPattern
|
||||
14 | new c(...args);
|
||||
15 | c(...args);
|
||||
> 16 | const [y, ...yy] = useState(0);
|
||||
| ^^^^^
|
||||
17 | const { z, aa = "aa", ...zz } = useCustom();
|
||||
18 |
|
||||
19 | <Button {...args}></Button>;
|
||||
15 | const { z, aa = "aa" } = useCustom();
|
||||
16 |
|
||||
17 | <Button xlink:href="localhost:3000"></Button>;
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle AssignmentPattern assignments
|
||||
15 | c(...args);
|
||||
16 | const [y, ...yy] = useState(0);
|
||||
> 17 | const { z, aa = "aa", ...zz } = useCustom();
|
||||
13 | new c(...args);
|
||||
14 | c(...args);
|
||||
> 15 | const { z, aa = "aa" } = useCustom();
|
||||
| ^^^^^^^^^
|
||||
18 |
|
||||
19 | <Button {...args}></Button>;
|
||||
20 | <Button xlink:href="localhost:3000"></Button>;
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle RestElement properties in ObjectPattern
|
||||
15 | c(...args);
|
||||
16 | const [y, ...yy] = useState(0);
|
||||
> 17 | const { z, aa = "aa", ...zz } = useCustom();
|
||||
| ^^^^^
|
||||
18 |
|
||||
19 | <Button {...args}></Button>;
|
||||
20 | <Button xlink:href="localhost:3000"></Button>;
|
||||
16 |
|
||||
17 | <Button xlink:href="localhost:3000"></Button>;
|
||||
18 | <Button haha={1}></Button>;
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle JSXNamespacedName attribute names in JSXElement
|
||||
18 |
|
||||
19 | <Button {...args}></Button>;
|
||||
> 20 | <Button xlink:href="localhost:3000"></Button>;
|
||||
15 | const { z, aa = "aa" } = useCustom();
|
||||
16 |
|
||||
> 17 | <Button xlink:href="localhost:3000"></Button>;
|
||||
| ^^^^^^^^^^
|
||||
21 | <Button haha={1}></Button>;
|
||||
22 | <Button>{/** empty */}</Button>;
|
||||
23 | <DesignSystem.Button />;
|
||||
18 | <Button haha={1}></Button>;
|
||||
19 | <Button>{/** empty */}</Button>;
|
||||
20 | <DesignSystem.Button />;
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerJsxElement) Handle JSXEmptyExpression expressions
|
||||
20 | <Button xlink:href="localhost:3000"></Button>;
|
||||
21 | <Button haha={1}></Button>;
|
||||
> 22 | <Button>{/** empty */}</Button>;
|
||||
17 | <Button xlink:href="localhost:3000"></Button>;
|
||||
18 | <Button haha={1}></Button>;
|
||||
> 19 | <Button>{/** empty */}</Button>;
|
||||
| ^^^^^^^^^^^^
|
||||
23 | <DesignSystem.Button />;
|
||||
24 |
|
||||
25 | const j = function bar([quz, qux], ...args) {};
|
||||
20 | <DesignSystem.Button />;
|
||||
21 |
|
||||
22 | const j = function bar([quz, qux], ...args) {};
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerJsxElementName) Handle JSXMemberExpression tags
|
||||
21 | <Button haha={1}></Button>;
|
||||
22 | <Button>{/** empty */}</Button>;
|
||||
> 23 | <DesignSystem.Button />;
|
||||
18 | <Button haha={1}></Button>;
|
||||
19 | <Button>{/** empty */}</Button>;
|
||||
> 20 | <DesignSystem.Button />;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
24 |
|
||||
25 | const j = function bar([quz, qux], ...args) {};
|
||||
26 |
|
||||
21 |
|
||||
22 | const j = function bar([quz, qux], ...args) {};
|
||||
23 |
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lower) Handle ArrayPattern params
|
||||
23 | <DesignSystem.Button />;
|
||||
24 |
|
||||
> 25 | const j = function bar([quz, qux], ...args) {};
|
||||
20 | <DesignSystem.Button />;
|
||||
21 |
|
||||
> 22 | const j = function bar([quz, qux], ...args) {};
|
||||
| ^^^^^^^^^^
|
||||
26 |
|
||||
27 | for (; i < 3; i += 1) {
|
||||
28 | x.push(i);
|
||||
23 |
|
||||
24 | for (; i < 3; i += 1) {
|
||||
25 | x.push(i);
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lower) Handle RestElement params
|
||||
23 | <DesignSystem.Button />;
|
||||
24 |
|
||||
> 25 | const j = function bar([quz, qux], ...args) {};
|
||||
20 | <DesignSystem.Button />;
|
||||
21 |
|
||||
> 22 | const j = function bar([quz, qux], ...args) {};
|
||||
| ^^^^^^^
|
||||
26 |
|
||||
27 | for (; i < 3; i += 1) {
|
||||
28 | x.push(i);
|
||||
23 |
|
||||
24 | for (; i < 3; i += 1) {
|
||||
25 | x.push(i);
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
|
||||
25 | const j = function bar([quz, qux], ...args) {};
|
||||
26 |
|
||||
> 27 | for (; i < 3; i += 1) {
|
||||
22 | const j = function bar([quz, qux], ...args) {};
|
||||
23 |
|
||||
> 24 | for (; i < 3; i += 1) {
|
||||
| ^
|
||||
28 | x.push(i);
|
||||
29 | }
|
||||
30 | for (; i < 3; ) {
|
||||
25 | x.push(i);
|
||||
26 | }
|
||||
27 | for (; i < 3; ) {
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
|
||||
28 | x.push(i);
|
||||
29 | }
|
||||
> 30 | for (; i < 3; ) {
|
||||
25 | x.push(i);
|
||||
26 | }
|
||||
> 27 | for (; i < 3; ) {
|
||||
| ^
|
||||
31 | break;
|
||||
32 | }
|
||||
33 | for (;;) {
|
||||
28 | break;
|
||||
29 | }
|
||||
30 | for (;;) {
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty update in ForStatement
|
||||
28 | x.push(i);
|
||||
29 | }
|
||||
> 30 | for (; i < 3; ) {
|
||||
25 | x.push(i);
|
||||
26 | }
|
||||
> 27 | for (; i < 3; ) {
|
||||
| ^
|
||||
31 | break;
|
||||
32 | }
|
||||
33 | for (;;) {
|
||||
28 | break;
|
||||
29 | }
|
||||
30 | for (;;) {
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
|
||||
28 | break;
|
||||
29 | }
|
||||
> 30 | for (;;) {
|
||||
| ^
|
||||
31 | break;
|
||||
32 | }
|
||||
> 33 | for (;;) {
|
||||
| ^
|
||||
34 | break;
|
||||
35 | }
|
||||
36 |
|
||||
33 |
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty update in ForStatement
|
||||
28 | break;
|
||||
29 | }
|
||||
> 30 | for (;;) {
|
||||
| ^
|
||||
31 | break;
|
||||
32 | }
|
||||
> 33 | for (;;) {
|
||||
| ^
|
||||
34 | break;
|
||||
35 | }
|
||||
36 |
|
||||
33 |
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty test in ForStatement
|
||||
28 | break;
|
||||
29 | }
|
||||
> 30 | for (;;) {
|
||||
| ^
|
||||
31 | break;
|
||||
32 | }
|
||||
> 33 | for (;;) {
|
||||
| ^
|
||||
34 | break;
|
||||
35 | }
|
||||
36 |
|
||||
33 |
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle tagged template with interpolations
|
||||
35 | }
|
||||
36 |
|
||||
> 37 | graphql`
|
||||
32 | }
|
||||
33 |
|
||||
> 34 | graphql`
|
||||
| ^
|
||||
38 | ${g}
|
||||
39 | `;
|
||||
40 |
|
||||
35 | ${g}
|
||||
36 | `;
|
||||
37 |
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value
|
||||
39 | `;
|
||||
40 |
|
||||
> 41 | graphql`\\t\n`;
|
||||
36 | `;
|
||||
37 |
|
||||
> 38 | graphql`\\t\n`;
|
||||
| ^^^^^^^^^^^^^^
|
||||
42 |
|
||||
43 | for (const c of [1, 2]) {
|
||||
44 | }
|
||||
39 |
|
||||
40 | for (const c of [1, 2]) {
|
||||
41 | }
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle ForOfStatement statements
|
||||
41 | graphql`\\t\n`;
|
||||
42 |
|
||||
> 43 | for (const c of [1, 2]) {
|
||||
38 | graphql`\\t\n`;
|
||||
39 |
|
||||
> 40 | for (const c of [1, 2]) {
|
||||
| ^
|
||||
44 | }
|
||||
45 |
|
||||
46 | for (let x in { a: 1 }) {
|
||||
41 | }
|
||||
42 |
|
||||
43 | for (let x in { a: 1 }) {
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle ForInStatement statements
|
||||
41 | }
|
||||
42 |
|
||||
> 43 | for (let x in { a: 1 }) {
|
||||
| ^
|
||||
44 | }
|
||||
45 |
|
||||
> 46 | for (let x in { a: 1 }) {
|
||||
| ^
|
||||
47 | }
|
||||
48 |
|
||||
49 | let updateIdentifier = 0;
|
||||
46 | let updateIdentifier = 0;
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression
|
||||
48 |
|
||||
49 | let updateIdentifier = 0;
|
||||
> 50 | --updateIdentifier;
|
||||
45 |
|
||||
46 | let updateIdentifier = 0;
|
||||
> 47 | --updateIdentifier;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
51 | ++updateIdentifier;
|
||||
52 | updateIdentifier.y++;
|
||||
53 | updateIdentifier.y--;
|
||||
48 | ++updateIdentifier;
|
||||
49 | updateIdentifier.y++;
|
||||
50 | updateIdentifier.y--;
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression
|
||||
49 | let updateIdentifier = 0;
|
||||
50 | --updateIdentifier;
|
||||
> 51 | ++updateIdentifier;
|
||||
46 | let updateIdentifier = 0;
|
||||
47 | --updateIdentifier;
|
||||
> 48 | ++updateIdentifier;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
52 | updateIdentifier.y++;
|
||||
53 | updateIdentifier.y--;
|
||||
54 |
|
||||
49 | updateIdentifier.y++;
|
||||
50 | updateIdentifier.y--;
|
||||
51 |
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument
|
||||
50 | --updateIdentifier;
|
||||
51 | ++updateIdentifier;
|
||||
> 52 | updateIdentifier.y++;
|
||||
47 | --updateIdentifier;
|
||||
48 | ++updateIdentifier;
|
||||
> 49 | updateIdentifier.y++;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
53 | updateIdentifier.y--;
|
||||
54 |
|
||||
55 | switch (i) {
|
||||
50 | updateIdentifier.y--;
|
||||
51 |
|
||||
52 | switch (i) {
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument
|
||||
51 | ++updateIdentifier;
|
||||
52 | updateIdentifier.y++;
|
||||
> 53 | updateIdentifier.y--;
|
||||
48 | ++updateIdentifier;
|
||||
49 | updateIdentifier.y++;
|
||||
> 50 | updateIdentifier.y--;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
54 |
|
||||
55 | switch (i) {
|
||||
56 | case 1 + 1: {
|
||||
51 |
|
||||
52 | switch (i) {
|
||||
53 | case 1 + 1: {
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
|
||||
58 | case foo(): {
|
||||
59 | }
|
||||
> 60 | case x.y: {
|
||||
55 | case foo(): {
|
||||
56 | }
|
||||
> 57 | case x.y: {
|
||||
| ^^^
|
||||
61 | }
|
||||
62 | default: {
|
||||
63 | }
|
||||
58 | }
|
||||
59 | default: {
|
||||
60 | }
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
|
||||
56 | case 1 + 1: {
|
||||
57 | }
|
||||
> 58 | case foo(): {
|
||||
53 | case 1 + 1: {
|
||||
54 | }
|
||||
> 55 | case foo(): {
|
||||
| ^^^^^
|
||||
59 | }
|
||||
60 | case x.y: {
|
||||
61 | }
|
||||
56 | }
|
||||
57 | case x.y: {
|
||||
58 | }
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
|
||||
54 |
|
||||
55 | switch (i) {
|
||||
> 56 | case 1 + 1: {
|
||||
51 |
|
||||
52 | switch (i) {
|
||||
> 53 | case 1 + 1: {
|
||||
| ^^^^^
|
||||
57 | }
|
||||
58 | case foo(): {
|
||||
59 | }
|
||||
54 | }
|
||||
55 | case foo(): {
|
||||
56 | }
|
||||
|
||||
[ReactForget] InvalidInputError: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported.
|
||||
65 |
|
||||
66 | // Cannot assign to globals
|
||||
> 67 | someUnknownGlobal = true;
|
||||
62 |
|
||||
63 | // Cannot assign to globals
|
||||
> 64 | someUnknownGlobal = true;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
68 | moduleLocal = true;
|
||||
69 | }
|
||||
70 |
|
||||
65 | moduleLocal = true;
|
||||
66 | }
|
||||
67 |
|
||||
|
||||
[ReactForget] InvalidInputError: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported.
|
||||
66 | // Cannot assign to globals
|
||||
67 | someUnknownGlobal = true;
|
||||
> 68 | moduleLocal = true;
|
||||
63 | // Cannot assign to globals
|
||||
64 | someUnknownGlobal = true;
|
||||
> 65 | moduleLocal = true;
|
||||
| ^^^^^^^^^^^
|
||||
69 | }
|
||||
70 |
|
||||
71 | let moduleLocal = false;
|
||||
66 | }
|
||||
67 |
|
||||
68 | let moduleLocal = false;
|
||||
```
|
||||
|
||||
|
||||
@@ -9,14 +9,11 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
|
||||
}
|
||||
}
|
||||
|
||||
const g = { ...a, b() {}, c: () => {} };
|
||||
const h = [...b];
|
||||
const g = { b() {}, c: () => {} };
|
||||
new c(...args);
|
||||
c(...args);
|
||||
const [y, ...yy] = useState(0);
|
||||
const { z, aa = "aa", ...zz } = useCustom();
|
||||
const { z, aa = "aa" } = useCustom();
|
||||
|
||||
<Button {...args}></Button>;
|
||||
<Button xlink:href="localhost:3000"></Button>;
|
||||
<Button haha={1}></Button>;
|
||||
<Button>{/** empty */}</Button>;
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ function Component(props) {
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const setValue = useState(null)[1];
|
||||
const [value, setValue] = useState(null);
|
||||
|
||||
const onChange = (e) => setValue((value) => value + e.target.value);
|
||||
|
||||
|
||||
@@ -13,10 +13,9 @@ function component({ a, b }) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function component(t19) {
|
||||
function component(t16) {
|
||||
const $ = React.unstable_useMemoCache(7);
|
||||
const a = t19.a;
|
||||
const b = t19.b;
|
||||
const { a, b } = t16;
|
||||
const c_0 = $[0] !== a;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function foo(props) {
|
||||
let { x } = { x: [] };
|
||||
x.push(props.bar);
|
||||
if (props.cond) {
|
||||
({ x } = { x: {} });
|
||||
({ x } = { x: [] });
|
||||
x.push(props.foo);
|
||||
}
|
||||
mut(x);
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function foo(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const c_0 = $[0] !== props;
|
||||
let x;
|
||||
if (c_0) {
|
||||
({ x } = { x: [] });
|
||||
x.push(props.bar);
|
||||
if (props.cond) {
|
||||
({ x } = { x: [] });
|
||||
x.push(props.foo);
|
||||
}
|
||||
|
||||
mut(x);
|
||||
$[0] = props;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
function foo(props) {
|
||||
let { x } = { x: [] };
|
||||
x.push(props.bar);
|
||||
if (props.cond) {
|
||||
({ x } = { x: {} });
|
||||
({ x } = { x: [] });
|
||||
x.push(props.foo);
|
||||
}
|
||||
mut(x);
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function foo(props) {
|
||||
let { x } = { x: [] };
|
||||
x.push(props.bar);
|
||||
if (props.cond) {
|
||||
({ x } = { x: {} });
|
||||
({ x } = { x: [] });
|
||||
x.push(props.foo);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function foo(props) {
|
||||
const $ = React.unstable_useMemoCache(4);
|
||||
const c_0 = $[0] !== props.bar;
|
||||
let x;
|
||||
if (c_0) {
|
||||
({ x } = { x: [] });
|
||||
x.push(props.bar);
|
||||
$[0] = props.bar;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
if (props.cond) {
|
||||
const c_2 = $[2] !== props.foo;
|
||||
if (c_2) {
|
||||
({ x } = { x: [] });
|
||||
x.push(props.foo);
|
||||
$[2] = props.foo;
|
||||
$[3] = x;
|
||||
} else {
|
||||
x = $[3];
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
function foo(props) {
|
||||
let { x } = { x: [] };
|
||||
x.push(props.bar);
|
||||
if (props.cond) {
|
||||
({ x } = { x: {} });
|
||||
({ x } = { x: [] });
|
||||
x.push(props.foo);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
@@ -16,9 +16,7 @@ function component() {
|
||||
```javascript
|
||||
function component() {
|
||||
const $ = React.unstable_useMemoCache(5);
|
||||
const t2 = useState(0);
|
||||
const count = t2[0];
|
||||
const setCount = t2[1];
|
||||
const [count, setCount] = useState(0);
|
||||
const c_0 = $[0] !== setCount;
|
||||
const c_1 = $[1] !== count;
|
||||
let t0;
|
||||
|
||||
Reference in New Issue
Block a user