Compiler: unfork prettier config (#30205)

Updates the prettier config to format all `.ts` and `.tsx` files in the
repo using the existing defaults and removing overrides.

The first commit in this PR contains the config changes, the second is
just the result of running `yarn prettier-all`.
This commit is contained in:
Jan Kassens
2024-07-18 17:00:24 -04:00
committed by GitHub
parent 6fac743ed7
commit fd2b3e13d3
1898 changed files with 12437 additions and 12663 deletions
@@ -10,8 +10,8 @@ import {
IdentifierId,
ReactiveScope,
makeInstructionId,
} from "../HIR";
import DisjointSet from "../Utils/DisjointSet";
} from '../HIR';
import DisjointSet from '../Utils/DisjointSet';
/**
* Ensures that method call instructions have scopes such that either:
@@ -24,8 +24,8 @@ export function alignMethodCallScopes(fn: HIRFunction): void {
for (const [, block] of fn.body.blocks) {
for (const instr of block.instructions) {
const { lvalue, value } = instr;
if (value.kind === "MethodCall") {
const {lvalue, value} = instr;
if (value.kind === 'MethodCall') {
const lvalueScope = lvalue.identifier.scope;
const propertyScope = value.property.identifier.scope;
if (lvalueScope !== null) {
@@ -44,8 +44,8 @@ export function alignMethodCallScopes(fn: HIRFunction): void {
scopeMapping.set(value.property.identifier.id, null);
}
} else if (
value.kind === "FunctionExpression" ||
value.kind === "ObjectMethod"
value.kind === 'FunctionExpression' ||
value.kind === 'ObjectMethod'
) {
alignMethodCallScopes(value.loweredFunc.func);
}
@@ -57,10 +57,10 @@ export function alignMethodCallScopes(fn: HIRFunction): void {
return;
}
root.range.start = makeInstructionId(
Math.min(scope.range.start, root.range.start)
Math.min(scope.range.start, root.range.start),
);
root.range.end = makeInstructionId(
Math.max(scope.range.end, root.range.end)
Math.max(scope.range.end, root.range.end),
);
});
@@ -5,16 +5,16 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "..";
import {CompilerError} from '..';
import {
GeneratedSource,
HIRFunction,
Identifier,
ReactiveScope,
makeInstructionId,
} from "../HIR";
import { eachInstructionValueOperand } from "../HIR/visitors";
import DisjointSet from "../Utils/DisjointSet";
} from '../HIR';
import {eachInstructionValueOperand} from '../HIR/visitors';
import DisjointSet from '../Utils/DisjointSet';
/**
* Align scopes of object method values to that of their enclosing object expressions.
@@ -27,10 +27,10 @@ function findScopesToMerge(fn: HIRFunction): DisjointSet<ReactiveScope> {
const mergeScopesBuilder = new DisjointSet<ReactiveScope>();
for (const [_, block] of fn.body.blocks) {
for (const { lvalue, value } of block.instructions) {
if (value.kind === "ObjectMethod") {
for (const {lvalue, value} of block.instructions) {
if (value.kind === 'ObjectMethod') {
objectMethodDecls.add(lvalue.identifier);
} else if (value.kind === "ObjectExpression") {
} else if (value.kind === 'ObjectExpression') {
for (const operand of eachInstructionValueOperand(value)) {
if (objectMethodDecls.has(operand.identifier)) {
const operandScope = operand.identifier.scope;
@@ -40,10 +40,10 @@ function findScopesToMerge(fn: HIRFunction): DisjointSet<ReactiveScope> {
operandScope != null && lvalueScope != null,
{
reason:
"Internal error: Expected all ObjectExpressions and ObjectMethods to have non-null scope.",
'Internal error: Expected all ObjectExpressions and ObjectMethods to have non-null scope.',
suggestions: null,
loc: GeneratedSource,
}
},
);
mergeScopesBuilder.union([operandScope, lvalueScope]);
}
@@ -57,10 +57,10 @@ function findScopesToMerge(fn: HIRFunction): DisjointSet<ReactiveScope> {
export function alignObjectMethodScopes(fn: HIRFunction): void {
// Handle inner functions: we assume that Scopes are disjoint across functions
for (const [_, block] of fn.body.blocks) {
for (const { value } of block.instructions) {
for (const {value} of block.instructions) {
if (
value.kind === "ObjectMethod" ||
value.kind === "FunctionExpression"
value.kind === 'ObjectMethod' ||
value.kind === 'FunctionExpression'
) {
alignObjectMethodScopes(value.loweredFunc.func);
}
@@ -74,10 +74,10 @@ export function alignObjectMethodScopes(fn: HIRFunction): void {
for (const [scope, root] of scopeGroupsMap) {
if (scope !== root) {
root.range.start = makeInstructionId(
Math.min(scope.range.start, root.range.start)
Math.min(scope.range.start, root.range.start),
);
root.range.end = makeInstructionId(
Math.max(scope.range.end, root.range.end)
Math.max(scope.range.end, root.range.end),
);
}
}
@@ -87,7 +87,7 @@ export function alignObjectMethodScopes(fn: HIRFunction): void {
*/
for (const [_, block] of fn.body.blocks) {
for (const {
lvalue: { identifier },
lvalue: {identifier},
} of block.instructions) {
if (identifier.scope != null) {
const root = scopeGroupsMap.get(identifier.scope);
@@ -14,9 +14,9 @@ import {
ReactiveScope,
ScopeId,
makeInstructionId,
} from "../HIR/HIR";
import { getPlaceScope } from "./BuildReactiveBlocks";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
} from '../HIR/HIR';
import {getPlaceScope} from './BuildReactiveBlocks';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
/*
* Note: this is the 2nd of 4 passes that determine how to break a function into discrete
@@ -84,10 +84,10 @@ class Visitor extends ReactiveFunctionVisitor<Context> {
override visitInstruction(instr: ReactiveInstruction, state: Context): void {
switch (instr.value.kind) {
case "OptionalExpression":
case "SequenceExpression":
case "ConditionalExpression":
case "LogicalExpression": {
case 'OptionalExpression':
case 'SequenceExpression':
case 'ConditionalExpression':
case 'LogicalExpression': {
const prevScopeCount = state.currentScopes().length;
this.traverseInstruction(instr, state);
@@ -105,7 +105,7 @@ class Visitor extends ReactiveFunctionVisitor<Context> {
for (let i = prevScopeCount; i < scopes.length; i++) {
const scope = scopes[i];
scope.scope.range.start = makeInstructionId(
Math.min(instr.id, scope.scope.range.start)
Math.min(instr.id, scope.scope.range.start),
);
}
break;
@@ -123,7 +123,7 @@ class Visitor extends ReactiveFunctionVisitor<Context> {
}
}
type PendingReactiveScope = { active: boolean; scope: ReactiveScope };
type PendingReactiveScope = {active: boolean; scope: ReactiveScope};
class Context {
/*
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "..";
import {CompilerError} from '..';
import {
BlockId,
HIRFunction,
@@ -14,16 +14,16 @@ import {
Place,
ReactiveScope,
makeInstructionId,
} from "../HIR/HIR";
} from '../HIR/HIR';
import {
eachInstructionLValue,
eachInstructionValueOperand,
eachTerminalOperand,
mapTerminalSuccessors,
terminalFallthrough,
} from "../HIR/visitors";
import { retainWhere_Set } from "../Utils/utils";
import { getPlaceScope } from "./BuildReactiveBlocks";
} from '../HIR/visitors';
import {retainWhere_Set} from '../Utils/utils';
import {getPlaceScope} from './BuildReactiveBlocks';
type InstructionRange = MutableRange;
/*
@@ -80,7 +80,7 @@ export function alignReactiveScopesToBlockScopesHIR(fn: HIRFunction): void {
function recordPlace(
id: InstructionId,
place: Place,
node: ValueBlockNode | null
node: ValueBlockNode | null,
): void {
if (place.identifier.scope !== null) {
placeScopes.set(place, place.identifier.scope);
@@ -91,7 +91,7 @@ export function alignReactiveScopesToBlockScopesHIR(fn: HIRFunction): void {
return;
}
activeScopes.add(scope);
node?.children.push({ kind: "scope", scope, id });
node?.children.push({kind: 'scope', scope, id});
if (seen.has(scope)) {
return;
@@ -99,17 +99,17 @@ export function alignReactiveScopesToBlockScopesHIR(fn: HIRFunction): void {
seen.add(scope);
if (node != null && node.valueRange !== null) {
scope.range.start = makeInstructionId(
Math.min(node.valueRange.start, scope.range.start)
Math.min(node.valueRange.start, scope.range.start),
);
scope.range.end = makeInstructionId(
Math.max(node.valueRange.end, scope.range.end)
Math.max(node.valueRange.end, scope.range.end),
);
}
}
for (const [, block] of fn.body.blocks) {
const startingId = block.instructions[0]?.id ?? block.terminal.id;
retainWhere_Set(activeScopes, (scope) => scope.range.end > startingId);
retainWhere_Set(activeScopes, scope => scope.range.end > startingId);
const top = activeBlockFallthroughRanges.at(-1);
if (top?.fallthrough === block.id) {
activeBlockFallthroughRanges.pop();
@@ -120,12 +120,12 @@ export function alignReactiveScopesToBlockScopesHIR(fn: HIRFunction): void {
*/
for (const scope of activeScopes) {
scope.range.start = makeInstructionId(
Math.min(scope.range.start, top.range.start)
Math.min(scope.range.start, top.range.start),
);
}
}
const { instructions, terminal } = block;
const {instructions, terminal} = block;
const node = valueBlockNodes.get(block.id) ?? null;
for (const instr of instructions) {
for (const lvalue of eachInstructionLValue(instr)) {
@@ -152,7 +152,7 @@ export function alignReactiveScopesToBlockScopesHIR(fn: HIRFunction): void {
for (const scope of activeScopes) {
if (scope.range.end > terminal.id) {
scope.range.end = makeInstructionId(
Math.max(scope.range.end, nextId)
Math.max(scope.range.end, nextId),
);
}
}
@@ -169,7 +169,7 @@ export function alignReactiveScopesToBlockScopesHIR(fn: HIRFunction): void {
});
CompilerError.invariant(!valueBlockNodes.has(fallthrough), {
reason: "Expect hir blocks to have unique fallthroughs",
reason: 'Expect hir blocks to have unique fallthroughs',
loc: terminal.loc,
});
if (node != null) {
@@ -185,22 +185,22 @@ export function alignReactiveScopesToBlockScopesHIR(fn: HIRFunction): void {
* TODO: add a variant of eachTerminalSuccessor() that visits _all_ successors, not
* just those that are direct successors for normal control-flow ordering.
*/
mapTerminalSuccessors(terminal, (successor) => {
mapTerminalSuccessors(terminal, successor => {
if (valueBlockNodes.has(successor)) {
return successor;
}
const successorBlock = fn.body.blocks.get(successor)!;
if (successorBlock.kind === "block" || successorBlock.kind === "catch") {
if (successorBlock.kind === 'block' || successorBlock.kind === 'catch') {
/*
* we need the block kind check here because the do..while terminal's
* successor is a block, and try's successor is a catch block
*/
} else if (
node == null ||
terminal.kind === "ternary" ||
terminal.kind === "logical" ||
terminal.kind === "optional"
terminal.kind === 'ternary' ||
terminal.kind === 'logical' ||
terminal.kind === 'optional'
) {
/**
* Create a new node whenever we transition from non-value -> value block.
@@ -232,7 +232,7 @@ export function alignReactiveScopesToBlockScopesHIR(fn: HIRFunction): void {
valueRange = node.valueRange;
}
const childNode: ValueBlockNode = {
kind: "node",
kind: 'node',
id: terminal.id,
children: [],
valueRange,
@@ -249,13 +249,13 @@ export function alignReactiveScopesToBlockScopesHIR(fn: HIRFunction): void {
}
type ValueBlockNode = {
kind: "node";
kind: 'node';
id: InstructionId;
valueRange: MutableRange;
children: Array<ValueBlockNode | ReactiveScopeNode>;
};
type ReactiveScopeNode = {
kind: "scope";
kind: 'scope';
id: InstructionId;
scope: ReactiveScope;
};
@@ -263,17 +263,17 @@ type ReactiveScopeNode = {
function _debug(node: ValueBlockNode): string {
const buf: Array<string> = [];
_printNode(node, buf, 0);
return buf.join("\n");
return buf.join('\n');
}
function _printNode(
node: ValueBlockNode | ReactiveScopeNode,
out: Array<string>,
depth: number = 0
depth: number = 0,
): void {
let prefix = " ".repeat(depth);
if (node.kind === "scope") {
let prefix = ' '.repeat(depth);
if (node.kind === 'scope') {
out.push(
`${prefix}[${node.id}] @${node.scope.id} [${node.scope.range.start}:${node.scope.range.end}]`
`${prefix}[${node.id}] @${node.scope.id} [${node.scope.range.start}:${node.scope.range.end}]`,
);
} else {
let range = ` (range=[${node.valueRange.start}:${node.valueRange.end}])`;
@@ -5,17 +5,17 @@
* LICENSE file in the root directory of this source tree.
*/
import { visitReactiveFunction } from ".";
import { CompilerError } from "..";
import {visitReactiveFunction} from '.';
import {CompilerError} from '..';
import {
InstructionId,
Place,
ReactiveFunction,
ReactiveScopeBlock,
ScopeId,
} from "../HIR";
import { getPlaceScope } from "./BuildReactiveBlocks";
import { ReactiveFunctionVisitor } from "./visitors";
} from '../HIR';
import {getPlaceScope} from './BuildReactiveBlocks';
import {ReactiveFunctionVisitor} from './visitors';
/*
* Internal validation pass that checks all the instructions involved in creating
@@ -41,14 +41,14 @@ import { ReactiveFunctionVisitor } from "./visitors";
* against compiler coding mistakes in earlier passes.
*/
export function assertScopeInstructionsWithinScopes(
fn: ReactiveFunction
fn: ReactiveFunction,
): void {
const existingScopes = new Set<ScopeId>();
visitReactiveFunction(fn, new FindAllScopesVisitor(), existingScopes);
visitReactiveFunction(
fn,
new CheckInstructionsAgainstScopesVisitor(),
existingScopes
existingScopes,
);
}
@@ -67,7 +67,7 @@ class CheckInstructionsAgainstScopesVisitor extends ReactiveFunctionVisitor<
override visitPlace(
id: InstructionId,
place: Place,
state: Set<ScopeId>
state: Set<ScopeId>,
): void {
const scope = getPlaceScope(id, place);
if (
@@ -84,7 +84,7 @@ class CheckInstructionsAgainstScopesVisitor extends ReactiveFunctionVisitor<
description: `Instruction [${id}] is part of scope @${scope.id}, but that scope has already completed.`,
loc: place.loc,
reason:
"Encountered an instruction that should be part of a scope, but where that scope has already completed",
'Encountered an instruction that should be part of a scope, but where that scope has already completed',
suggestions: null,
});
}
@@ -5,9 +5,9 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "..";
import { BlockId, ReactiveFunction, ReactiveTerminalStatement } from "../HIR";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
import {CompilerError} from '..';
import {BlockId, ReactiveFunction, ReactiveTerminalStatement} from '../HIR';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
/**
* Assert that all break/continue targets reference existent labels.
@@ -19,15 +19,15 @@ export function assertWellFormedBreakTargets(fn: ReactiveFunction): void {
class Visitor extends ReactiveFunctionVisitor<Set<BlockId>> {
override visitTerminal(
stmt: ReactiveTerminalStatement,
seenLabels: Set<BlockId>
seenLabels: Set<BlockId>,
): void {
if (stmt.label != null) {
seenLabels.add(stmt.label.id);
}
const terminal = stmt.terminal;
if (terminal.kind === "break" || terminal.kind === "continue") {
if (terminal.kind === 'break' || terminal.kind === 'continue') {
CompilerError.invariant(seenLabels.has(terminal.target), {
reason: "Unexpected break to invalid label",
reason: 'Unexpected break to invalid label',
loc: stmt.terminal.loc,
});
}
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "../CompilerError";
import {CompilerError} from '../CompilerError';
import {
BlockId,
InstructionId,
@@ -17,10 +17,10 @@ import {
ReactiveScopeBlock,
ReactiveStatement,
ScopeId,
} from "../HIR";
import { eachInstructionLValue } from "../HIR/visitors";
import { assertExhaustive } from "../Utils/utils";
import { eachReactiveValueOperand, mapTerminalBlocks } from "./visitors";
} from '../HIR';
import {eachInstructionLValue} from '../HIR/visitors';
import {assertExhaustive} from '../Utils/utils';
import {eachReactiveValueOperand, mapTerminalBlocks} from './visitors';
/*
* Note: this is the 4th of 4 passes that determine how to break a function into discrete
@@ -63,7 +63,7 @@ class Context {
append(
stmt: ReactiveStatement,
label: { id: BlockId; implicit: boolean } | null
label: {id: BlockId; implicit: boolean} | null,
): void {
this.#builders.at(-1)!.append(stmt, label);
}
@@ -74,7 +74,7 @@ class Context {
fn();
const popped = this.#builders.pop();
CompilerError.invariant(popped === builder, {
reason: "Expected push/pop to be called 1:1",
reason: 'Expected push/pop to be called 1:1',
description: null,
loc: null,
suggestions: null,
@@ -86,23 +86,23 @@ class Context {
class Builder {
#instructions: ReactiveBlock;
#stack: Array<
| { kind: "scope"; block: ReactiveScopeBlock }
| { kind: "block"; block: ReactiveBlock }
| {kind: 'scope'; block: ReactiveScopeBlock}
| {kind: 'block'; block: ReactiveBlock}
>;
constructor() {
const block: ReactiveBlock = [];
this.#instructions = block;
this.#stack = [{ kind: "block", block }];
this.#stack = [{kind: 'block', block}];
}
append(
item: ReactiveStatement,
label: { id: BlockId; implicit: boolean } | null
label: {id: BlockId; implicit: boolean} | null,
): void {
if (label !== null) {
CompilerError.invariant(item.kind === "terminal", {
reason: "Only terminals may have a label",
CompilerError.invariant(item.kind === 'terminal', {
reason: 'Only terminals may have a label',
description: null,
loc: null,
suggestions: null,
@@ -114,25 +114,25 @@ class Builder {
startScope(scope: ReactiveScope): void {
const block: ReactiveScopeBlock = {
kind: "scope",
kind: 'scope',
scope,
instructions: [],
};
this.append(block, null);
this.#instructions = block.instructions;
this.#stack.push({ kind: "scope", block });
this.#stack.push({kind: 'scope', block});
}
visitId(id: InstructionId): void {
for (let i = 0; i < this.#stack.length; i++) {
const entry = this.#stack[i]!;
if (entry.kind === "scope" && id >= entry.block.scope.range.end) {
if (entry.kind === 'scope' && id >= entry.block.scope.range.end) {
this.#stack.length = i;
break;
}
}
const last = this.#stack[this.#stack.length - 1]!;
if (last.kind === "block") {
if (last.kind === 'block') {
this.#instructions = last.block;
} else {
this.#instructions = last.block.instructions;
@@ -148,8 +148,8 @@ class Builder {
* );
*/
const first = this.#stack[0]!;
CompilerError.invariant(first.kind === "block", {
reason: "Expected first stack item to be a basic block",
CompilerError.invariant(first.kind === 'block', {
reason: 'Expected first stack item to be a basic block',
description: null,
loc: null,
suggestions: null,
@@ -161,7 +161,7 @@ class Builder {
function visitBlock(context: Context, block: ReactiveBlock): void {
for (const stmt of block) {
switch (stmt.kind) {
case "instruction": {
case 'instruction': {
context.visitId(stmt.instruction.id);
const scope = getInstructionScope(stmt.instruction);
if (scope !== null) {
@@ -170,12 +170,12 @@ function visitBlock(context: Context, block: ReactiveBlock): void {
context.append(stmt, null);
break;
}
case "terminal": {
case 'terminal': {
const id = stmt.terminal.id;
if (id !== null) {
context.visitId(id);
}
mapTerminalBlocks(stmt.terminal, (block) => {
mapTerminalBlocks(stmt.terminal, block => {
return context.enter(() => {
visitBlock(context, block);
});
@@ -183,10 +183,10 @@ function visitBlock(context: Context, block: ReactiveBlock): void {
context.append(stmt, stmt.label);
break;
}
case "pruned-scope":
case "scope": {
case 'pruned-scope':
case 'scope': {
CompilerError.invariant(false, {
reason: "Expected the function to not have scopes already assigned",
reason: 'Expected the function to not have scopes already assigned',
description: null,
loc: null,
suggestions: null,
@@ -195,7 +195,7 @@ function visitBlock(context: Context, block: ReactiveBlock): void {
default: {
assertExhaustive(
stmt,
`Unexpected statement kind \`${(stmt as any).kind}\``
`Unexpected statement kind \`${(stmt as any).kind}\``,
);
}
}
@@ -203,12 +203,12 @@ function visitBlock(context: Context, block: ReactiveBlock): void {
}
export function getInstructionScope(
instr: ReactiveInstruction
instr: ReactiveInstruction,
): ReactiveScope | null {
CompilerError.invariant(instr.lvalue !== null, {
reason:
"Expected lvalues to not be null when assigning scopes. " +
"Pruning lvalues too early can result in missing scope information.",
'Expected lvalues to not be null when assigning scopes. ' +
'Pruning lvalues too early can result in missing scope information.',
description: null,
loc: instr.loc,
suggestions: null,
@@ -230,7 +230,7 @@ export function getInstructionScope(
export function getPlaceScope(
id: InstructionId,
place: Place
place: Place,
): ReactiveScope | null {
const scope = place.identifier.scope;
if (scope !== null && isScopeActive(scope, id)) {
@@ -12,8 +12,8 @@ import {
PrunedReactiveScopeBlock,
ReactiveFunction,
isPrimitiveType,
} from "../HIR/HIR";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
} from '../HIR/HIR';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
class Visitor extends ReactiveFunctionVisitor<Set<IdentifierId>> {
/*
@@ -23,7 +23,7 @@ class Visitor extends ReactiveFunctionVisitor<Set<IdentifierId>> {
override visitLValue(
id: InstructionId,
lvalue: Place,
state: Set<IdentifierId>
state: Set<IdentifierId>,
): void {
this.visitPlace(id, lvalue, state);
}
@@ -36,7 +36,7 @@ class Visitor extends ReactiveFunctionVisitor<Set<IdentifierId>> {
override visitPlace(
_id: InstructionId,
place: Place,
state: Set<IdentifierId>
state: Set<IdentifierId>,
): void {
if (place.reactive) {
state.add(place.identifier.id);
@@ -45,7 +45,7 @@ class Visitor extends ReactiveFunctionVisitor<Set<IdentifierId>> {
override visitPrunedScope(
scopeBlock: PrunedReactiveScopeBlock,
state: Set<IdentifierId>
state: Set<IdentifierId>,
): void {
this.traversePrunedScope(scopeBlock, state);
@@ -62,7 +62,7 @@ class Visitor extends ReactiveFunctionVisitor<Set<IdentifierId>> {
* in `InferReactivePlaces`.
*/
export function collectReactiveIdentifiers(
fn: ReactiveFunction
fn: ReactiveFunction,
): Set<IdentifierId> {
const visitor = new Visitor();
const state = new Set<IdentifierId>();
@@ -5,9 +5,9 @@
* LICENSE file in the root directory of this source tree.
*/
import { visitReactiveFunction } from ".";
import { InstructionId, Place, ReactiveFunction, ReactiveValue } from "../HIR";
import { ReactiveFunctionVisitor } from "./visitors";
import {visitReactiveFunction} from '.';
import {InstructionId, Place, ReactiveFunction, ReactiveValue} from '../HIR';
import {ReactiveFunctionVisitor} from './visitors';
/**
* Returns a set of unique globals (by name) that are referenced transitively within the function.
@@ -22,12 +22,12 @@ class Visitor extends ReactiveFunctionVisitor<Set<string>> {
override visitValue(
id: InstructionId,
value: ReactiveValue,
state: Set<string>
state: Set<string>,
): void {
this.traverseValue(id, value, state);
if (value.kind === "FunctionExpression" || value.kind === "ObjectMethod") {
if (value.kind === 'FunctionExpression' || value.kind === 'ObjectMethod') {
this.visitHirFunction(value.loweredFunc.func, state);
} else if (value.kind === "LoadGlobal") {
} else if (value.kind === 'LoadGlobal') {
state.add(value.binding.name);
}
}
@@ -36,7 +36,7 @@ class Visitor extends ReactiveFunctionVisitor<Set<string>> {
_id: InstructionId,
_dependencies: Array<Place>,
fn: ReactiveFunction,
state: Set<string>
state: Set<string>,
): void {
visitReactiveFunction(fn, this, state);
}
@@ -5,10 +5,10 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "../CompilerError";
import { Identifier, ReactiveScopeDependency } from "../HIR";
import { printIdentifier } from "../HIR/PrintHIR";
import { assertExhaustive } from "../Utils/utils";
import {CompilerError} from '../CompilerError';
import {Identifier, ReactiveScopeDependency} from '../HIR';
import {printIdentifier} from '../HIR/PrintHIR';
import {assertExhaustive} from '../Utils/utils';
/*
* We need to understand optional member expressions only when determining
@@ -69,7 +69,7 @@ export class ReactiveScopeDependencyTree {
}
add(dep: ReactiveScopePropertyDependency, inConditional: boolean): void {
const { path, optionalPath } = dep;
const {path, optionalPath} = dep;
let currNode = this.#getOrCreateRoot(dep.identifier);
const accessType = inConditional
@@ -111,7 +111,7 @@ export class ReactiveScopeDependencyTree {
let currChild = getOrMakeProperty(currNode, property);
currChild.accessType = merge(
currChild.accessType,
PropertyAccessType.ConditionalAccess
PropertyAccessType.ConditionalAccess,
);
currNode = currChild;
}
@@ -119,7 +119,7 @@ export class ReactiveScopeDependencyTree {
// The final node should be marked as a conditional dependency.
currNode.accessType = merge(
currNode.accessType,
PropertyAccessType.ConditionalDependency
PropertyAccessType.ConditionalDependency,
);
}
}
@@ -130,15 +130,15 @@ export class ReactiveScopeDependencyTree {
const deps = deriveMinimalDependenciesInSubtree(rootNode);
CompilerError.invariant(
deps.every(
(dep) => dep.accessType === PropertyAccessType.UnconditionalDependency
dep => dep.accessType === PropertyAccessType.UnconditionalDependency,
),
{
reason:
"[PropagateScopeDependencies] All dependencies must be reduced to unconditional dependencies.",
'[PropagateScopeDependencies] All dependencies must be reduced to unconditional dependencies.',
description: null,
loc: null,
suggestions: null,
}
},
);
for (const dep of deps) {
@@ -155,10 +155,10 @@ export class ReactiveScopeDependencyTree {
addDepsFromInnerScope(
depsFromInnerScope: ReactiveScopeDependencyTree,
innerScopeInConditionalWithinParent: boolean,
checkValidDepIdFn: (dep: ReactiveScopeDependency) => boolean
checkValidDepIdFn: (dep: ReactiveScopeDependency) => boolean,
): void {
for (const [id, otherRoot] of depsFromInnerScope.#roots) {
if (!checkValidDepIdFn({ identifier: id, path: [] })) {
if (!checkValidDepIdFn({identifier: id, path: []})) {
continue;
}
let currRoot = this.#getOrCreateRoot(id);
@@ -172,17 +172,17 @@ export class ReactiveScopeDependencyTree {
}
promoteDepsFromExhaustiveConditionals(
trees: Array<ReactiveScopeDependencyTree>
trees: Array<ReactiveScopeDependencyTree>,
): void {
CompilerError.invariant(trees.length > 1, {
reason: "Expected trees to be at least 2 elements long.",
reason: 'Expected trees to be at least 2 elements long.',
description: null,
loc: null,
suggestions: null,
});
for (const [id, root] of this.#roots) {
const nodesForRootId = mapNonNull(trees, (tree) => {
const nodesForRootId = mapNonNull(trees, tree => {
const node = tree.#roots.get(id);
if (node != null && isUnconditional(node.accessType)) {
return node;
@@ -193,7 +193,7 @@ export class ReactiveScopeDependencyTree {
if (nodesForRootId) {
addSubtreeIntersection(
root.properties,
nodesForRootId.map((root) => root.properties)
nodesForRootId.map(root => root.properties),
);
}
}
@@ -209,11 +209,11 @@ export class ReactiveScopeDependencyTree {
for (const [rootId, rootNode] of this.#roots.entries()) {
const rootResults = printSubtree(rootNode, includeAccesses).map(
(result) => `${printIdentifier(rootId)}.${result}`
result => `${printIdentifier(rootId)}.${result}`,
);
res.push(rootResults);
}
return res.flat().join("\n");
return res.flat().join('\n');
}
}
@@ -237,10 +237,10 @@ export class ReactiveScopeDependencyTree {
* ```
*/
enum PropertyAccessType {
ConditionalAccess = "ConditionalAccess",
UnconditionalAccess = "UnconditionalAccess",
ConditionalDependency = "ConditionalDependency",
UnconditionalDependency = "UnconditionalDependency",
ConditionalAccess = 'ConditionalAccess',
UnconditionalAccess = 'UnconditionalAccess',
ConditionalDependency = 'ConditionalDependency',
UnconditionalDependency = 'UnconditionalDependency',
}
const MIN_ACCESS_TYPE = PropertyAccessType.ConditionalAccess;
@@ -259,7 +259,7 @@ function isDependency(access: PropertyAccessType): boolean {
function merge(
access1: PropertyAccessType,
access2: PropertyAccessType
access2: PropertyAccessType,
): PropertyAccessType {
const resultIsUnconditional =
isUnconditional(access1) || isUnconditional(access2);
@@ -318,17 +318,17 @@ const promoteCondResult = [
* @returns a minimal list of dependencies in this subtree.
*/
function deriveMinimalDependenciesInSubtree(
dep: DependencyNode
dep: DependencyNode,
): Array<ReduceResultNode> {
const results: Array<ReduceResultNode> = [];
for (const [childName, childNode] of dep.properties) {
const childResult = deriveMinimalDependenciesInSubtree(childNode).map(
({ relativePath, accessType }) => {
({relativePath, accessType}) => {
return {
relativePath: [childName, ...relativePath],
accessType,
};
}
},
);
results.push(...childResult);
}
@@ -340,8 +340,8 @@ function deriveMinimalDependenciesInSubtree(
case PropertyAccessType.UnconditionalAccess: {
if (
results.every(
({ accessType }) =>
accessType === PropertyAccessType.UnconditionalDependency
({accessType}) =>
accessType === PropertyAccessType.UnconditionalDependency,
)
) {
// all children are unconditional dependencies, return them to preserve granularity
@@ -358,8 +358,8 @@ function deriveMinimalDependenciesInSubtree(
case PropertyAccessType.ConditionalDependency: {
if (
results.every(
({ accessType }) =>
accessType === PropertyAccessType.ConditionalDependency
({accessType}) =>
accessType === PropertyAccessType.ConditionalDependency,
)
) {
/*
@@ -379,7 +379,7 @@ function deriveMinimalDependenciesInSubtree(
default: {
assertExhaustive(
dep.accessType,
"[PropgateScopeDependencies] Unhandled access type!"
'[PropgateScopeDependencies] Unhandled access type!',
);
}
}
@@ -395,7 +395,7 @@ function demoteSubtreeToConditional(subtree: DependencyNode): void {
let node;
while ((node = stack.pop()) !== undefined) {
const { accessType, properties } = node;
const {accessType, properties} = node;
if (!isUnconditional(accessType)) {
// A conditionally accessed node should not have unconditional children
continue;
@@ -435,7 +435,7 @@ function demoteSubtreeToConditional(subtree: DependencyNode): void {
function addSubtree(
currNode: DependencyNode,
otherNode: DependencyNode,
demoteOtherNode: boolean
demoteOtherNode: boolean,
): void {
let otherType = otherNode.accessType;
if (demoteOtherNode) {
@@ -488,11 +488,11 @@ function addSubtree(
*/
function addSubtreeIntersection(
currProperties: Map<string, DependencyNode>,
otherProperties: Array<Map<string, DependencyNode>>
otherProperties: Array<Map<string, DependencyNode>>,
): void {
CompilerError.invariant(otherProperties.length > 1, {
reason:
"[DeriveMinimalDependencies] Expected otherProperties to be at least 2 elements long.",
'[DeriveMinimalDependencies] Expected otherProperties to be at least 2 elements long.',
description: null,
loc: null,
suggestions: null,
@@ -506,7 +506,7 @@ function addSubtreeIntersection(
*/
for (const [propertyName, currNode] of currProperties) {
const otherNodes = mapNonNull(otherProperties, (properties) => {
const otherNodes = mapNonNull(otherProperties, properties => {
const node = properties.get(propertyName);
if (node != null && isUnconditional(node.accessType)) {
return node;
@@ -522,10 +522,10 @@ function addSubtreeIntersection(
if (otherNodes) {
addSubtreeIntersection(
currNode.properties,
otherNodes.map((node) => node.properties)
otherNodes.map(node => node.properties),
);
const isDep = otherNodes.some((tree) => isDependency(tree.accessType));
const isDep = otherNodes.some(tree => isDependency(tree.accessType));
const externalAccessType = isDep
? PropertyAccessType.UnconditionalDependency
: PropertyAccessType.UnconditionalAccess;
@@ -536,7 +536,7 @@ function addSubtreeIntersection(
function printSubtree(
node: DependencyNode,
includeAccesses: boolean
includeAccesses: boolean,
): Array<string> {
const results: Array<string> = [];
for (const [propertyName, propertyNode] of node.properties) {
@@ -544,16 +544,14 @@ function printSubtree(
results.push(`${propertyName} (${propertyNode.accessType})`);
}
const propertyResults = printSubtree(propertyNode, includeAccesses);
results.push(
...propertyResults.map((result) => `${propertyName}.${result}`)
);
results.push(...propertyResults.map(result => `${propertyName}.${result}`));
}
return results;
}
function getOrMakeProperty(
node: DependencyNode,
property: string
property: string,
): DependencyNode {
let child = node.properties.get(property);
if (child == null) {
@@ -568,7 +566,7 @@ function getOrMakeProperty(
function mapNonNull<T extends NonNullable<V>, V, U>(
arr: Array<U>,
fn: (arg0: U) => T | undefined | null
fn: (arg0: U) => T | undefined | null,
): Array<T> | null {
const result = [];
for (let i = 0; i < arr.length; i++) {
@@ -16,13 +16,13 @@ import {
ReactiveScopeBlock,
ReactiveStatement,
promoteTemporary,
} from "../HIR";
import { eachPatternOperand, mapPatternOperands } from "../HIR/visitors";
} from '../HIR';
import {eachPatternOperand, mapPatternOperands} from '../HIR/visitors';
import {
ReactiveFunctionTransform,
Transformed,
visitReactiveFunction,
} from "./visitors";
} from './visitors';
/*
* Destructuring statements may sometimes define some variables which are declared by the scope,
@@ -74,7 +74,7 @@ import {
*
*/
export function extractScopeDeclarationsFromDestructuring(
fn: ReactiveFunction
fn: ReactiveFunction,
): void {
const state = new State(fn.env);
visitReactiveFunction(fn, new Visitor(), state);
@@ -99,34 +99,34 @@ class Visitor extends ReactiveFunctionTransform<State> {
override transformInstruction(
instruction: ReactiveInstruction,
state: State
state: State,
): Transformed<ReactiveStatement> {
this.visitInstruction(instruction, state);
if (instruction.value.kind === "Destructure") {
if (instruction.value.kind === 'Destructure') {
const transformed = transformDestructuring(
state,
instruction,
instruction.value
instruction.value,
);
if (transformed) {
return {
kind: "replace-many",
value: transformed.map((instruction) => ({
kind: "instruction",
kind: 'replace-many',
value: transformed.map(instruction => ({
kind: 'instruction',
instruction,
})),
};
}
}
return { kind: "keep" };
return {kind: 'keep'};
}
}
function transformDestructuring(
state: State,
instr: ReactiveInstruction,
destructure: Destructure
destructure: Destructure,
): null | Array<ReactiveInstruction> {
let reassigned: Set<IdentifierId> = new Set();
let hasDeclaration = false;
@@ -146,7 +146,7 @@ function transformDestructuring(
*/
const instructions: Array<ReactiveInstruction> = [];
const renamed: Map<Place, Place> = new Map();
mapPatternOperands(destructure.lvalue.pattern, (place) => {
mapPatternOperands(destructure.lvalue.pattern, place => {
if (!reassigned.has(place.identifier.id)) {
return place;
}
@@ -169,7 +169,7 @@ function transformDestructuring(
id: instr.id,
lvalue: null,
value: {
kind: "StoreLocal",
kind: 'StoreLocal',
lvalue: {
kind: InstructionKind.Reassign,
place: original,
@@ -11,13 +11,13 @@ import {
ReactiveStatement,
ReactiveTerminal,
ReactiveTerminalStatement,
} from "../HIR/HIR";
import { assertExhaustive } from "../Utils/utils";
} from '../HIR/HIR';
import {assertExhaustive} from '../Utils/utils';
import {
ReactiveFunctionTransform,
Transformed,
visitReactiveFunction,
} from "./visitors";
} from './visitors';
/*
* Given a reactive function, flattens any scopes contained within a loop construct.
@@ -30,53 +30,53 @@ export function flattenReactiveLoops(fn: ReactiveFunction): void {
class Transform extends ReactiveFunctionTransform<boolean> {
override transformScope(
scope: ReactiveScopeBlock,
isWithinLoop: boolean
isWithinLoop: boolean,
): Transformed<ReactiveStatement> {
this.visitScope(scope, isWithinLoop);
if (isWithinLoop) {
return {
kind: "replace",
kind: 'replace',
value: {
kind: "pruned-scope",
kind: 'pruned-scope',
scope: scope.scope,
instructions: scope.instructions,
},
};
} else {
return { kind: "keep" };
return {kind: 'keep'};
}
}
override visitTerminal(
stmt: ReactiveTerminalStatement<ReactiveTerminal>,
isWithinLoop: boolean
isWithinLoop: boolean,
): void {
switch (stmt.terminal.kind) {
// Loop terminals flatten nested scopes
case "do-while":
case "while":
case "for":
case "for-of":
case "for-in": {
case 'do-while':
case 'while':
case 'for':
case 'for-of':
case 'for-in': {
this.traverseTerminal(stmt, true);
break;
}
// Non-loop terminals passthrough is contextual, inherits the parent isWithinScope
case "try":
case "label":
case "break":
case "continue":
case "if":
case "return":
case "switch":
case "throw": {
case 'try':
case 'label':
case 'break':
case 'continue':
case 'if':
case 'return':
case 'switch':
case 'throw': {
this.traverseTerminal(stmt, isWithinLoop);
break;
}
default: {
assertExhaustive(
stmt.terminal,
`Unexpected terminal kind \`${(stmt.terminal as any).kind}\``
`Unexpected terminal kind \`${(stmt.terminal as any).kind}\``,
);
}
}
@@ -5,8 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/
import { BlockId, HIRFunction, PrunedScopeTerminal } from "../HIR";
import { assertExhaustive, retainWhere } from "../Utils/utils";
import {BlockId, HIRFunction, PrunedScopeTerminal} from '../HIR';
import {assertExhaustive, retainWhere} from '../Utils/utils';
/**
* Prunes any reactive scopes that are within a loop (for, while, etc). We don't yet
@@ -18,21 +18,21 @@ import { assertExhaustive, retainWhere } from "../Utils/utils";
export function flattenReactiveLoopsHIR(fn: HIRFunction): void {
const activeLoops = Array<BlockId>();
for (const [, block] of fn.body.blocks) {
retainWhere(activeLoops, (id) => id !== block.id);
const { terminal } = block;
retainWhere(activeLoops, id => id !== block.id);
const {terminal} = block;
switch (terminal.kind) {
case "do-while":
case "for":
case "for-in":
case "for-of":
case "while": {
case 'do-while':
case 'for':
case 'for-in':
case 'for-of':
case 'while': {
activeLoops.push(terminal.fallthrough);
break;
}
case "scope": {
case 'scope': {
if (activeLoops.length !== 0) {
block.terminal = {
kind: "pruned-scope",
kind: 'pruned-scope',
block: terminal.block,
fallthrough: terminal.fallthrough,
id: terminal.id,
@@ -42,28 +42,28 @@ export function flattenReactiveLoopsHIR(fn: HIRFunction): void {
}
break;
}
case "branch":
case "goto":
case "if":
case "label":
case "logical":
case "maybe-throw":
case "optional":
case "pruned-scope":
case "return":
case "sequence":
case "switch":
case "ternary":
case "throw":
case "try":
case "unreachable":
case "unsupported": {
case 'branch':
case 'goto':
case 'if':
case 'label':
case 'logical':
case 'maybe-throw':
case 'optional':
case 'pruned-scope':
case 'return':
case 'sequence':
case 'switch':
case 'ternary':
case 'throw':
case 'try':
case 'unreachable':
case 'unsupported': {
break;
}
default: {
assertExhaustive(
terminal,
`Unexpected terminal kind \`${(terminal as any).kind}\``
`Unexpected terminal kind \`${(terminal as any).kind}\``,
);
}
}
@@ -14,12 +14,12 @@ import {
ReactiveValue,
getHookKind,
isUseOperator,
} from "../HIR";
} from '../HIR';
import {
ReactiveFunctionTransform,
Transformed,
visitReactiveFunction,
} from "./visitors";
} from './visitors';
/**
* For simplicity the majority of compiler passes do not treat hooks specially. However, hooks are different
@@ -57,7 +57,7 @@ type State = {
class Transform extends ReactiveFunctionTransform<State> {
override transformScope(
scope: ReactiveScopeBlock,
outerState: State
outerState: State,
): Transformed<ReactiveStatement> {
const innerState: State = {
env: outerState.env,
@@ -72,7 +72,7 @@ class Transform extends ReactiveFunctionTransform<State> {
* flatten it away
*/
return {
kind: "replace-many",
kind: 'replace-many',
value: scope.instructions,
};
}
@@ -81,26 +81,26 @@ class Transform extends ReactiveFunctionTransform<State> {
* mark it as pruned
*/
return {
kind: "replace",
kind: 'replace',
value: {
kind: "pruned-scope",
kind: 'pruned-scope',
scope: scope.scope,
instructions: scope.instructions,
},
};
} else {
return { kind: "keep" };
return {kind: 'keep'};
}
}
override visitValue(
id: InstructionId,
value: ReactiveValue,
state: State
state: State,
): void {
this.traverseValue(id, value, state);
switch (value.kind) {
case "CallExpression": {
case 'CallExpression': {
if (
getHookKind(state.env, value.callee.identifier) != null ||
isUseOperator(value.callee.identifier)
@@ -109,7 +109,7 @@ class Transform extends ReactiveFunctionTransform<State> {
}
break;
}
case "MethodCall": {
case 'MethodCall': {
if (
getHookKind(state.env, value.property.identifier) != null ||
isUseOperator(value.property.identifier)
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "..";
import {CompilerError} from '..';
import {
BlockId,
HIRFunction,
@@ -14,8 +14,8 @@ import {
ReactiveScope,
getHookKind,
isUseOperator,
} from "../HIR";
import { retainWhere } from "../Utils/utils";
} from '../HIR';
import {retainWhere} from '../Utils/utils';
/**
* For simplicity the majority of compiler passes do not treat hooks specially. However, hooks are different
@@ -39,31 +39,31 @@ import { retainWhere } from "../Utils/utils";
* to ensure the hook call does not inadvertently become conditional.
*/
export function flattenScopesWithHooksOrUseHIR(fn: HIRFunction): void {
const activeScopes: Array<{ block: BlockId; scope: ReactiveScope }> = [];
const activeScopes: Array<{block: BlockId; scope: ReactiveScope}> = [];
const prune: Array<BlockId> = [];
for (const [, block] of fn.body.blocks) {
const firstId = block.instructions[0]?.id ?? block.terminal.id;
retainWhere(activeScopes, (current) => current.scope.range.end > firstId);
retainWhere(activeScopes, current => current.scope.range.end > firstId);
for (const instr of block.instructions) {
const { value } = instr;
const {value} = instr;
switch (value.kind) {
case "MethodCall":
case "CallExpression": {
case 'MethodCall':
case 'CallExpression': {
const callee =
value.kind === "MethodCall" ? value.property : value.callee;
value.kind === 'MethodCall' ? value.property : value.callee;
if (
getHookKind(fn.env, callee.identifier) != null ||
isUseOperator(callee.identifier)
) {
prune.push(...activeScopes.map((entry) => entry.block));
prune.push(...activeScopes.map(entry => entry.block));
activeScopes.length = 0;
}
}
}
}
if (block.terminal.kind === "scope") {
if (block.terminal.kind === 'scope') {
activeScopes.push({
block: block.id,
scope: block.terminal.scope,
@@ -74,7 +74,7 @@ export function flattenScopesWithHooksOrUseHIR(fn: HIRFunction): void {
for (const id of prune) {
const block = fn.body.blocks.get(id)!;
const terminal = block.terminal;
CompilerError.invariant(terminal.kind === "scope", {
CompilerError.invariant(terminal.kind === 'scope', {
reason: `Expected block to have a scope terminal`,
description: `Expected block bb${block.id} to end in a scope terminal`,
loc: terminal.loc,
@@ -82,7 +82,7 @@ export function flattenScopesWithHooksOrUseHIR(fn: HIRFunction): void {
const body = fn.body.blocks.get(terminal.block)!;
if (
body.instructions.length === 1 &&
body.terminal.kind === "goto" &&
body.terminal.kind === 'goto' &&
body.terminal.block === terminal.fallthrough
) {
/*
@@ -91,7 +91,7 @@ export function flattenScopesWithHooksOrUseHIR(fn: HIRFunction): void {
* flattening
*/
block.terminal = {
kind: "label",
kind: 'label',
block: terminal.block,
fallthrough: terminal.fallthrough,
id: terminal.id,
@@ -101,7 +101,7 @@ export function flattenScopesWithHooksOrUseHIR(fn: HIRFunction): void {
}
block.terminal = {
kind: "pruned-scope",
kind: 'pruned-scope',
block: terminal.block,
fallthrough: terminal.fallthrough,
id: terminal.id,
@@ -5,8 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError, SourceLocation } from "..";
import { Environment } from "../HIR";
import {CompilerError, SourceLocation} from '..';
import {Environment} from '../HIR';
import {
GeneratedSource,
HIRFunction,
@@ -15,15 +15,15 @@ import {
Place,
ReactiveScope,
makeInstructionId,
} from "../HIR/HIR";
} from '../HIR/HIR';
import {
doesPatternContainSpreadElement,
eachInstructionOperand,
eachPatternOperand,
} from "../HIR/visitors";
import DisjointSet from "../Utils/DisjointSet";
import { logHIRFunction } from "../Utils/logger";
import { assertExhaustive } from "../Utils/utils";
} from '../HIR/visitors';
import DisjointSet from '../Utils/DisjointSet';
import {logHIRFunction} from '../Utils/logger';
import {assertExhaustive} from '../Utils/utils';
/*
* Note: this is the 1st of 4 passes that determine how to break a function into discrete
@@ -118,11 +118,11 @@ export function inferReactiveScopeVariables(fn: HIRFunction): void {
scope.range.start = identifier.mutableRange.start;
} else if (identifier.mutableRange.start !== 0) {
scope.range.start = makeInstructionId(
Math.min(scope.range.start, identifier.mutableRange.start)
Math.min(scope.range.start, identifier.mutableRange.start),
);
}
scope.range.end = makeInstructionId(
Math.max(scope.range.end, identifier.mutableRange.end)
Math.max(scope.range.end, identifier.mutableRange.end),
);
scope.loc = mergeLocation(scope.loc, identifier.loc);
}
@@ -136,7 +136,7 @@ export function inferReactiveScopeVariables(fn: HIRFunction): void {
maxInstruction = makeInstructionId(Math.max(maxInstruction, instr.id));
}
maxInstruction = makeInstructionId(
Math.max(maxInstruction, block.terminal.id)
Math.max(maxInstruction, block.terminal.id),
);
}
@@ -153,7 +153,7 @@ export function inferReactiveScopeVariables(fn: HIRFunction): void {
scope.range.end > maxInstruction + 1
) {
// Make it easier to debug why the error occurred
logHIRFunction("InferReactiveScopeVariables (invalid scope)", fn);
logHIRFunction('InferReactiveScopeVariables (invalid scope)', fn);
CompilerError.invariant(false, {
reason: `Invalid mutable range for scope`,
loc: GeneratedSource,
@@ -185,76 +185,76 @@ function mergeLocation(l: SourceLocation, r: SourceLocation): SourceLocation {
}
// Is the operand mutable at this given instruction
export function isMutable({ id }: Instruction, place: Place): boolean {
export function isMutable({id}: Instruction, place: Place): boolean {
const range = place.identifier.mutableRange;
return id >= range.start && id < range.end;
}
function mayAllocate(env: Environment, instruction: Instruction): boolean {
const { value } = instruction;
const {value} = instruction;
switch (value.kind) {
case "Destructure": {
case 'Destructure': {
return doesPatternContainSpreadElement(value.lvalue.pattern);
}
case "PostfixUpdate":
case "PrefixUpdate":
case "Await":
case "DeclareLocal":
case "DeclareContext":
case "StoreLocal":
case "LoadGlobal":
case "MetaProperty":
case "TypeCastExpression":
case "LoadLocal":
case "LoadContext":
case "StoreContext":
case "PropertyDelete":
case "ComputedLoad":
case "ComputedDelete":
case "JSXText":
case "TemplateLiteral":
case "Primitive":
case "GetIterator":
case "IteratorNext":
case "NextPropertyOf":
case "Debugger":
case "StartMemoize":
case "FinishMemoize":
case "UnaryExpression":
case "BinaryExpression":
case "PropertyLoad":
case "StoreGlobal": {
case 'PostfixUpdate':
case 'PrefixUpdate':
case 'Await':
case 'DeclareLocal':
case 'DeclareContext':
case 'StoreLocal':
case 'LoadGlobal':
case 'MetaProperty':
case 'TypeCastExpression':
case 'LoadLocal':
case 'LoadContext':
case 'StoreContext':
case 'PropertyDelete':
case 'ComputedLoad':
case 'ComputedDelete':
case 'JSXText':
case 'TemplateLiteral':
case 'Primitive':
case 'GetIterator':
case 'IteratorNext':
case 'NextPropertyOf':
case 'Debugger':
case 'StartMemoize':
case 'FinishMemoize':
case 'UnaryExpression':
case 'BinaryExpression':
case 'PropertyLoad':
case 'StoreGlobal': {
return false;
}
case "CallExpression":
case "MethodCall": {
return instruction.lvalue.identifier.type.kind !== "Primitive";
case 'CallExpression':
case 'MethodCall': {
return instruction.lvalue.identifier.type.kind !== 'Primitive';
}
case "RegExpLiteral":
case "PropertyStore":
case "ComputedStore":
case "ArrayExpression":
case "JsxExpression":
case "JsxFragment":
case "NewExpression":
case "ObjectExpression":
case "UnsupportedNode":
case "ObjectMethod":
case "FunctionExpression":
case "TaggedTemplateExpression": {
case 'RegExpLiteral':
case 'PropertyStore':
case 'ComputedStore':
case 'ArrayExpression':
case 'JsxExpression':
case 'JsxFragment':
case 'NewExpression':
case 'ObjectExpression':
case 'UnsupportedNode':
case 'ObjectMethod':
case 'FunctionExpression':
case 'TaggedTemplateExpression': {
return true;
}
default: {
assertExhaustive(
value,
`Unexpected value kind \`${(value as any).kind}\``
`Unexpected value kind \`${(value as any).kind}\``,
);
}
}
}
export function findDisjointMutableValues(
fn: HIRFunction
fn: HIRFunction,
): DisjointSet<Identifier> {
const scopeIdentifiers = new DisjointSet<Identifier>();
for (const [_, block] of fn.body.blocks) {
@@ -286,8 +286,8 @@ export function findDisjointMutableValues(
operands.push(instr.lvalue!.identifier);
}
if (
instr.value.kind === "StoreLocal" ||
instr.value.kind === "StoreContext"
instr.value.kind === 'StoreLocal' ||
instr.value.kind === 'StoreContext'
) {
if (
instr.value.lvalue.place.identifier.mutableRange.end >
@@ -301,7 +301,7 @@ export function findDisjointMutableValues(
) {
operands.push(instr.value.value.identifier);
}
} else if (instr.value.kind === "Destructure") {
} else if (instr.value.kind === 'Destructure') {
for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
if (
place.identifier.mutableRange.end >
@@ -316,7 +316,7 @@ export function findDisjointMutableValues(
) {
operands.push(instr.value.value.identifier);
}
} else if (instr.value.kind === "MethodCall") {
} else if (instr.value.kind === 'MethodCall') {
for (const operand of eachInstructionOperand(instr)) {
if (
isMutable(instr, operand) &&
@@ -11,8 +11,8 @@ import {
makeInstructionId,
Place,
ReactiveValue,
} from "../HIR";
import { eachReactiveValueOperand } from "./visitors";
} from '../HIR';
import {eachReactiveValueOperand} from './visitors';
/**
* This pass supports the `fbt` translation system (https://facebook.github.io/fbt/)
@@ -40,7 +40,7 @@ import { eachReactiveValueOperand } from "./visitors";
* `customMacros` environment configuration.
*/
export function memoizeFbtAndMacroOperandsInSameScope(
fn: HIRFunction
fn: HIRFunction,
): Set<IdentifierId> {
const fbtMacroTags = new Set([
...FBT_TAGS,
@@ -58,30 +58,30 @@ export function memoizeFbtAndMacroOperandsInSameScope(
}
export const FBT_TAGS: Set<string> = new Set([
"fbt",
"fbt:param",
"fbs",
"fbs:param",
'fbt',
'fbt:param',
'fbs',
'fbs:param',
]);
export const SINGLE_CHILD_FBT_TAGS: Set<string> = new Set([
"fbt:param",
"fbs:param",
'fbt:param',
'fbs:param',
]);
function visit(
fn: HIRFunction,
fbtMacroTags: Set<string>,
fbtValues: Set<IdentifierId>
fbtValues: Set<IdentifierId>,
): void {
for (const [, block] of fn.body.blocks) {
for (const instruction of block.instructions) {
const { lvalue, value } = instruction;
const {lvalue, value} = instruction;
if (lvalue === null) {
continue;
}
if (
value.kind === "Primitive" &&
typeof value.value === "string" &&
value.kind === 'Primitive' &&
typeof value.value === 'string' &&
fbtMacroTags.has(value.value)
) {
/*
@@ -90,7 +90,7 @@ function visit(
*/
fbtValues.add(lvalue.identifier.id);
} else if (
value.kind === "LoadGlobal" &&
value.kind === 'LoadGlobal' &&
fbtMacroTags.has(value.binding.name)
) {
// Record references to `fbt` as a global
@@ -113,8 +113,8 @@ function visit(
fbtScope.range.start = makeInstructionId(
Math.min(
fbtScope.range.start,
operand.identifier.mutableRange.start
)
operand.identifier.mutableRange.start,
),
);
fbtValues.add(operand.identifier.id);
}
@@ -139,8 +139,8 @@ function visit(
fbtScope.range.start = makeInstructionId(
Math.min(
fbtScope.range.start,
operand.identifier.mutableRange.start
)
operand.identifier.mutableRange.start,
),
);
/*
@@ -158,7 +158,7 @@ function visit(
for (const operand of eachReactiveValueOperand(value)) {
if (
operand.identifier.name !== null &&
operand.identifier.name.kind === "named"
operand.identifier.name.kind === 'named'
) {
/*
* named identifiers were already locals, we only have to force temporaries
@@ -172,8 +172,8 @@ function visit(
fbtScope.range.start = makeInstructionId(
Math.min(
fbtScope.range.start,
operand.identifier.mutableRange.start
)
operand.identifier.mutableRange.start,
),
);
}
}
@@ -183,33 +183,33 @@ function visit(
function isFbtCallExpression(
fbtValues: Set<IdentifierId>,
value: ReactiveValue
value: ReactiveValue,
): boolean {
return (
value.kind === "CallExpression" && fbtValues.has(value.callee.identifier.id)
value.kind === 'CallExpression' && fbtValues.has(value.callee.identifier.id)
);
}
function isFbtJsxExpression(
fbtMacroTags: Set<string>,
fbtValues: Set<IdentifierId>,
value: ReactiveValue
value: ReactiveValue,
): boolean {
return (
value.kind === "JsxExpression" &&
((value.tag.kind === "Identifier" &&
value.kind === 'JsxExpression' &&
((value.tag.kind === 'Identifier' &&
fbtValues.has(value.tag.identifier.id)) ||
(value.tag.kind === "BuiltinTag" && fbtMacroTags.has(value.tag.name)))
(value.tag.kind === 'BuiltinTag' && fbtMacroTags.has(value.tag.name)))
);
}
function isFbtJsxChild(
fbtValues: Set<IdentifierId>,
lvalue: Place | null,
value: ReactiveValue
value: ReactiveValue,
): boolean {
return (
(value.kind === "JsxExpression" || value.kind === "JsxFragment") &&
(value.kind === 'JsxExpression' || value.kind === 'JsxFragment') &&
lvalue !== null &&
fbtValues.has(lvalue.identifier.id)
);
@@ -14,11 +14,11 @@ import {
ReactiveInstruction,
ReactiveScope,
ScopeId,
} from "../HIR";
import DisjointSet from "../Utils/DisjointSet";
import { retainWhere } from "../Utils/utils";
import { getPlaceScope } from "./BuildReactiveBlocks";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
} from '../HIR';
import DisjointSet from '../Utils/DisjointSet';
import {retainWhere} from '../Utils/utils';
import {getPlaceScope} from './BuildReactiveBlocks';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
/*
* Note: this is the 3rd of 4 passes that determine how to break a function into discrete
@@ -120,12 +120,12 @@ class Visitor extends ReactiveFunctionVisitor<Context> {
}
override visitInstruction(
instruction: ReactiveInstruction,
state: Context
state: Context,
): void {
if (
instruction.value.kind === "ConditionalExpression" ||
instruction.value.kind === "LogicalExpression" ||
instruction.value.kind === "OptionalExpression"
instruction.value.kind === 'ConditionalExpression' ||
instruction.value.kind === 'LogicalExpression' ||
instruction.value.kind === 'OptionalExpression'
) {
state.enter(() => {
super.visitInstruction(instruction, state);
@@ -154,7 +154,7 @@ class Context {
visitId(id: InstructionId): void {
const currentBlock = this.scopes[this.scopes.length - 1]!;
retainWhere(currentBlock.scopes, (pending) => {
retainWhere(currentBlock.scopes, pending => {
if (pending.scope.range.end > id) {
return true;
} else {
@@ -175,7 +175,7 @@ class Context {
if (!this.seenScopes.has(scope.id)) {
this.seenScopes.add(scope.id);
currentBlock.seen.add(scope.id);
currentBlock.scopes.push({ shadowedBy: null, scope });
currentBlock.scopes.push({shadowedBy: null, scope});
return;
}
// Scope has already been seen, find it in the current block or a parent
@@ -186,7 +186,7 @@ class Context {
* scopes that cross control-flow boundaries are merged with overlapping
* scopes
*/
this.joinedScopes.union([scope, ...nextBlock.scopes.map((s) => s.scope)]);
this.joinedScopes.union([scope, ...nextBlock.scopes.map(s => s.scope)]);
index--;
if (index < 0) {
/*
@@ -213,7 +213,7 @@ class Context {
* }
*/
currentBlock.seen.add(scope.id);
currentBlock.scopes.push({ shadowedBy: null, scope });
currentBlock.scopes.push({shadowedBy: null, scope});
return;
}
nextBlock = this.scopes[index]!;
@@ -240,7 +240,7 @@ class Context {
* a scope relative to its eventual post-merge mutable range
*/
const end = makeInstructionId(
Math.max(current.scope.range.end, scope.range.end)
Math.max(current.scope.range.end, scope.range.end),
);
current.scope.range.end = end;
scope.range.end = end;
@@ -250,7 +250,7 @@ class Context {
}
if (!currentBlock.seen.has(scope.id)) {
currentBlock.seen.add(scope.id);
currentBlock.scopes.push({ shadowedBy: null, scope });
currentBlock.scopes.push({shadowedBy: null, scope});
}
}
@@ -264,10 +264,10 @@ class Context {
this.joinedScopes.forEach((scope, groupScope) => {
if (scope !== groupScope) {
groupScope.range.start = makeInstructionId(
Math.min(groupScope.range.start, scope.range.start)
Math.min(groupScope.range.start, scope.range.start),
);
groupScope.range.end = makeInstructionId(
Math.max(groupScope.range.end, scope.range.end)
Math.max(groupScope.range.end, scope.range.end),
);
}
});
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "..";
import {CompilerError} from '..';
import {
IdentifierId,
InstructionId,
@@ -20,22 +20,22 @@ import {
ReactiveStatement,
Type,
makeInstructionId,
} from "../HIR";
} from '../HIR';
import {
BuiltInArrayId,
BuiltInFunctionId,
BuiltInJsxId,
BuiltInObjectId,
} from "../HIR/ObjectShape";
import { eachInstructionLValue } from "../HIR/visitors";
import { assertExhaustive } from "../Utils/utils";
import { printReactiveScopeSummary } from "./PrintReactiveFunction";
} from '../HIR/ObjectShape';
import {eachInstructionLValue} from '../HIR/visitors';
import {assertExhaustive} from '../Utils/utils';
import {printReactiveScopeSummary} from './PrintReactiveFunction';
import {
ReactiveFunctionTransform,
ReactiveFunctionVisitor,
Transformed,
visitReactiveFunction,
} from "./visitors";
} from './visitors';
/*
* The primary goal of this pass is to reduce memoization overhead, specifically:
@@ -82,7 +82,7 @@ import {
* better to flatten.
*/
export function mergeReactiveScopesThatInvalidateTogether(
fn: ReactiveFunction
fn: ReactiveFunction,
): void {
const lastUsageVisitor = new FindLastUsageVisitor();
visitReactiveFunction(fn, lastUsageVisitor, undefined);
@@ -119,22 +119,22 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
override transformScope(
scopeBlock: ReactiveScopeBlock,
state: ReactiveScopeDependencies | null
state: ReactiveScopeDependencies | null,
): Transformed<ReactiveStatement> {
this.visitScope(scopeBlock, scopeBlock.scope.dependencies);
if (
state !== null &&
areEqualDependencies(state, scopeBlock.scope.dependencies)
) {
return { kind: "replace-many", value: scopeBlock.instructions };
return {kind: 'replace-many', value: scopeBlock.instructions};
} else {
return { kind: "keep" };
return {kind: 'keep'};
}
}
override visitBlock(
block: ReactiveBlock,
state: ReactiveScopeDependencies | null
state: ReactiveScopeDependencies | null,
): void {
// Pass 1: visit nested blocks to potentially merge their scopes
this.traverseBlock(block, state);
@@ -152,7 +152,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
CompilerError.invariant(current !== null, {
loc: null,
reason:
"MergeConsecutiveScopes: expected current scope to be non-null if reset()",
'MergeConsecutiveScopes: expected current scope to be non-null if reset()',
suggestions: null,
description: null,
});
@@ -164,37 +164,37 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
for (let i = 0; i < block.length; i++) {
const instr = block[i]!;
switch (instr.kind) {
case "terminal": {
case 'terminal': {
// For now we don't merge across terminals
if (current !== null) {
log(
`Reset scope @${current.block.scope.id} from terminal [${instr.terminal.id}]`
`Reset scope @${current.block.scope.id} from terminal [${instr.terminal.id}]`,
);
reset();
}
break;
}
case "pruned-scope": {
case 'pruned-scope': {
// For now we don't merge across pruned scopes
if (current !== null) {
log(
`Reset scope @${current.block.scope.id} from pruned scope @${instr.scope.id}`
`Reset scope @${current.block.scope.id} from pruned scope @${instr.scope.id}`,
);
reset();
}
break;
}
case "instruction": {
case 'instruction': {
switch (instr.instruction.value.kind) {
case "BinaryExpression":
case "ComputedLoad":
case "JSXText":
case "LoadGlobal":
case "LoadLocal":
case "Primitive":
case "PropertyLoad":
case "TemplateLiteral":
case "UnaryExpression": {
case 'BinaryExpression':
case 'ComputedLoad':
case 'JSXText':
case 'LoadGlobal':
case 'LoadLocal':
case 'Primitive':
case 'PropertyLoad':
case 'TemplateLiteral':
case 'UnaryExpression': {
/*
* We can merge two scopes if there are intervening instructions, but:
* - Only if the instructions are simple and it's okay to make them
@@ -208,7 +208,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
}
break;
}
case "StoreLocal": {
case 'StoreLocal': {
/**
* It's safe to have intervening StoreLocal instructions _if_ they are const
* and the last usage of the variable is at or before the next scope. This is
@@ -222,13 +222,13 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
instr.instruction.value.lvalue.kind === InstructionKind.Const
) {
for (const lvalue of eachInstructionLValue(
instr.instruction
instr.instruction,
)) {
current.lvalues.add(lvalue.identifier.id);
}
} else {
log(
`Reset scope @${current.block.scope.id} from StoreLocal in [${instr.instruction.id}]`
`Reset scope @${current.block.scope.id} from StoreLocal in [${instr.instruction.id}]`,
);
reset();
}
@@ -239,7 +239,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
// Other instructions are known to prevent merging, so we reset the scope if present
if (current !== null) {
log(
`Reset scope @${current.block.scope.id} from instruction [${instr.instruction.id}]`
`Reset scope @${current.block.scope.id} from instruction [${instr.instruction.id}]`,
);
reset();
}
@@ -247,23 +247,23 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
}
break;
}
case "scope": {
case 'scope': {
if (
current !== null &&
canMergeScopes(current.block, instr) &&
areLValuesLastUsedByScope(
instr.scope,
current.lvalues,
this.lastUsage
this.lastUsage,
)
) {
// The current and next scopes can merge!
log(
`Can merge scope @${current.block.scope.id} with @${instr.scope.id}`
`Can merge scope @${current.block.scope.id} with @${instr.scope.id}`,
);
// Update the merged scope's range
current.block.scope.range.end = makeInstructionId(
Math.max(current.block.scope.range.end, instr.scope.range.end)
Math.max(current.block.scope.range.end, instr.scope.range.end),
);
// Add declarations
for (const [key, value] of instr.scope.declarations) {
@@ -287,7 +287,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
* inputs change, so it is not a candidate for future merging
*/
log(
` but scope @${instr.scope.id} doesnt guaranteed invalidate so it cannot merge further`
` but scope @${instr.scope.id} doesnt guaranteed invalidate so it cannot merge further`,
);
reset();
}
@@ -296,7 +296,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
if (current !== null) {
// Reset if necessary
log(
`Reset scope @${current.block.scope.id}, not mergeable with subsequent scope @${instr.scope.id}`
`Reset scope @${current.block.scope.id}, not mergeable with subsequent scope @${instr.scope.id}`,
);
reset();
}
@@ -310,7 +310,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
};
} else {
log(
`scope @${instr.scope.id} doesnt guaranteed invalidate so it cannot merge further`
`scope @${instr.scope.id} doesnt guaranteed invalidate so it cannot merge further`,
);
}
}
@@ -319,7 +319,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
default: {
assertExhaustive(
instr,
`Unexpected instruction kind \`${(instr as any).kind}\``
`Unexpected instruction kind \`${(instr as any).kind}\``,
);
}
}
@@ -332,7 +332,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
for (const entry of merged) {
log(
printReactiveScopeSummary(entry.block.scope) +
` from=${entry.from} to=${entry.to}`
` from=${entry.from} to=${entry.to}`,
);
}
}
@@ -350,10 +350,10 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
index = entry.from;
}
const mergedScope = block[entry.from]!;
CompilerError.invariant(mergedScope.kind === "scope", {
CompilerError.invariant(mergedScope.kind === 'scope', {
loc: null,
reason:
"MergeConsecutiveScopes: Expected scope starting index to be a scope",
'MergeConsecutiveScopes: Expected scope starting index to be a scope',
description: null,
suggestions: null,
});
@@ -361,7 +361,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
index++;
while (index < entry.to) {
const instr = block[index++]!;
if (instr.kind === "scope") {
if (instr.kind === 'scope') {
mergedScope.instructions.push(...instr.instructions);
mergedScope.scope.merged.add(instr.scope.id);
} else {
@@ -383,7 +383,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
*/
function updateScopeDeclarations(
scope: ReactiveScope,
lastUsage: Map<IdentifierId, InstructionId>
lastUsage: Map<IdentifierId, InstructionId>,
): void {
for (const [key] of scope.declarations) {
const lastUsedAt = lastUsage.get(key)!;
@@ -401,7 +401,7 @@ function updateScopeDeclarations(
function areLValuesLastUsedByScope(
scope: ReactiveScope,
lvalues: Set<IdentifierId>,
lastUsage: Map<IdentifierId, InstructionId>
lastUsage: Map<IdentifierId, InstructionId>,
): boolean {
for (const lvalue of lvalues) {
const lastUsedAt = lastUsage.get(lvalue)!;
@@ -415,7 +415,7 @@ function areLValuesLastUsedByScope(
function canMergeScopes(
current: ReactiveScopeBlock,
next: ReactiveScopeBlock
next: ReactiveScopeBlock,
): boolean {
// Don't merge scopes with reassignments
if (
@@ -444,18 +444,18 @@ function canMergeScopes(
if (
areEqualDependencies(
new Set(
[...current.scope.declarations.values()].map((declaration) => ({
[...current.scope.declarations.values()].map(declaration => ({
identifier: declaration.identifier,
path: [],
}))
})),
),
next.scope.dependencies
next.scope.dependencies,
) ||
(next.scope.dependencies.size !== 0 &&
[...next.scope.dependencies].every(
(dep) =>
dep =>
current.scope.declarations.has(dep.identifier.id) &&
isAlwaysInvalidatingType(dep.identifier.type)
isAlwaysInvalidatingType(dep.identifier.type),
))
) {
log(` outputs of prev are input to current`);
@@ -468,7 +468,7 @@ function canMergeScopes(
}
function isAlwaysInvalidatingType(type: Type): boolean {
if (type.kind === "Object") {
if (type.kind === 'Object') {
switch (type.shapeId) {
case BuiltInArrayId:
case BuiltInObjectId:
@@ -483,7 +483,7 @@ function isAlwaysInvalidatingType(type: Type): boolean {
function areEqualDependencies(
a: Set<ReactiveScopeDependency>,
b: Set<ReactiveScopeDependency>
b: Set<ReactiveScopeDependency>,
): boolean {
if (a.size !== b.size) {
return false;
@@ -529,6 +529,6 @@ function scopeIsEligibleForMerging(scopeBlock: ReactiveScopeBlock): boolean {
return true;
}
return [...scopeBlock.scope.declarations].some(([, decl]) =>
isAlwaysInvalidatingType(decl.identifier.type)
isAlwaysInvalidatingType(decl.identifier.type),
);
}
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "../CompilerError";
import {CompilerError} from '../CompilerError';
import {
PrunedReactiveScopeBlock,
ReactiveFunction,
@@ -15,23 +15,23 @@ import {
ReactiveStatement,
ReactiveTerminal,
ReactiveValue,
} from "../HIR/HIR";
} from '../HIR/HIR';
import {
printFunction,
printIdentifier,
printInstructionValue,
printPlace,
printType,
} from "../HIR/PrintHIR";
import { assertExhaustive } from "../Utils/utils";
} from '../HIR/PrintHIR';
import {assertExhaustive} from '../Utils/utils';
export function printReactiveFunctionWithOutlined(
fn: ReactiveFunction
fn: ReactiveFunction,
): string {
const writer = new Writer();
writeReactiveFunction(fn, writer);
for (const outlined of fn.env.getOutlinedFunctions()) {
writer.writeLine("\nfunction " + printFunction(outlined.fn));
writer.writeLine('\nfunction ' + printFunction(outlined.fn));
}
return writer.complete();
}
@@ -43,81 +43,81 @@ export function printReactiveFunction(fn: ReactiveFunction): string {
}
function writeReactiveFunction(fn: ReactiveFunction, writer: Writer): void {
writer.writeLine(`function ${fn.id !== null ? fn.id : "<unknown>"}(`);
writer.writeLine(`function ${fn.id !== null ? fn.id : '<unknown>'}(`);
writer.indented(() => {
for (const param of fn.params) {
if (param.kind === "Identifier") {
if (param.kind === 'Identifier') {
writer.writeLine(`${printPlace(param)},`);
} else {
writer.writeLine(`...${printPlace(param.place)},`);
}
}
});
writer.writeLine(") {");
writer.writeLine(') {');
writeReactiveInstructions(writer, fn.body);
writer.writeLine("}");
writer.writeLine('}');
}
export function printReactiveScopeSummary(scope: ReactiveScope): string {
const items = [];
// If the scope has a return value it needs a label
items.push("scope");
items.push('scope');
items.push(`@${scope.id}`);
items.push(`[${scope.range.start}:${scope.range.end}]`);
items.push(
`dependencies=[${Array.from(scope.dependencies)
.map((dep) => printDependency(dep))
.join(", ")}]`
.map(dep => printDependency(dep))
.join(', ')}]`,
);
items.push(
`declarations=[${Array.from(scope.declarations)
.map(([, decl]) =>
printIdentifier({ ...decl.identifier, scope: decl.scope })
printIdentifier({...decl.identifier, scope: decl.scope}),
)
.join(", ")}]`
.join(', ')}]`,
);
items.push(
`reassignments=[${Array.from(scope.reassignments).map((reassign) =>
printIdentifier(reassign)
)}]`
`reassignments=[${Array.from(scope.reassignments).map(reassign =>
printIdentifier(reassign),
)}]`,
);
if (scope.earlyReturnValue !== null) {
items.push(
`earlyReturn={id: ${printIdentifier(
scope.earlyReturnValue.value
)}, label: ${scope.earlyReturnValue.label}}}`
scope.earlyReturnValue.value,
)}, label: ${scope.earlyReturnValue.label}}}`,
);
}
return items.join(" ");
return items.join(' ');
}
export function writeReactiveBlock(
writer: Writer,
block: ReactiveScopeBlock
block: ReactiveScopeBlock,
): void {
writer.writeLine(`${printReactiveScopeSummary(block.scope)} {`);
writeReactiveInstructions(writer, block.instructions);
writer.writeLine("}");
writer.writeLine('}');
}
export function writePrunedScope(
writer: Writer,
block: PrunedReactiveScopeBlock
block: PrunedReactiveScopeBlock,
): void {
writer.writeLine(`<pruned> ${printReactiveScopeSummary(block.scope)} {`);
writeReactiveInstructions(writer, block.instructions);
writer.writeLine("}");
writer.writeLine('}');
}
export function printDependency(dependency: ReactiveScopeDependency): string {
const identifier =
printIdentifier(dependency.identifier) +
printType(dependency.identifier.type);
return `${identifier}${dependency.path.map((prop) => `.${prop}`).join("")}`;
return `${identifier}${dependency.path.map(prop => `.${prop}`).join('')}`;
}
export function printReactiveInstructions(
instructions: Array<ReactiveStatement>
instructions: Array<ReactiveStatement>,
): string {
const writer = new Writer();
writeReactiveInstructions(writer, instructions);
@@ -126,7 +126,7 @@ export function printReactiveInstructions(
export function writeReactiveInstructions(
writer: Writer,
instructions: Array<ReactiveStatement>
instructions: Array<ReactiveStatement>,
): void {
writer.indented(() => {
for (const instr of instructions) {
@@ -137,11 +137,11 @@ export function writeReactiveInstructions(
function writeReactiveInstruction(
writer: Writer,
instr: ReactiveStatement
instr: ReactiveStatement,
): void {
switch (instr.kind) {
case "instruction": {
const { instruction } = instr;
case 'instruction': {
const {instruction} = instr;
const id = `[${instruction.id}]`;
if (instruction.lvalue !== null) {
@@ -155,15 +155,15 @@ function writeReactiveInstruction(
}
break;
}
case "scope": {
case 'scope': {
writeReactiveBlock(writer, instr);
break;
}
case "pruned-scope": {
case 'pruned-scope': {
writePrunedScope(writer, instr);
break;
}
case "terminal": {
case 'terminal': {
if (instr.label !== null) {
writer.write(`bb${instr.label.id}: `);
}
@@ -173,7 +173,7 @@ function writeReactiveInstruction(
default: {
assertExhaustive(
instr,
`Unexpected terminal kind \`${(instr as any).kind}\``
`Unexpected terminal kind \`${(instr as any).kind}\``,
);
}
}
@@ -187,7 +187,7 @@ export function printReactiveValue(value: ReactiveValue): string {
function writeReactiveValue(writer: Writer, value: ReactiveValue): void {
switch (value.kind) {
case "ConditionalExpression": {
case 'ConditionalExpression': {
writer.writeLine(`Ternary `);
writer.indented(() => {
writeReactiveValue(writer, value.test);
@@ -203,7 +203,7 @@ function writeReactiveValue(writer: Writer, value: ReactiveValue): void {
writer.newline();
break;
}
case "LogicalExpression": {
case 'LogicalExpression': {
writer.writeLine(`Logical`);
writer.indented(() => {
writeReactiveValue(writer, value.left);
@@ -213,15 +213,15 @@ function writeReactiveValue(writer: Writer, value: ReactiveValue): void {
writer.newline();
break;
}
case "SequenceExpression": {
case 'SequenceExpression': {
writer.writeLine(`Sequence`);
writer.indented(() => {
writer.indented(() => {
value.instructions.forEach((instr) =>
value.instructions.forEach(instr =>
writeReactiveInstruction(writer, {
kind: "instruction",
kind: 'instruction',
instruction: instr,
})
}),
);
writer.write(`[${value.id}] `);
writeReactiveValue(writer, value.value);
@@ -230,7 +230,7 @@ function writeReactiveValue(writer: Writer, value: ReactiveValue): void {
writer.newline();
break;
}
case "OptionalExpression": {
case 'OptionalExpression': {
writer.append(`OptionalExpression optional=${value.optional}`);
writer.newline();
writer.indented(() => {
@@ -241,7 +241,7 @@ function writeReactiveValue(writer: Writer, value: ReactiveValue): void {
}
default: {
const printed = printInstructionValue(value);
const lines = printed.split("\n");
const lines = printed.split('\n');
if (lines.length === 1) {
writer.writeLine(printed);
} else {
@@ -257,130 +257,130 @@ function writeReactiveValue(writer: Writer, value: ReactiveValue): void {
function writeTerminal(writer: Writer, terminal: ReactiveTerminal): void {
switch (terminal.kind) {
case "break": {
case 'break': {
const id = terminal.id !== null ? `[${terminal.id}]` : [];
writer.writeLine(
`${id} break bb${terminal.target} (${terminal.targetKind})`
`${id} break bb${terminal.target} (${terminal.targetKind})`,
);
break;
}
case "continue": {
case 'continue': {
const id = `[${terminal.id}]`;
writer.writeLine(
`${id} continue bb${terminal.target} (${terminal.targetKind})`
`${id} continue bb${terminal.target} (${terminal.targetKind})`,
);
break;
}
case "do-while": {
case 'do-while': {
writer.writeLine(`[${terminal.id}] do-while {`);
writeReactiveInstructions(writer, terminal.loop);
writer.writeLine("} (");
writer.writeLine('} (');
writer.indented(() => {
writeReactiveValue(writer, terminal.test);
});
writer.writeLine(")");
writer.writeLine(')');
break;
}
case "while": {
case 'while': {
writer.writeLine(`[${terminal.id}] while (`);
writer.indented(() => {
writeReactiveValue(writer, terminal.test);
});
writer.writeLine(") {");
writer.writeLine(') {');
writeReactiveInstructions(writer, terminal.loop);
writer.writeLine("}");
writer.writeLine('}');
break;
}
case "if": {
const { test, consequent, alternate } = terminal;
case 'if': {
const {test, consequent, alternate} = terminal;
writer.writeLine(`[${terminal.id}] if (${printPlace(test)}) {`);
writeReactiveInstructions(writer, consequent);
if (alternate !== null) {
writer.writeLine("} else {");
writer.writeLine('} else {');
writeReactiveInstructions(writer, alternate);
}
writer.writeLine("}");
writer.writeLine('}');
break;
}
case "switch": {
case 'switch': {
writer.writeLine(
`[${terminal.id}] switch (${printPlace(terminal.test)}) {`
`[${terminal.id}] switch (${printPlace(terminal.test)}) {`,
);
writer.indented(() => {
for (const case_ of terminal.cases) {
let prefix =
case_.test !== null ? `case ${printPlace(case_.test)}` : "default";
case_.test !== null ? `case ${printPlace(case_.test)}` : 'default';
writer.writeLine(`${prefix}: {`);
writer.indented(() => {
const block = case_.block;
CompilerError.invariant(block != null, {
reason: "Expected case to have a block",
reason: 'Expected case to have a block',
description: null,
loc: case_.test?.loc ?? null,
suggestions: null,
});
writeReactiveInstructions(writer, block);
});
writer.writeLine("}");
writer.writeLine('}');
}
});
writer.writeLine("}");
writer.writeLine('}');
break;
}
case "for": {
case 'for': {
writer.writeLine(`[${terminal.id}] for (`);
writer.indented(() => {
writeReactiveValue(writer, terminal.init);
writer.writeLine(";");
writer.writeLine(';');
writeReactiveValue(writer, terminal.test);
writer.writeLine(";");
writer.writeLine(';');
if (terminal.update !== null) {
writeReactiveValue(writer, terminal.update);
}
});
writer.writeLine(") {");
writer.writeLine(') {');
writeReactiveInstructions(writer, terminal.loop);
writer.writeLine("}");
writer.writeLine('}');
break;
}
case "for-of": {
case 'for-of': {
writer.writeLine(`[${terminal.id}] for-of (`);
writer.indented(() => {
writeReactiveValue(writer, terminal.init);
writer.writeLine(";");
writer.writeLine(';');
writeReactiveValue(writer, terminal.test);
});
writer.writeLine(") {");
writer.writeLine(') {');
writeReactiveInstructions(writer, terminal.loop);
writer.writeLine("}");
writer.writeLine('}');
break;
}
case "for-in": {
case 'for-in': {
writer.writeLine(`[${terminal.id}] for-in (`);
writer.indented(() => {
writeReactiveValue(writer, terminal.init);
});
writer.writeLine(") {");
writer.writeLine(') {');
writeReactiveInstructions(writer, terminal.loop);
writer.writeLine("}");
writer.writeLine('}');
break;
}
case "throw": {
case 'throw': {
writer.writeLine(`[${terminal.id}] throw ${printPlace(terminal.value)}`);
break;
}
case "return": {
case 'return': {
writer.writeLine(`[${terminal.id}] return ${printPlace(terminal.value)}`);
break;
}
case "label": {
writer.writeLine("{");
case 'label': {
writer.writeLine('{');
writeReactiveInstructions(writer, terminal.block);
writer.writeLine("}");
writer.writeLine('}');
break;
}
case "try": {
case 'try': {
writer.writeLine(`[${terminal.id}] try {`);
writeReactiveInstructions(writer, terminal.block);
writer.write(`} catch `);
@@ -390,7 +390,7 @@ function writeTerminal(writer: Writer, terminal: ReactiveTerminal): void {
writer.writeLine(`{`);
}
writeReactiveInstructions(writer, terminal.handler);
writer.writeLine("}");
writer.writeLine('}');
break;
}
default:
@@ -403,9 +403,9 @@ export class Writer {
#line: string;
#depth: number;
constructor({ depth }: { depth: number } = { depth: 0 }) {
constructor({depth}: {depth: number} = {depth: 0}) {
this.#depth = Math.max(depth, 0);
this.#line = "";
this.#line = '';
}
complete(): string {
@@ -413,7 +413,7 @@ export class Writer {
if (line.length > 0) {
this.#out.push(line);
}
return this.#out.join("\n");
return this.#out.join('\n');
}
append(s: string): void {
@@ -425,13 +425,13 @@ export class Writer {
if (line.length > 0) {
this.#out.push(line);
}
this.#line = "";
this.#line = '';
}
write(s: string): void {
if (this.#line.length === 0 && this.#depth > 0) {
// indent before writing
this.#line = " ".repeat(this.#depth);
this.#line = ' '.repeat(this.#depth);
}
this.#line += s;
}
@@ -5,8 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "../CompilerError";
import { GeneratedSource } from "../HIR";
import {CompilerError} from '../CompilerError';
import {GeneratedSource} from '../HIR';
import {
Identifier,
IdentifierId,
@@ -19,14 +19,14 @@ import {
ScopeId,
promoteTemporary,
promoteTemporaryJsxTag,
} from "../HIR/HIR";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
} from '../HIR/HIR';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
class Visitor extends ReactiveFunctionVisitor<State> {
override visitScope(scopeBlock: ReactiveScopeBlock, state: State): void {
this.traverseScope(scopeBlock, state);
for (const dep of scopeBlock.scope.dependencies) {
const { identifier } = dep;
const {identifier} = dep;
if (identifier.name == null) {
promoteIdentifier(identifier, state);
}
@@ -47,7 +47,7 @@ class Visitor extends ReactiveFunctionVisitor<State> {
override visitPrunedScope(
scopeBlock: PrunedReactiveScopeBlock,
state: State
state: State,
): void {
this.traversePrunedScope(scopeBlock, state);
for (const [, declaration] of scopeBlock.scope.declarations) {
@@ -69,10 +69,10 @@ class Visitor extends ReactiveFunctionVisitor<State> {
override visitValue(
id: InstructionId,
value: ReactiveValue,
state: State
state: State,
): void {
this.traverseValue(id, value, state);
if (value.kind === "FunctionExpression" || value.kind === "ObjectMethod") {
if (value.kind === 'FunctionExpression' || value.kind === 'ObjectMethod') {
this.visitHirFunction(value.loweredFunc.func, state);
}
}
@@ -81,10 +81,10 @@ class Visitor extends ReactiveFunctionVisitor<State> {
_id: InstructionId,
_dependencies: Array<Place>,
fn: ReactiveFunction,
state: State
state: State,
): void {
for (const operand of fn.params) {
const place = operand.kind === "Identifier" ? operand : operand.place;
const place = operand.kind === 'Identifier' ? operand : operand.place;
if (place.identifier.name === null) {
promoteIdentifier(place.identifier, state);
}
@@ -98,7 +98,7 @@ type State = {
tags: JsxExpressionTags;
pruned: Map<
IdentifierId,
{ activeScopes: Array<ScopeId>; usedOutsideScope: boolean }
{activeScopes: Array<ScopeId>; usedOutsideScope: boolean}
>; // true if referenced within another scope, false if only accessed outside of scopes
};
@@ -120,17 +120,17 @@ class CollectPromotableTemporaries extends ReactiveFunctionVisitor<State> {
override visitValue(
id: InstructionId,
value: ReactiveValue,
state: State
state: State,
): void {
this.traverseValue(id, value, state);
if (value.kind === "JsxExpression" && value.tag.kind === "Identifier") {
if (value.kind === 'JsxExpression' && value.tag.kind === 'Identifier') {
state.tags.add(value.tag.identifier.id);
}
}
override visitPrunedScope(
scopeBlock: PrunedReactiveScopeBlock,
state: State
state: State,
): void {
for (const [id] of scopeBlock.scope.declarations) {
state.pruned.set(id, {
@@ -154,7 +154,7 @@ export function promoteUsedTemporaries(fn: ReactiveFunction): void {
};
visitReactiveFunction(fn, new CollectPromotableTemporaries(), state);
for (const operand of fn.params) {
const place = operand.kind === "Identifier" ? operand : operand.place;
const place = operand.kind === 'Identifier' ? operand : operand.place;
if (place.identifier.name === null) {
promoteIdentifier(place.identifier, state);
}
@@ -165,7 +165,7 @@ export function promoteUsedTemporaries(fn: ReactiveFunction): void {
function promoteIdentifier(identifier: Identifier, state: State): void {
CompilerError.invariant(identifier.name === null, {
reason:
"promoteTemporary: Expected to be called only for temporary variables",
'promoteTemporary: Expected to be called only for temporary variables',
description: null,
loc: GeneratedSource,
suggestions: null,
@@ -5,8 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/
import { visitReactiveFunction } from ".";
import { Effect } from "..";
import {visitReactiveFunction} from '.';
import {Effect} from '..';
import {
Environment,
GeneratedSource,
@@ -18,10 +18,10 @@ import {
ReactiveTerminalStatement,
makeInstructionId,
promoteTemporary,
} from "../HIR";
import { createTemporaryPlace } from "../HIR/HIRBuilder";
import { EARLY_RETURN_SENTINEL } from "./CodegenReactiveFunction";
import { ReactiveFunctionTransform, Transformed } from "./visitors";
} from '../HIR';
import {createTemporaryPlace} from '../HIR/HIRBuilder';
import {EARLY_RETURN_SENTINEL} from './CodegenReactiveFunction';
import {ReactiveFunctionTransform, Transformed} from './visitors';
/**
* This pass ensures that reactive blocks honor the control flow behavior of the
@@ -119,7 +119,7 @@ type State = {
* Store early return information to bubble it back up to the outermost
* reactive scope
*/
earlyReturnValue: ReactiveScope["earlyReturnValue"];
earlyReturnValue: ReactiveScope['earlyReturnValue'];
};
class Transform extends ReactiveFunctionTransform<State> {
@@ -131,7 +131,7 @@ class Transform extends ReactiveFunctionTransform<State> {
override visitScope(
scopeBlock: ReactiveScopeBlock,
parentState: State
parentState: State,
): void {
/**
* Exit early if an earlier pass has already created an early return,
@@ -165,56 +165,56 @@ class Transform extends ReactiveFunctionTransform<State> {
const argTemp = createTemporaryPlace(this.env, loc);
scopeBlock.instructions = [
{
kind: "instruction",
kind: 'instruction',
instruction: {
id: makeInstructionId(0),
loc,
lvalue: { ...symbolTemp },
lvalue: {...symbolTemp},
value: {
kind: "LoadGlobal",
kind: 'LoadGlobal',
binding: {
kind: "Global",
name: "Symbol",
kind: 'Global',
name: 'Symbol',
},
loc,
},
},
},
{
kind: "instruction",
kind: 'instruction',
instruction: {
id: makeInstructionId(0),
loc,
lvalue: { ...forTemp },
lvalue: {...forTemp},
value: {
kind: "PropertyLoad",
object: { ...symbolTemp },
property: "for",
kind: 'PropertyLoad',
object: {...symbolTemp},
property: 'for',
loc,
},
},
},
{
kind: "instruction",
kind: 'instruction',
instruction: {
id: makeInstructionId(0),
loc,
lvalue: { ...argTemp },
lvalue: {...argTemp},
value: {
kind: "Primitive",
kind: 'Primitive',
value: EARLY_RETURN_SENTINEL,
loc,
},
},
},
{
kind: "instruction",
kind: 'instruction',
instruction: {
id: makeInstructionId(0),
loc,
lvalue: { ...sentinelTemp },
lvalue: {...sentinelTemp},
value: {
kind: "MethodCall",
kind: 'MethodCall',
receiver: symbolTemp,
property: forTemp,
args: [argTemp],
@@ -223,37 +223,37 @@ class Transform extends ReactiveFunctionTransform<State> {
},
},
{
kind: "instruction",
kind: 'instruction',
instruction: {
id: makeInstructionId(0),
loc,
lvalue: null,
value: {
kind: "StoreLocal",
kind: 'StoreLocal',
loc,
type: null,
lvalue: {
kind: InstructionKind.Let,
place: {
kind: "Identifier",
kind: 'Identifier',
effect: Effect.ConditionallyMutate,
loc,
reactive: true,
identifier: earlyReturnValue.value,
},
},
value: { ...sentinelTemp },
value: {...sentinelTemp},
},
},
},
{
kind: "terminal",
kind: 'terminal',
label: {
id: earlyReturnValue.label,
implicit: false,
},
terminal: {
kind: "label",
kind: 'label',
id: makeInstructionId(0),
loc: GeneratedSource,
block: instructions,
@@ -272,11 +272,11 @@ class Transform extends ReactiveFunctionTransform<State> {
override transformTerminal(
stmt: ReactiveTerminalStatement,
state: State
state: State,
): Transformed<ReactiveStatement> {
if (state.withinReactiveScope && stmt.terminal.kind === "return") {
if (state.withinReactiveScope && stmt.terminal.kind === 'return') {
const loc = stmt.terminal.value.loc;
let earlyReturnValue: ReactiveScope["earlyReturnValue"];
let earlyReturnValue: ReactiveScope['earlyReturnValue'];
if (state.earlyReturnValue !== null) {
earlyReturnValue = state.earlyReturnValue;
} else {
@@ -290,22 +290,22 @@ class Transform extends ReactiveFunctionTransform<State> {
}
state.earlyReturnValue = earlyReturnValue;
return {
kind: "replace-many",
kind: 'replace-many',
value: [
{
kind: "instruction",
kind: 'instruction',
instruction: {
id: makeInstructionId(0),
loc,
lvalue: null,
value: {
kind: "StoreLocal",
kind: 'StoreLocal',
loc,
type: null,
lvalue: {
kind: InstructionKind.Reassign,
place: {
kind: "Identifier",
kind: 'Identifier',
identifier: earlyReturnValue.value,
effect: Effect.Capture,
loc,
@@ -317,13 +317,13 @@ class Transform extends ReactiveFunctionTransform<State> {
},
},
{
kind: "terminal",
kind: 'terminal',
label: null,
terminal: {
kind: "break",
kind: 'break',
id: makeInstructionId(0),
loc,
targetKind: "labeled",
targetKind: 'labeled',
target: earlyReturnValue.label,
},
},
@@ -331,6 +331,6 @@ class Transform extends ReactiveFunctionTransform<State> {
};
}
this.traverseTerminal(stmt, state);
return { kind: "keep" };
return {kind: 'keep'};
}
}
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "../CompilerError";
import {CompilerError} from '../CompilerError';
import {
BlockId,
GeneratedSource,
@@ -27,18 +27,15 @@ import {
ReactiveTerminalStatement,
ReactiveValue,
ScopeId,
} from "../HIR/HIR";
import {
eachInstructionValueOperand,
eachPatternOperand,
} from "../HIR/visitors";
import { empty, Stack } from "../Utils/Stack";
import { assertExhaustive } from "../Utils/utils";
} from '../HIR/HIR';
import {eachInstructionValueOperand, eachPatternOperand} from '../HIR/visitors';
import {empty, Stack} from '../Utils/Stack';
import {assertExhaustive} from '../Utils/utils';
import {
ReactiveScopeDependencyTree,
ReactiveScopePropertyDependency,
} from "./DeriveMinimalDependencies";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
} from './DeriveMinimalDependencies';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
/*
* Infers the dependencies of each scope to include variables whose values
@@ -55,7 +52,7 @@ export function propagateScopeDependencies(fn: ReactiveFunction): void {
const context = new Context(escapingTemporaries.usedOutsideDeclaringScope);
for (const param of fn.params) {
if (param.kind === "Identifier") {
if (param.kind === 'Identifier') {
context.declare(param.identifier, {
id: makeInstructionId(0),
scope: empty(),
@@ -70,7 +67,7 @@ export function propagateScopeDependencies(fn: ReactiveFunction): void {
visitReactiveFunction(
fn,
new PropagationVisitor(fn.env.config.enableTreatFunctionDepsAsConditional),
context
context,
);
}
@@ -88,7 +85,7 @@ class FindPromotedTemporaries extends ReactiveFunctionVisitor<TemporariesUsedOut
override visitScope(
scope: ReactiveScopeBlock,
state: TemporariesUsedOutsideDefiningScope
state: TemporariesUsedOutsideDefiningScope,
): void {
this.scopes.push(scope.scope.id);
this.traverseScope(scope, state);
@@ -97,7 +94,7 @@ class FindPromotedTemporaries extends ReactiveFunctionVisitor<TemporariesUsedOut
override visitInstruction(
instruction: ReactiveInstruction,
state: TemporariesUsedOutsideDefiningScope
state: TemporariesUsedOutsideDefiningScope,
): void {
// Visit all places first, then record temporaries which may need to be promoted
this.traverseInstruction(instruction, state);
@@ -107,9 +104,9 @@ class FindPromotedTemporaries extends ReactiveFunctionVisitor<TemporariesUsedOut
return;
}
switch (instruction.value.kind) {
case "LoadLocal":
case "LoadContext":
case "PropertyLoad": {
case 'LoadLocal':
case 'LoadContext':
case 'PropertyLoad': {
state.declarations.set(instruction.lvalue.identifier.id, scope);
break;
}
@@ -122,7 +119,7 @@ class FindPromotedTemporaries extends ReactiveFunctionVisitor<TemporariesUsedOut
override visitPlace(
_id: InstructionId,
place: Place,
state: TemporariesUsedOutsideDefiningScope
state: TemporariesUsedOutsideDefiningScope,
): void {
const declaringScope = state.declarations.get(place.identifier.id);
if (declaringScope === undefined) {
@@ -164,7 +161,7 @@ class PoisonState {
constructor(
poisonedBlocks: Set<BlockId>,
poisonedScopes: Set<ScopeId>,
isPoisoned: boolean
isPoisoned: boolean,
) {
this.poisonedBlocks = poisonedBlocks;
this.poisonedScopes = poisonedScopes;
@@ -175,7 +172,7 @@ class PoisonState {
return new PoisonState(
new Set(this.poisonedBlocks),
new Set(this.poisonedScopes),
this.isPoisoned
this.isPoisoned,
);
}
@@ -183,7 +180,7 @@ class PoisonState {
const copy = new PoisonState(
this.poisonedBlocks,
this.poisonedScopes,
this.isPoisoned
this.isPoisoned,
);
this.poisonedBlocks = other.poisonedBlocks;
this.poisonedScopes = other.poisonedScopes;
@@ -193,7 +190,7 @@ class PoisonState {
merge(
others: Array<PoisonState>,
currentScope: ScopeTraversalState | null
currentScope: ScopeTraversalState | null,
): void {
for (const other of others) {
for (const id of other.poisonedBlocks) {
@@ -212,9 +209,7 @@ class PoisonState {
this.isPoisoned = true;
return;
} else if (
currentScope.ownBlocks.find((blockId) =>
this.poisonedBlocks.has(blockId)
)
currentScope.ownBlocks.find(blockId => this.poisonedBlocks.has(blockId))
) {
this.isPoisoned = true;
return;
@@ -233,7 +228,7 @@ class PoisonState {
*/
addPoisonTarget(
target: BlockId | null,
activeScopes: Stack<ScopeTraversalState>
activeScopes: Stack<ScopeTraversalState>,
): void {
const currentScope = activeScopes.value;
if (target == null && currentScope != null) {
@@ -255,7 +250,7 @@ class PoisonState {
this.poisonedBlocks.add(target);
if (
!this.isPoisoned &&
currentScope?.ownBlocks.find((blockId) => blockId === target)
currentScope?.ownBlocks.find(blockId => blockId === target)
) {
this.isPoisoned = true;
}
@@ -269,7 +264,7 @@ class PoisonState {
*/
removeMaybePoisonedScope(
id: ScopeId,
currentScope: ScopeTraversalState | null
currentScope: ScopeTraversalState | null,
): void {
this.poisonedScopes.delete(id);
this.#invalidate(currentScope);
@@ -277,7 +272,7 @@ class PoisonState {
removeMaybePoisonedBlock(
id: BlockId,
currentScope: ScopeTraversalState | null
currentScope: ScopeTraversalState | null,
): void {
this.poisonedBlocks.delete(id);
this.#invalidate(currentScope);
@@ -365,7 +360,7 @@ class Context {
this.#dependencies.addDepsFromInnerScope(
scopedDependencies,
this.#inConditionalWithinScope || this.isPoisoned,
this.#checkValidDependency.bind(this)
this.#checkValidDependency.bind(this),
);
if (prevDepsInConditional != null) {
@@ -373,7 +368,7 @@ class Context {
prevDepsInConditional.addDepsFromInnerScope(
this.#depsInCurrentConditional,
true,
this.#checkValidDependency.bind(this)
this.#checkValidDependency.bind(this),
);
this.#depsInCurrentConditional = prevDepsInConditional;
}
@@ -428,13 +423,13 @@ class Context {
* @param depsInConditionals
*/
promoteDepsFromExhaustiveConditionals(
depsInConditionals: Array<ReactiveScopeDependencyTree>
depsInConditionals: Array<ReactiveScopeDependencyTree>,
): void {
this.#dependencies.promoteDepsFromExhaustiveConditionals(
depsInConditionals
depsInConditionals,
);
this.#depsInCurrentConditional.promoteDepsFromExhaustiveConditionals(
depsInConditionals
depsInConditionals,
);
}
@@ -462,7 +457,7 @@ class Context {
#getProperty(
object: Place,
property: string,
isConditional: boolean
isConditional: boolean,
): ReactiveScopePropertyDependency {
const resolvedObject = this.resolveTemporary(object);
const resolvedDependency = this.#properties.get(resolvedObject.identifier);
@@ -513,7 +508,7 @@ class Context {
// ref.current access is not a valid dep
if (
isUseRefType(maybeDependency.identifier) &&
maybeDependency.path.at(0) === "current"
maybeDependency.path.at(0) === 'current'
) {
return false;
}
@@ -553,7 +548,7 @@ class Context {
if (this.#scopes === null) {
return false;
}
return this.#scopes.find((state) => state.value === scope);
return this.#scopes.find(state => state.value === scope);
}
get currentScope(): Stack<ScopeTraversalState> {
@@ -579,7 +574,7 @@ class Context {
if (resolved.identifier.name === null) {
const propertyDependency = this.#properties.get(resolved.identifier);
if (propertyDependency !== undefined) {
dependency = { ...propertyDependency };
dependency = {...propertyDependency};
}
}
this.visitDependency(dependency);
@@ -604,13 +599,13 @@ class Context {
* (all other decls e.g. `let x;` should be initialized in BuildHIR)
*/
const originalDeclaration = this.#declarations.get(
maybeDependency.identifier.id
maybeDependency.identifier.id,
);
if (
originalDeclaration !== undefined &&
originalDeclaration.scope.value !== null
) {
originalDeclaration.scope.each((scope) => {
originalDeclaration.scope.each(scope => {
if (!this.#isScopeActive(scope.value)) {
scope.value.declarations.set(maybeDependency.identifier.id, {
identifier: maybeDependency.identifier,
@@ -629,7 +624,7 @@ class Context {
*/
this.#dependencies.add(
maybeDependency,
this.#inConditionalWithinScope || isPoisoned
this.#inConditionalWithinScope || isPoisoned,
);
}
}
@@ -643,9 +638,9 @@ class Context {
if (
currentScope != null &&
!Array.from(currentScope.reassignments).some(
(identifier) => identifier.id === place.identifier.id
identifier => identifier.id === place.identifier.id,
) &&
this.#checkValidDependency({ identifier: place.identifier, path: [] })
this.#checkValidDependency({identifier: place.identifier, path: []})
) {
currentScope.reassignments.add(place.identifier);
}
@@ -664,7 +659,7 @@ class Context {
currentScope.ownBlocks = currentScope.ownBlocks.pop();
CompilerError.invariant(last != null && last === id, {
reason: "[PropagateScopeDependencies] Misformed block stack",
reason: '[PropagateScopeDependencies] Misformed block stack',
loc: GeneratedSource,
});
}
@@ -690,7 +685,7 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
override visitPrunedScope(
scopeBlock: PrunedReactiveScopeBlock,
context: Context
context: Context,
): void {
/*
* NOTE: we explicitly throw away the deps, we only enter() the scope to record its
@@ -703,9 +698,9 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
override visitInstruction(
instruction: ReactiveInstruction,
context: Context
context: Context,
): void {
const { id, value, lvalue } = instruction;
const {id, value, lvalue} = instruction;
this.visitInstructionValue(context, id, value, lvalue);
if (lvalue == null) {
return;
@@ -719,19 +714,19 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
visitReactiveValue(
context: Context,
id: InstructionId,
value: ReactiveValue
value: ReactiveValue,
): void {
switch (value.kind) {
case "OptionalExpression": {
case 'OptionalExpression': {
const inner = value.value;
/*
* OptionalExpression value is a SequenceExpression where the instructions
* represent the code prior to the `?` and the final value represents the
* conditional code that follows.
*/
CompilerError.invariant(inner.kind === "SequenceExpression", {
CompilerError.invariant(inner.kind === 'SequenceExpression', {
reason:
"Expected OptionalExpression value to be a SequenceExpression",
'Expected OptionalExpression value to be a SequenceExpression',
description: `Found a \`${value.kind}\``,
loc: value.loc,
suggestions: null,
@@ -746,14 +741,14 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
});
break;
}
case "LogicalExpression": {
case 'LogicalExpression': {
this.visitReactiveValue(context, id, value.left);
context.enterConditional(() => {
this.visitReactiveValue(context, id, value.right);
});
break;
}
case "ConditionalExpression": {
case 'ConditionalExpression': {
this.visitReactiveValue(context, id, value.test);
const consequentDeps = context.enterConditional(() => {
@@ -768,14 +763,14 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
]);
break;
}
case "SequenceExpression": {
case 'SequenceExpression': {
for (const instr of value.instructions) {
this.visitInstruction(instr, context);
}
this.visitInstructionValue(context, id, value.value, null);
break;
}
case "FunctionExpression": {
case 'FunctionExpression': {
if (this.enableTreatFunctionDepsAsConditional) {
context.enterConditional(() => {
for (const operand of eachInstructionValueOperand(value)) {
@@ -789,7 +784,7 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
}
break;
}
case "ReactiveFunctionValue": {
case 'ReactiveFunctionValue': {
CompilerError.invariant(false, {
reason: `Unexpected ReactiveFunctionValue`,
loc: value.loc,
@@ -809,9 +804,9 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
context: Context,
id: InstructionId,
value: ReactiveValue,
lvalue: Place | null
lvalue: Place | null,
): void {
if (value.kind === "LoadLocal" && lvalue !== null) {
if (value.kind === 'LoadLocal' && lvalue !== null) {
if (
value.place.identifier.name !== null &&
lvalue.identifier.name === null &&
@@ -821,13 +816,13 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
} else {
context.visitOperand(value.place);
}
} else if (value.kind === "PropertyLoad") {
} else if (value.kind === 'PropertyLoad') {
if (lvalue !== null && !context.isUsedOutsideDeclaringScope(lvalue)) {
context.declareProperty(lvalue, value.object, value.property);
} else {
context.visitProperty(value.object, value.property);
}
} else if (value.kind === "StoreLocal") {
} else if (value.kind === 'StoreLocal') {
context.visitOperand(value.value);
if (value.lvalue.kind === InstructionKind.Reassign) {
context.visitReassignment(value.lvalue.place);
@@ -837,8 +832,8 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
scope: context.currentScope,
});
} else if (
value.kind === "DeclareLocal" ||
value.kind === "DeclareContext"
value.kind === 'DeclareLocal' ||
value.kind === 'DeclareContext'
) {
/*
* Some variables may be declared and never initialized. We need
@@ -856,7 +851,7 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
id,
scope: context.currentScope,
});
} else if (value.kind === "Destructure") {
} else if (value.kind === 'Destructure') {
context.visitOperand(value.value);
for (const place of eachPatternOperand(value.lvalue.pattern)) {
if (value.lvalue.kind === InstructionKind.Reassign) {
@@ -878,16 +873,16 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
}
const terminal = stmt.terminal;
switch (terminal.kind) {
case "continue":
case "break": {
case 'continue':
case 'break': {
context.poisonState.addPoisonTarget(
terminal.target,
context.currentScope
context.currentScope,
);
break;
}
case "throw":
case "return": {
case 'throw':
case 'return': {
context.poisonState.addPoisonTarget(null, context.currentScope);
break;
}
@@ -901,24 +896,24 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
override visitTerminal(
stmt: ReactiveTerminalStatement,
context: Context
context: Context,
): void {
this.enterTerminal(stmt, context);
const terminal = stmt.terminal;
switch (terminal.kind) {
case "break":
case "continue": {
case 'break':
case 'continue': {
break;
}
case "return": {
case 'return': {
context.visitOperand(terminal.value);
break;
}
case "throw": {
case 'throw': {
context.visitOperand(terminal.value);
break;
}
case "for": {
case 'for': {
this.visitReactiveValue(context, terminal.id, terminal.init);
this.visitReactiveValue(context, terminal.id, terminal.test);
context.enterConditional(() => {
@@ -929,37 +924,37 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
});
break;
}
case "for-of": {
case 'for-of': {
this.visitReactiveValue(context, terminal.id, terminal.init);
context.enterConditional(() => {
this.visitBlock(terminal.loop, context);
});
break;
}
case "for-in": {
case 'for-in': {
this.visitReactiveValue(context, terminal.id, terminal.init);
context.enterConditional(() => {
this.visitBlock(terminal.loop, context);
});
break;
}
case "do-while": {
case 'do-while': {
this.visitBlock(terminal.loop, context);
context.enterConditional(() => {
this.visitReactiveValue(context, terminal.id, terminal.test);
});
break;
}
case "while": {
case 'while': {
this.visitReactiveValue(context, terminal.id, terminal.test);
context.enterConditional(() => {
this.visitBlock(terminal.loop, context);
});
break;
}
case "if": {
case 'if': {
context.visitOperand(terminal.test);
const { consequent, alternate } = terminal;
const {consequent, alternate} = terminal;
/*
* Consequent and alternate branches are mutually exclusive,
* so we save and restore the poison state here.
@@ -975,13 +970,13 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
});
context.poisonState.merge(
[ifPoisonState],
context.currentScope.value
context.currentScope.value,
);
context.promoteDepsFromExhaustiveConditionals([depsInIf, depsInElse]);
}
break;
}
case "switch": {
case 'switch': {
context.visitOperand(terminal.test);
const isDefaultOnly =
terminal.cases.length === 1 && terminal.cases[0].test == null;
@@ -1004,7 +999,7 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
* CFG representation for fallthrough. This is safe. It only
* reduces granularity of dependencies.
*/
for (const { test, block } of terminal.cases) {
for (const {test, block} of terminal.cases) {
if (test !== null) {
context.visitOperand(test);
} else {
@@ -1012,12 +1007,12 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
}
if (block !== undefined) {
mutExPoisonStates.push(
context.poisonState.take(prevPoisonState.clone())
context.poisonState.take(prevPoisonState.clone()),
);
depsInCases.push(
context.enterConditional(() => {
this.visitBlock(block, context);
})
}),
);
}
}
@@ -1026,15 +1021,15 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
}
context.poisonState.merge(
mutExPoisonStates,
context.currentScope.value
context.currentScope.value,
);
break;
}
case "label": {
case 'label': {
this.visitBlock(terminal.block, context);
break;
}
case "try": {
case 'try': {
this.visitBlock(terminal.block, context);
this.visitBlock(terminal.handler, context);
break;
@@ -1042,7 +1037,7 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
default: {
assertExhaustive(
terminal,
`Unexpected terminal kind \`${(terminal as any).kind}\``
`Unexpected terminal kind \`${(terminal as any).kind}\``,
);
}
}
@@ -9,12 +9,12 @@ import {
ReactiveFunction,
ReactiveScopeBlock,
ReactiveStatement,
} from "../HIR/HIR";
} from '../HIR/HIR';
import {
ReactiveFunctionTransform,
Transformed,
visitReactiveFunction,
} from "./visitors";
} from './visitors';
/*
* Removes *all* reactive scopes. Intended for experimentation only, to allow
@@ -28,9 +28,9 @@ export function pruneAllReactiveScopes(fn: ReactiveFunction): void {
class Transform extends ReactiveFunctionTransform<void> {
override transformScope(
scopeBlock: ReactiveScopeBlock,
state: void
state: void,
): Transformed<ReactiveStatement> {
this.visitScope(scopeBlock, state);
return { kind: "replace-many", value: scopeBlock.instructions };
return {kind: 'replace-many', value: scopeBlock.instructions};
}
}
@@ -5,18 +5,14 @@
* LICENSE file in the root directory of this source tree.
*/
import {
ReactiveFunctionTransform,
Transformed,
visitReactiveFunction,
} from ".";
import {ReactiveFunctionTransform, Transformed, visitReactiveFunction} from '.';
import {
Identifier,
ReactiveFunction,
ReactiveInstruction,
ReactiveScopeBlock,
ReactiveStatement,
} from "../HIR";
} from '../HIR';
/**
* Some instructions will *always* produce a new value, and unless memoized will *always*
@@ -38,17 +34,17 @@ class Transform extends ReactiveFunctionTransform<boolean> {
override transformInstruction(
instruction: ReactiveInstruction,
withinScope: boolean
withinScope: boolean,
): Transformed<ReactiveStatement> {
this.visitInstruction(instruction, withinScope);
const { lvalue, value } = instruction;
const {lvalue, value} = instruction;
switch (value.kind) {
case "ArrayExpression":
case "ObjectExpression":
case "JsxExpression":
case "JsxFragment":
case "NewExpression": {
case 'ArrayExpression':
case 'ObjectExpression':
case 'JsxExpression':
case 'JsxFragment':
case 'NewExpression': {
if (lvalue !== null) {
this.alwaysInvalidatingValues.add(lvalue.identifier);
if (!withinScope) {
@@ -57,7 +53,7 @@ class Transform extends ReactiveFunctionTransform<boolean> {
}
break;
}
case "StoreLocal": {
case 'StoreLocal': {
if (this.alwaysInvalidatingValues.has(value.value.identifier)) {
this.alwaysInvalidatingValues.add(value.lvalue.place.identifier);
}
@@ -66,7 +62,7 @@ class Transform extends ReactiveFunctionTransform<boolean> {
}
break;
}
case "LoadLocal": {
case 'LoadLocal': {
if (
lvalue !== null &&
this.alwaysInvalidatingValues.has(value.place.identifier)
@@ -82,12 +78,12 @@ class Transform extends ReactiveFunctionTransform<boolean> {
break;
}
}
return { kind: "keep" };
return {kind: 'keep'};
}
override transformScope(
scopeBlock: ReactiveScopeBlock,
_withinScope: boolean
_withinScope: boolean,
): Transformed<ReactiveStatement> {
this.visitScope(scopeBlock, true);
@@ -108,15 +104,15 @@ class Transform extends ReactiveFunctionTransform<boolean> {
}
}
return {
kind: "replace",
kind: 'replace',
value: {
kind: "pruned-scope",
kind: 'pruned-scope',
scope: scopeBlock.scope,
instructions: scopeBlock.instructions,
},
};
}
}
return { kind: "keep" };
return {kind: 'keep'};
}
}
@@ -11,12 +11,12 @@ import {
ReactiveFunction,
ReactiveInstruction,
ReactiveStatement,
} from "../HIR";
} from '../HIR';
import {
ReactiveFunctionTransform,
Transformed,
visitReactiveFunction,
} from "./visitors";
} from './visitors';
/*
* Prunes DeclareContexts lowered for HoistedConsts, and transforms any references back to its
@@ -32,25 +32,25 @@ type HoistedIdentifiers = Set<Identifier>;
class Visitor extends ReactiveFunctionTransform<HoistedIdentifiers> {
override transformInstruction(
instruction: ReactiveInstruction,
state: HoistedIdentifiers
state: HoistedIdentifiers,
): Transformed<ReactiveStatement> {
this.visitInstruction(instruction, state);
if (
instruction.value.kind === "DeclareContext" &&
instruction.value.lvalue.kind === "HoistedConst"
instruction.value.kind === 'DeclareContext' &&
instruction.value.lvalue.kind === 'HoistedConst'
) {
state.add(instruction.value.lvalue.place.identifier);
return { kind: "remove" };
return {kind: 'remove'};
}
if (
instruction.value.kind === "StoreContext" &&
instruction.value.kind === 'StoreContext' &&
state.has(instruction.value.lvalue.place.identifier)
) {
return {
kind: "replace",
kind: 'replace',
value: {
kind: "instruction",
kind: 'instruction',
instruction: {
...instruction,
value: {
@@ -60,13 +60,13 @@ class Visitor extends ReactiveFunctionTransform<HoistedIdentifiers> {
kind: InstructionKind.Const,
},
type: null,
kind: "StoreLocal",
kind: 'StoreLocal',
},
},
},
};
}
return { kind: "keep" };
return {kind: 'keep'};
}
}
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "../CompilerError";
import {CompilerError} from '../CompilerError';
import {
Environment,
Identifier,
@@ -20,11 +20,11 @@ import {
getHookKind,
isUseRefType,
isUseStateType,
} from "../HIR";
import { eachCallArgument, eachInstructionLValue } from "../HIR/visitors";
import DisjointSet from "../Utils/DisjointSet";
import { assertExhaustive } from "../Utils/utils";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
} from '../HIR';
import {eachCallArgument, eachInstructionLValue} from '../HIR/visitors';
import DisjointSet from '../Utils/DisjointSet';
import {assertExhaustive} from '../Utils/utils';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
/**
* This pass is built based on the observation by @jbrown215 that arguments
@@ -57,7 +57,7 @@ import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
* from the block.
*/
type CreateUpdate = "Create" | "Update" | "Unknown";
type CreateUpdate = 'Create' | 'Update' | 'Unknown';
type KindMap = Map<IdentifierId, CreateUpdate>;
@@ -70,7 +70,7 @@ class Visitor extends ReactiveFunctionVisitor<CreateUpdate> {
constructor(
env: Environment,
aliases: DisjointSet<IdentifierId>,
paths: Map<IdentifierId, Map<string, IdentifierId>>
paths: Map<IdentifierId, Map<string, IdentifierId>>,
) {
super();
this.aliases = aliases;
@@ -80,16 +80,16 @@ class Visitor extends ReactiveFunctionVisitor<CreateUpdate> {
join(values: Array<CreateUpdate>): CreateUpdate {
function join2(l: CreateUpdate, r: CreateUpdate): CreateUpdate {
if (l === "Update" || r === "Update") {
return "Update";
} else if (l === "Create" || r === "Create") {
return "Create";
} else if (l === "Unknown" || r === "Unknown") {
return "Unknown";
if (l === 'Update' || r === 'Update') {
return 'Update';
} else if (l === 'Create' || r === 'Create') {
return 'Create';
} else if (l === 'Unknown' || r === 'Unknown') {
return 'Unknown';
}
assertExhaustive(r, `Unhandled variable kind ${r}`);
}
return values.reduce(join2, "Unknown");
return values.reduce(join2, 'Unknown');
}
isCreateOnlyHook(id: Identifier): boolean {
@@ -99,11 +99,11 @@ class Visitor extends ReactiveFunctionVisitor<CreateUpdate> {
override visitPlace(
_: InstructionId,
place: Place,
state: CreateUpdate
state: CreateUpdate,
): void {
this.map.set(
place.identifier.id,
this.join([state, this.map.get(place.identifier.id) ?? "Unknown"])
this.join([state, this.map.get(place.identifier.id) ?? 'Unknown']),
);
}
@@ -114,17 +114,17 @@ class Visitor extends ReactiveFunctionVisitor<CreateUpdate> {
override visitInstruction(instruction: ReactiveInstruction): void {
const state = this.join(
[...eachInstructionLValue(instruction)].map(
(operand) => this.map.get(operand.identifier.id) ?? "Unknown"
)
operand => this.map.get(operand.identifier.id) ?? 'Unknown',
),
);
const visitCallOrMethodNonArgs = (): void => {
switch (instruction.value.kind) {
case "CallExpression": {
case 'CallExpression': {
this.visitPlace(instruction.id, instruction.value.callee, state);
break;
}
case "MethodCall": {
case 'MethodCall': {
this.visitPlace(instruction.id, instruction.value.property, state);
this.visitPlace(instruction.id, instruction.value.receiver, state);
break;
@@ -135,11 +135,11 @@ class Visitor extends ReactiveFunctionVisitor<CreateUpdate> {
const isHook = (): boolean => {
let callee = null;
switch (instruction.value.kind) {
case "CallExpression": {
case 'CallExpression': {
callee = instruction.value.callee.identifier;
break;
}
case "MethodCall": {
case 'MethodCall': {
callee = instruction.value.property.identifier;
break;
}
@@ -148,18 +148,18 @@ class Visitor extends ReactiveFunctionVisitor<CreateUpdate> {
};
switch (instruction.value.kind) {
case "CallExpression":
case "MethodCall": {
case 'CallExpression':
case 'MethodCall': {
if (
instruction.lvalue &&
this.isCreateOnlyHook(instruction.lvalue.identifier)
) {
[...eachCallArgument(instruction.value.args)].forEach((operand) =>
this.visitPlace(instruction.id, operand, "Create")
[...eachCallArgument(instruction.value.args)].forEach(operand =>
this.visitPlace(instruction.id, operand, 'Create'),
);
visitCallOrMethodNonArgs();
} else {
this.traverseInstruction(instruction, isHook() ? "Update" : state);
this.traverseInstruction(instruction, isHook() ? 'Update' : state);
}
break;
}
@@ -173,17 +173,17 @@ class Visitor extends ReactiveFunctionVisitor<CreateUpdate> {
const state = this.join(
[
...scope.scope.declarations.keys(),
...[...scope.scope.reassignments.values()].map((ident) => ident.id),
].map((id) => this.map.get(id) ?? "Unknown")
...[...scope.scope.reassignments.values()].map(ident => ident.id),
].map(id => this.map.get(id) ?? 'Unknown'),
);
super.visitScope(scope, state);
[...scope.scope.dependencies].forEach((ident) => {
[...scope.scope.dependencies].forEach(ident => {
let target: undefined | IdentifierId =
this.aliases.find(ident.identifier.id) ?? ident.identifier.id;
ident.path.forEach((key) => {
ident.path.forEach(key => {
target &&= this.paths.get(target)?.get(key);
});
if (target && this.map.get(target) === "Create") {
if (target && this.map.get(target) === 'Create') {
scope.scope.dependencies.delete(ident);
}
});
@@ -191,9 +191,9 @@ class Visitor extends ReactiveFunctionVisitor<CreateUpdate> {
override visitTerminal(
stmt: ReactiveTerminalStatement,
state: CreateUpdate
state: CreateUpdate,
): void {
CompilerError.invariant(state !== "Create", {
CompilerError.invariant(state !== 'Create', {
reason: "Visiting a terminal statement with state 'Create'",
loc: stmt.terminal.loc,
});
@@ -204,24 +204,24 @@ class Visitor extends ReactiveFunctionVisitor<CreateUpdate> {
_id: InstructionId,
_dependencies: Array<Place>,
fn: ReactiveFunction,
state: CreateUpdate
state: CreateUpdate,
): void {
visitReactiveFunction(fn, this, state);
}
}
export default function pruneInitializationDependencies(
fn: ReactiveFunction
fn: ReactiveFunction,
): void {
const [aliases, paths] = getAliases(fn);
visitReactiveFunction(fn, new Visitor(fn.env, aliases, paths), "Update");
visitReactiveFunction(fn, new Visitor(fn.env, aliases, paths), 'Update');
}
function update(
map: Map<IdentifierId, Map<string, IdentifierId>>,
key: IdentifierId,
path: string,
value: IdentifierId
value: IdentifierId,
): void {
const inner = map.get(key) ?? new Map();
inner.set(path, value);
@@ -234,43 +234,43 @@ class AliasVisitor extends ReactiveFunctionVisitor {
override visitInstruction(instr: ReactiveInstruction): void {
if (
instr.value.kind === "StoreLocal" ||
instr.value.kind === "StoreContext"
instr.value.kind === 'StoreLocal' ||
instr.value.kind === 'StoreContext'
) {
this.scopeIdentifiers.union([
instr.value.lvalue.place.identifier.id,
instr.value.value.identifier.id,
]);
} else if (
instr.value.kind === "LoadLocal" ||
instr.value.kind === "LoadContext"
instr.value.kind === 'LoadLocal' ||
instr.value.kind === 'LoadContext'
) {
instr.lvalue &&
this.scopeIdentifiers.union([
instr.lvalue.identifier.id,
instr.value.place.identifier.id,
]);
} else if (instr.value.kind === "PropertyLoad") {
} else if (instr.value.kind === 'PropertyLoad') {
instr.lvalue &&
update(
this.scopePaths,
instr.value.object.identifier.id,
instr.value.property,
instr.lvalue.identifier.id
instr.lvalue.identifier.id,
);
} else if (instr.value.kind === "PropertyStore") {
} else if (instr.value.kind === 'PropertyStore') {
update(
this.scopePaths,
instr.value.object.identifier.id,
instr.value.property,
instr.value.value.identifier.id
instr.value.value.identifier.id,
);
}
}
}
function getAliases(
fn: ReactiveFunction
fn: ReactiveFunction,
): [DisjointSet<IdentifierId>, Map<IdentifierId, Map<string, IdentifierId>>] {
const visitor = new AliasVisitor();
visitReactiveFunction(fn, visitor, null);
@@ -282,7 +282,7 @@ function getAliases(
scopePaths,
disjoint.find(key) ?? key,
path,
disjoint.find(id) ?? id
disjoint.find(id) ?? id,
);
}
}
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "../CompilerError";
import {CompilerError} from '../CompilerError';
import {
Environment,
IdentifierId,
@@ -22,17 +22,17 @@ import {
ScopeId,
getHookKind,
isMutableEffect,
} from "../HIR";
import { getFunctionCallSignature } from "../Inference/InferReferenceEffects";
import { assertExhaustive } from "../Utils/utils";
import { getPlaceScope } from "./BuildReactiveBlocks";
} from '../HIR';
import {getFunctionCallSignature} from '../Inference/InferReferenceEffects';
import {assertExhaustive} from '../Utils/utils';
import {getPlaceScope} from './BuildReactiveBlocks';
import {
ReactiveFunctionTransform,
ReactiveFunctionVisitor,
Transformed,
eachReactiveValueOperand,
visitReactiveFunction,
} from "./visitors";
} from './visitors';
/*
* This pass prunes reactive scopes that are not necessary to bound downstream computation.
@@ -114,7 +114,7 @@ export function pruneNonEscapingScopes(fn: ReactiveFunction): void {
*/
const state = new State(fn.env);
for (const param of fn.params) {
if (param.kind === "Identifier") {
if (param.kind === 'Identifier') {
state.declare(param.identifier.id);
} else {
state.declare(param.place.identifier.id);
@@ -146,19 +146,19 @@ export type MemoizationOptions = {
// Describes how to determine whether a value should be memoized, relative to dependees and dependencies
enum MemoizationLevel {
// The value should be memoized if it escapes
Memoized = "Memoized",
Memoized = 'Memoized',
/*
* Values that are memoized if their dependencies are memoized (used for logical/ternary and
* other expressions that propagate dependencies wo changing them)
*/
Conditional = "Conditional",
Conditional = 'Conditional',
/*
* Values that cannot be compared with Object.is, but which by default don't need to be memoized
* unless forced
*/
Unmemoized = "Unmemoized",
Unmemoized = 'Unmemoized',
// The value will never be memoized: used for values that can be cheaply compared w Object.is
Never = "Never",
Never = 'Never',
}
/*
@@ -167,7 +167,7 @@ enum MemoizationLevel {
*/
function joinAliases(
kind1: MemoizationLevel,
kind2: MemoizationLevel
kind2: MemoizationLevel,
): MemoizationLevel {
if (
kind1 === MemoizationLevel.Memoized ||
@@ -240,21 +240,21 @@ class State {
visitOperand(
id: InstructionId,
place: Place,
identifier: IdentifierId
identifier: IdentifierId,
): void {
const scope = getPlaceScope(id, place);
if (scope !== null) {
let node = this.scopes.get(scope.id);
if (node === undefined) {
node = {
dependencies: [...scope.dependencies].map((dep) => dep.identifier.id),
dependencies: [...scope.dependencies].map(dep => dep.identifier.id),
seen: false,
};
this.scopes.set(scope.id, node);
}
const identifierNode = this.identifiers.get(identifier);
CompilerError.invariant(identifierNode !== undefined, {
reason: "Expected identifier to be initialized",
reason: 'Expected identifier to be initialized',
description: null,
loc: place.loc,
suggestions: null,
@@ -318,7 +318,7 @@ function computeMemoizedIdentifiers(state: State): Set<IdentifierId> {
function forceMemoizeScopeDependencies(id: ScopeId): void {
const node = state.scopes.get(id);
CompilerError.invariant(node !== undefined, {
reason: "Expected a node for all scopes",
reason: 'Expected a node for all scopes',
description: null,
loc: null,
suggestions: null,
@@ -358,19 +358,19 @@ function computeMemoizationInputs(
env: Environment,
value: ReactiveValue,
lvalue: Place | null,
options: MemoizationOptions
options: MemoizationOptions,
): {
// can optionally return a custom set of lvalues per instruction
lvalues: Array<LValueMemoization>;
rvalues: Array<Place>;
} {
switch (value.kind) {
case "ConditionalExpression": {
case 'ConditionalExpression': {
return {
// Only need to memoize if the rvalues are memoized
lvalues:
lvalue !== null
? [{ place: lvalue, level: MemoizationLevel.Conditional }]
? [{place: lvalue, level: MemoizationLevel.Conditional}]
: [],
rvalues: [
// Conditionals do not alias their test value.
@@ -381,12 +381,12 @@ function computeMemoizationInputs(
],
};
}
case "LogicalExpression": {
case 'LogicalExpression': {
return {
// Only need to memoize if the rvalues are memoized
lvalues:
lvalue !== null
? [{ place: lvalue, level: MemoizationLevel.Conditional }]
? [{place: lvalue, level: MemoizationLevel.Conditional}]
: [],
rvalues: [
...computeMemoizationInputs(env, value.left, null, options).rvalues,
@@ -394,12 +394,12 @@ function computeMemoizationInputs(
],
};
}
case "SequenceExpression": {
case 'SequenceExpression': {
return {
// Only need to memoize if the rvalues are memoized
lvalues:
lvalue !== null
? [{ place: lvalue, level: MemoizationLevel.Conditional }]
? [{place: lvalue, level: MemoizationLevel.Conditional}]
: [],
/*
* Only the final value of the sequence is a true rvalue:
@@ -410,13 +410,13 @@ function computeMemoizationInputs(
.rvalues,
};
}
case "JsxExpression": {
case 'JsxExpression': {
const operands: Array<Place> = [];
if (value.tag.kind === "Identifier") {
if (value.tag.kind === 'Identifier') {
operands.push(value.tag);
}
for (const prop of value.props) {
if (prop.kind === "JsxAttribute") {
if (prop.kind === 'JsxAttribute') {
operands.push(prop.place);
} else {
operands.push(prop.argument);
@@ -435,11 +435,11 @@ function computeMemoizationInputs(
* JSX elements themselves are not memoized unless forced to
* avoid breaking downstream memoization
*/
lvalues: lvalue !== null ? [{ place: lvalue, level }] : [],
lvalues: lvalue !== null ? [{place: lvalue, level}] : [],
rvalues: operands,
};
}
case "JsxFragment": {
case 'JsxFragment': {
const level = options.memoizeJsxElements
? MemoizationLevel.Memoized
: MemoizationLevel.Unmemoized;
@@ -448,89 +448,89 @@ function computeMemoizationInputs(
* JSX elements themselves are not memoized unless forced to
* avoid breaking downstream memoization
*/
lvalues: lvalue !== null ? [{ place: lvalue, level }] : [],
lvalues: lvalue !== null ? [{place: lvalue, level}] : [],
rvalues: value.children,
};
}
case "NextPropertyOf":
case "StartMemoize":
case "FinishMemoize":
case "Debugger":
case "ComputedDelete":
case "PropertyDelete":
case "LoadGlobal":
case "MetaProperty":
case "TemplateLiteral":
case "Primitive":
case "JSXText":
case "BinaryExpression":
case "UnaryExpression": {
case 'NextPropertyOf':
case 'StartMemoize':
case 'FinishMemoize':
case 'Debugger':
case 'ComputedDelete':
case 'PropertyDelete':
case 'LoadGlobal':
case 'MetaProperty':
case 'TemplateLiteral':
case 'Primitive':
case 'JSXText':
case 'BinaryExpression':
case 'UnaryExpression': {
const level = options.forceMemoizePrimitives
? MemoizationLevel.Memoized
: MemoizationLevel.Never;
return {
// All of these instructions return a primitive value and never need to be memoized
lvalues: lvalue !== null ? [{ place: lvalue, level }] : [],
lvalues: lvalue !== null ? [{place: lvalue, level}] : [],
rvalues: [],
};
}
case "Await":
case "TypeCastExpression": {
case 'Await':
case 'TypeCastExpression': {
return {
// Indirection for the inner value, memoized if the value is
lvalues:
lvalue !== null
? [{ place: lvalue, level: MemoizationLevel.Conditional }]
? [{place: lvalue, level: MemoizationLevel.Conditional}]
: [],
rvalues: [value.value],
};
}
case "IteratorNext": {
case 'IteratorNext': {
return {
// Indirection for the inner value, memoized if the value is
lvalues:
lvalue !== null
? [{ place: lvalue, level: MemoizationLevel.Conditional }]
? [{place: lvalue, level: MemoizationLevel.Conditional}]
: [],
rvalues: [value.iterator, value.collection],
};
}
case "GetIterator": {
case 'GetIterator': {
return {
// Indirection for the inner value, memoized if the value is
lvalues:
lvalue !== null
? [{ place: lvalue, level: MemoizationLevel.Conditional }]
? [{place: lvalue, level: MemoizationLevel.Conditional}]
: [],
rvalues: [value.collection],
};
}
case "LoadLocal": {
case 'LoadLocal': {
return {
// Indirection for the inner value, memoized if the value is
lvalues:
lvalue !== null
? [{ place: lvalue, level: MemoizationLevel.Conditional }]
? [{place: lvalue, level: MemoizationLevel.Conditional}]
: [],
rvalues: [value.place],
};
}
case "LoadContext": {
case 'LoadContext': {
return {
// Should never be pruned
lvalues:
lvalue !== null
? [{ place: lvalue, level: MemoizationLevel.Conditional }]
? [{place: lvalue, level: MemoizationLevel.Conditional}]
: [],
rvalues: [value.place],
};
}
case "DeclareContext": {
case 'DeclareContext': {
const lvalues = [
{ place: value.lvalue.place, level: MemoizationLevel.Memoized },
{place: value.lvalue.place, level: MemoizationLevel.Memoized},
];
if (lvalue !== null) {
lvalues.push({ place: lvalue, level: MemoizationLevel.Unmemoized });
lvalues.push({place: lvalue, level: MemoizationLevel.Unmemoized});
}
return {
lvalues,
@@ -538,25 +538,25 @@ function computeMemoizationInputs(
};
}
case "DeclareLocal": {
case 'DeclareLocal': {
const lvalues = [
{ place: value.lvalue.place, level: MemoizationLevel.Unmemoized },
{place: value.lvalue.place, level: MemoizationLevel.Unmemoized},
];
if (lvalue !== null) {
lvalues.push({ place: lvalue, level: MemoizationLevel.Unmemoized });
lvalues.push({place: lvalue, level: MemoizationLevel.Unmemoized});
}
return {
lvalues,
rvalues: [],
};
}
case "PrefixUpdate":
case "PostfixUpdate": {
case 'PrefixUpdate':
case 'PostfixUpdate': {
const lvalues = [
{ place: value.lvalue, level: MemoizationLevel.Conditional },
{place: value.lvalue, level: MemoizationLevel.Conditional},
];
if (lvalue !== null) {
lvalues.push({ place: lvalue, level: MemoizationLevel.Conditional });
lvalues.push({place: lvalue, level: MemoizationLevel.Conditional});
}
return {
// Indirection for the inner value, memoized if the value is
@@ -564,12 +564,12 @@ function computeMemoizationInputs(
rvalues: [value.value],
};
}
case "StoreLocal": {
case 'StoreLocal': {
const lvalues = [
{ place: value.lvalue.place, level: MemoizationLevel.Conditional },
{place: value.lvalue.place, level: MemoizationLevel.Conditional},
];
if (lvalue !== null) {
lvalues.push({ place: lvalue, level: MemoizationLevel.Conditional });
lvalues.push({place: lvalue, level: MemoizationLevel.Conditional});
}
return {
// Indirection for the inner value, memoized if the value is
@@ -577,13 +577,13 @@ function computeMemoizationInputs(
rvalues: [value.value],
};
}
case "StoreContext": {
case 'StoreContext': {
// Should never be pruned
const lvalues = [
{ place: value.lvalue.place, level: MemoizationLevel.Memoized },
{place: value.lvalue.place, level: MemoizationLevel.Memoized},
];
if (lvalue !== null) {
lvalues.push({ place: lvalue, level: MemoizationLevel.Conditional });
lvalues.push({place: lvalue, level: MemoizationLevel.Conditional});
}
return {
@@ -591,10 +591,10 @@ function computeMemoizationInputs(
rvalues: [value.value],
};
}
case "StoreGlobal": {
case 'StoreGlobal': {
const lvalues = [];
if (lvalue !== null) {
lvalues.push({ place: lvalue, level: MemoizationLevel.Unmemoized });
lvalues.push({place: lvalue, level: MemoizationLevel.Unmemoized});
}
return {
@@ -602,11 +602,11 @@ function computeMemoizationInputs(
rvalues: [value.value],
};
}
case "Destructure": {
case 'Destructure': {
// Indirection for the inner value, memoized if the value is
const lvalues = [];
if (lvalue !== null) {
lvalues.push({ place: lvalue, level: MemoizationLevel.Conditional });
lvalues.push({place: lvalue, level: MemoizationLevel.Conditional});
}
lvalues.push(...computePatternLValues(value.lvalue.pattern));
return {
@@ -614,14 +614,14 @@ function computeMemoizationInputs(
rvalues: [value.value],
};
}
case "ComputedLoad":
case "PropertyLoad": {
case 'ComputedLoad':
case 'PropertyLoad': {
const level = options.forceMemoizePrimitives
? MemoizationLevel.Memoized
: MemoizationLevel.Conditional;
return {
// Indirection for the inner value, memoized if the value is
lvalues: lvalue !== null ? [{ place: lvalue, level }] : [],
lvalues: lvalue !== null ? [{place: lvalue, level}] : [],
/*
* Only the object is aliased to the result, and the result only needs to be
* memoized if the object is
@@ -629,27 +629,27 @@ function computeMemoizationInputs(
rvalues: [value.object],
};
}
case "ComputedStore": {
case 'ComputedStore': {
/*
* The object being stored to acts as an lvalue (it aliases the value), but
* the computed key is not aliased
*/
const lvalues = [
{ place: value.object, level: MemoizationLevel.Conditional },
{place: value.object, level: MemoizationLevel.Conditional},
];
if (lvalue !== null) {
lvalues.push({ place: lvalue, level: MemoizationLevel.Conditional });
lvalues.push({place: lvalue, level: MemoizationLevel.Conditional});
}
return {
lvalues,
rvalues: [value.value],
};
}
case "OptionalExpression": {
case 'OptionalExpression': {
// Indirection for the inner value, memoized if the value is
const lvalues = [];
if (lvalue !== null) {
lvalues.push({ place: lvalue, level: MemoizationLevel.Conditional });
lvalues.push({place: lvalue, level: MemoizationLevel.Conditional});
}
return {
lvalues: lvalues,
@@ -658,15 +658,15 @@ function computeMemoizationInputs(
],
};
}
case "CallExpression": {
case 'CallExpression': {
const signature = getFunctionCallSignature(
env,
value.callee.identifier.type
value.callee.identifier.type,
);
const operands = [...eachReactiveValueOperand(value)];
let lvalues = [];
if (lvalue !== null) {
lvalues.push({ place: lvalue, level: MemoizationLevel.Memoized });
lvalues.push({place: lvalue, level: MemoizationLevel.Memoized});
}
if (signature?.noAlias === true) {
return {
@@ -676,23 +676,23 @@ function computeMemoizationInputs(
}
lvalues.push(
...operands
.filter((operand) => isMutableEffect(operand.effect, operand.loc))
.map((place) => ({ place, level: MemoizationLevel.Memoized }))
.filter(operand => isMutableEffect(operand.effect, operand.loc))
.map(place => ({place, level: MemoizationLevel.Memoized})),
);
return {
lvalues,
rvalues: operands,
};
}
case "MethodCall": {
case 'MethodCall': {
const signature = getFunctionCallSignature(
env,
value.property.identifier.type
value.property.identifier.type,
);
const operands = [...eachReactiveValueOperand(value)];
let lvalues = [];
if (lvalue !== null) {
lvalues.push({ place: lvalue, level: MemoizationLevel.Memoized });
lvalues.push({place: lvalue, level: MemoizationLevel.Memoized});
}
if (signature?.noAlias === true) {
return {
@@ -702,39 +702,39 @@ function computeMemoizationInputs(
}
lvalues.push(
...operands
.filter((operand) => isMutableEffect(operand.effect, operand.loc))
.map((place) => ({ place, level: MemoizationLevel.Memoized }))
.filter(operand => isMutableEffect(operand.effect, operand.loc))
.map(place => ({place, level: MemoizationLevel.Memoized})),
);
return {
lvalues,
rvalues: operands,
};
}
case "RegExpLiteral":
case "ObjectMethod":
case "FunctionExpression":
case "TaggedTemplateExpression":
case "ArrayExpression":
case "NewExpression":
case "ObjectExpression":
case "PropertyStore": {
case 'RegExpLiteral':
case 'ObjectMethod':
case 'FunctionExpression':
case 'TaggedTemplateExpression':
case 'ArrayExpression':
case 'NewExpression':
case 'ObjectExpression':
case 'PropertyStore': {
/*
* All of these instructions may produce new values which must be memoized if
* reachable from a return value. Any mutable rvalue may alias any other rvalue
*/
const operands = [...eachReactiveValueOperand(value)];
const lvalues = operands
.filter((operand) => isMutableEffect(operand.effect, operand.loc))
.map((place) => ({ place, level: MemoizationLevel.Memoized }));
.filter(operand => isMutableEffect(operand.effect, operand.loc))
.map(place => ({place, level: MemoizationLevel.Memoized}));
if (lvalue !== null) {
lvalues.push({ place: lvalue, level: MemoizationLevel.Memoized });
lvalues.push({place: lvalue, level: MemoizationLevel.Memoized});
}
return {
lvalues,
rvalues: operands,
};
}
case "ReactiveFunctionValue": {
case 'ReactiveFunctionValue': {
CompilerError.invariant(false, {
reason: `Unexpected ReactiveFunctionValue node`,
description: null,
@@ -742,7 +742,7 @@ function computeMemoizationInputs(
suggestions: null,
});
}
case "UnsupportedNode": {
case 'UnsupportedNode': {
CompilerError.invariant(false, {
reason: `Unexpected unsupported node`,
description: null,
@@ -753,7 +753,7 @@ function computeMemoizationInputs(
default: {
assertExhaustive(
value,
`Unexpected value kind \`${(value as any).kind}\``
`Unexpected value kind \`${(value as any).kind}\``,
);
}
}
@@ -762,19 +762,19 @@ function computeMemoizationInputs(
function computePatternLValues(pattern: Pattern): Array<LValueMemoization> {
const lvalues: Array<LValueMemoization> = [];
switch (pattern.kind) {
case "ArrayPattern": {
case 'ArrayPattern': {
for (const item of pattern.items) {
if (item.kind === "Identifier") {
lvalues.push({ place: item, level: MemoizationLevel.Conditional });
} else if (item.kind === "Spread") {
lvalues.push({ place: item.place, level: MemoizationLevel.Memoized });
if (item.kind === 'Identifier') {
lvalues.push({place: item, level: MemoizationLevel.Conditional});
} else if (item.kind === 'Spread') {
lvalues.push({place: item.place, level: MemoizationLevel.Memoized});
}
}
break;
}
case "ObjectPattern": {
case 'ObjectPattern': {
for (const property of pattern.properties) {
if (property.kind === "ObjectProperty") {
if (property.kind === 'ObjectProperty') {
lvalues.push({
place: property.place,
level: MemoizationLevel.Conditional,
@@ -791,7 +791,7 @@ function computePatternLValues(pattern: Pattern): Array<LValueMemoization> {
default: {
assertExhaustive(
pattern,
`Unexpected pattern kind \`${(pattern as any).kind}\``
`Unexpected pattern kind \`${(pattern as any).kind}\``,
);
}
}
@@ -817,7 +817,7 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<State> {
override visitInstruction(
instruction: ReactiveInstruction,
state: State
state: State,
): void {
this.traverseInstruction(instruction, state);
@@ -826,7 +826,7 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<State> {
this.env,
instruction.value,
instruction.lvalue,
this.options
this.options,
);
// Associate all the rvalues with the instruction's scope if it has one
@@ -837,7 +837,7 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<State> {
}
// Add the operands as dependencies of all lvalues.
for (const { place: lvalue, level } of aliasing.lvalues) {
for (const {place: lvalue, level} of aliasing.lvalues) {
const lvalueId =
state.definitions.get(lvalue.identifier.id) ?? lvalue.identifier.id;
let node = state.identifiers.get(lvalueId);
@@ -868,23 +868,23 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<State> {
state.visitOperand(instruction.id, lvalue, lvalueId);
}
if (instruction.value.kind === "LoadLocal" && instruction.lvalue !== null) {
if (instruction.value.kind === 'LoadLocal' && instruction.lvalue !== null) {
state.definitions.set(
instruction.lvalue.identifier.id,
instruction.value.place.identifier.id
instruction.value.place.identifier.id,
);
} else if (
instruction.value.kind === "CallExpression" ||
instruction.value.kind === "MethodCall"
instruction.value.kind === 'CallExpression' ||
instruction.value.kind === 'MethodCall'
) {
let callee =
instruction.value.kind === "CallExpression"
instruction.value.kind === 'CallExpression'
? instruction.value.callee
: instruction.value.property;
if (getHookKind(state.env, callee.identifier) != null) {
const signature = getFunctionCallSignature(
this.env,
callee.identifier.type
callee.identifier.type,
);
/*
* Hook values are assumed to escape by default since they can be inputs
@@ -896,7 +896,7 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<State> {
return;
}
for (const operand of instruction.value.args) {
const place = operand.kind === "Spread" ? operand.place : operand;
const place = operand.kind === 'Spread' ? operand.place : operand;
state.escapingValues.add(place.identifier.id);
}
}
@@ -905,11 +905,11 @@ class CollectDependenciesVisitor extends ReactiveFunctionVisitor<State> {
override visitTerminal(
stmt: ReactiveTerminalStatement<ReactiveTerminal>,
state: State
state: State,
): void {
this.traverseTerminal(stmt, state);
if (stmt.terminal.kind === "return") {
if (stmt.terminal.kind === 'return') {
state.escapingValues.add(stmt.terminal.value.identifier.id);
}
}
@@ -923,7 +923,7 @@ class PruneScopesTransform extends ReactiveFunctionTransform<
override transformScope(
scopeBlock: ReactiveScopeBlock,
state: Set<IdentifierId>
state: Set<IdentifierId>,
): Transformed<ReactiveStatement> {
this.visitScope(scopeBlock, state);
@@ -941,22 +941,22 @@ class PruneScopesTransform extends ReactiveFunctionTransform<
scopeBlock.scope.reassignments.size === 0) ||
scopeBlock.scope.earlyReturnValue !== null
) {
return { kind: "keep" };
return {kind: 'keep'};
}
const hasMemoizedOutput =
Array.from(scopeBlock.scope.declarations.keys()).some((id) =>
state.has(id)
Array.from(scopeBlock.scope.declarations.keys()).some(id =>
state.has(id),
) ||
Array.from(scopeBlock.scope.reassignments).some((identifier) =>
state.has(identifier.id)
Array.from(scopeBlock.scope.reassignments).some(identifier =>
state.has(identifier.id),
);
if (hasMemoizedOutput) {
return { kind: "keep" };
return {kind: 'keep'};
} else {
this.prunedScopes.add(scopeBlock.scope.id);
return {
kind: "replace-many",
kind: 'replace-many',
value: scopeBlock.instructions,
};
}
@@ -964,7 +964,7 @@ class PruneScopesTransform extends ReactiveFunctionTransform<
override transformInstruction(
instruction: ReactiveInstruction,
state: Set<IdentifierId>
state: Set<IdentifierId>,
): Transformed<ReactiveStatement> {
this.traverseInstruction(instruction, state);
@@ -973,7 +973,7 @@ class PruneScopesTransform extends ReactiveFunctionTransform<
* need to be memoized. Remove associated `Memoize` instructions so that
* we don't report false positives on "missing" memoization of these values.
*/
if (instruction.value.kind === "FinishMemoize") {
if (instruction.value.kind === 'FinishMemoize') {
const identifier = instruction.value.decl.identifier;
if (
identifier.scope !== null &&
@@ -983,6 +983,6 @@ class PruneScopesTransform extends ReactiveFunctionTransform<
}
}
return { kind: "keep" };
return {kind: 'keep'};
}
}
@@ -11,10 +11,10 @@ import {
ReactiveInstruction,
ReactiveScopeBlock,
isStableType,
} from "../HIR";
import { eachPatternOperand } from "../HIR/visitors";
import { collectReactiveIdentifiers } from "./CollectReactiveIdentifiers";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
} from '../HIR';
import {eachPatternOperand} from '../HIR/visitors';
import {collectReactiveIdentifiers} from './CollectReactiveIdentifiers';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
/*
* PropagateScopeDependencies infers dependencies without considering whether dependencies
@@ -32,19 +32,19 @@ type ReactiveIdentifiers = Set<IdentifierId>;
class Visitor extends ReactiveFunctionVisitor<ReactiveIdentifiers> {
override visitInstruction(
instruction: ReactiveInstruction,
state: ReactiveIdentifiers
state: ReactiveIdentifiers,
): void {
this.traverseInstruction(instruction, state);
const { lvalue, value } = instruction;
const {lvalue, value} = instruction;
switch (value.kind) {
case "LoadLocal": {
case 'LoadLocal': {
if (lvalue !== null && state.has(value.place.identifier.id)) {
state.add(lvalue.identifier.id);
}
break;
}
case "StoreLocal": {
case 'StoreLocal': {
if (state.has(value.value.identifier.id)) {
state.add(value.lvalue.place.identifier.id);
if (lvalue !== null) {
@@ -53,7 +53,7 @@ class Visitor extends ReactiveFunctionVisitor<ReactiveIdentifiers> {
}
break;
}
case "Destructure": {
case 'Destructure': {
if (state.has(value.value.identifier.id)) {
for (const lvalue of eachPatternOperand(value.lvalue.pattern)) {
if (isStableType(lvalue.identifier)) {
@@ -67,7 +67,7 @@ class Visitor extends ReactiveFunctionVisitor<ReactiveIdentifiers> {
}
break;
}
case "PropertyLoad": {
case 'PropertyLoad': {
if (
lvalue !== null &&
state.has(value.object.identifier.id) &&
@@ -77,7 +77,7 @@ class Visitor extends ReactiveFunctionVisitor<ReactiveIdentifiers> {
}
break;
}
case "ComputedLoad": {
case 'ComputedLoad': {
if (
lvalue !== null &&
(state.has(value.object.identifier.id) ||
@@ -92,7 +92,7 @@ class Visitor extends ReactiveFunctionVisitor<ReactiveIdentifiers> {
override visitScope(
scopeBlock: ReactiveScopeBlock,
state: ReactiveIdentifiers
state: ReactiveIdentifiers,
): void {
this.traverseScope(scopeBlock, state);
for (const dep of scopeBlock.scope.dependencies) {
@@ -11,8 +11,8 @@ import {
Place,
ReactiveFunction,
ReactiveInstruction,
} from "../HIR/HIR";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
} from '../HIR/HIR';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
/*
* Nulls out lvalues for temporary variables that are never accessed later. This only
@@ -34,7 +34,7 @@ class Visitor extends ReactiveFunctionVisitor<LValues> {
}
override visitInstruction(
instruction: ReactiveInstruction,
state: LValues
state: LValues,
): void {
this.traverseInstruction(instruction, state);
if (
@@ -10,12 +10,12 @@ import {
ReactiveFunction,
ReactiveStatement,
ReactiveTerminalStatement,
} from "../HIR/HIR";
} from '../HIR/HIR';
import {
ReactiveFunctionTransform,
Transformed,
visitReactiveFunction,
} from "./visitors";
} from './visitors';
/*
* Flattens labeled terminals where the label is not reachable, and
@@ -31,36 +31,36 @@ type Labels = Set<BlockId>;
class Transform extends ReactiveFunctionTransform<Labels> {
override transformTerminal(
stmt: ReactiveTerminalStatement,
state: Labels
state: Labels,
): Transformed<ReactiveStatement> {
this.traverseTerminal(stmt, state);
const { terminal } = stmt;
const {terminal} = stmt;
if (
(terminal.kind === "break" || terminal.kind === "continue") &&
terminal.targetKind === "labeled"
(terminal.kind === 'break' || terminal.kind === 'continue') &&
terminal.targetKind === 'labeled'
) {
state.add(terminal.target);
}
// Is this terminal reachable via a break/continue to its label?
const isReachableLabel = stmt.label !== null && state.has(stmt.label.id);
if (stmt.terminal.kind === "label" && !isReachableLabel) {
if (stmt.terminal.kind === 'label' && !isReachableLabel) {
// Flatten labeled terminals where the label isn't necessary
const block = [...stmt.terminal.block];
const last = block.at(-1);
if (
last !== undefined &&
last.kind === "terminal" &&
last.terminal.kind === "break" &&
last.kind === 'terminal' &&
last.terminal.kind === 'break' &&
last.terminal.target === null
) {
block.pop();
}
return { kind: "replace-many", value: block };
return {kind: 'replace-many', value: block};
} else {
if (!isReachableLabel && stmt.label != null) {
stmt.label.implicit = true;
}
return { kind: "keep" };
return {kind: 'keep'};
}
}
}
@@ -10,12 +10,12 @@ import {
ReactiveScopeBlock,
ReactiveStatement,
ReactiveTerminalStatement,
} from "../HIR/HIR";
} from '../HIR/HIR';
import {
ReactiveFunctionTransform,
Transformed,
visitReactiveFunction,
} from "./visitors";
} from './visitors';
// Converts scopes without outputs into regular blocks.
export function pruneUnusedScopes(fn: ReactiveFunction): void {
@@ -31,15 +31,15 @@ type State = {
class Transform extends ReactiveFunctionTransform<State> {
override visitTerminal(stmt: ReactiveTerminalStatement, state: State): void {
this.traverseTerminal(stmt, state);
if (stmt.terminal.kind === "return") {
if (stmt.terminal.kind === 'return') {
state.hasReturnStatement = true;
}
}
override transformScope(
scopeBlock: ReactiveScopeBlock,
_state: State
_state: State,
): Transformed<ReactiveStatement> {
const scopeState: State = { hasReturnStatement: false };
const scopeState: State = {hasReturnStatement: false};
this.visitScope(scopeBlock, scopeState);
if (
!scopeState.hasReturnStatement &&
@@ -52,15 +52,15 @@ class Transform extends ReactiveFunctionTransform<State> {
!hasOwnDeclaration(scopeBlock))
) {
return {
kind: "replace",
kind: 'replace',
value: {
kind: "pruned-scope",
kind: 'pruned-scope',
scope: scopeBlock.scope,
instructions: scopeBlock.instructions,
},
};
} else {
return { kind: "keep" };
return {kind: 'keep'};
}
}
}
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { CompilerError } from "../CompilerError";
import {CompilerError} from '../CompilerError';
import {
Identifier,
IdentifierId,
@@ -21,9 +21,9 @@ import {
isPromotedJsxTemporary,
isPromotedTemporary,
makeIdentifierName,
} from "../HIR/HIR";
import { collectReferencedGlobals } from "./CollectReferencedGlobals";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
} from '../HIR/HIR';
import {collectReferencedGlobals} from './CollectReferencedGlobals';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
/**
* Ensures that each named variable in the given function has a unique name
@@ -55,11 +55,11 @@ export function renameVariables(fn: ReactiveFunction): Set<string> {
function renameVariablesImpl(
fn: ReactiveFunction,
visitor: Visitor,
scopes: Scopes
scopes: Scopes,
): void {
scopes.enter(() => {
for (const param of fn.params) {
if (param.kind === "Identifier") {
if (param.kind === 'Identifier') {
scopes.visit(param.identifier);
} else {
scopes.visit(param.place.identifier);
@@ -87,7 +87,7 @@ class Visitor extends ReactiveFunctionVisitor<Scopes> {
override visitPrunedScope(
scopeBlock: PrunedReactiveScopeBlock,
state: Scopes
state: Scopes,
): void {
this.traverseBlock(scopeBlock.instructions, state);
}
@@ -102,10 +102,10 @@ class Visitor extends ReactiveFunctionVisitor<Scopes> {
override visitValue(
id: InstructionId,
value: ReactiveValue,
state: Scopes
state: Scopes,
): void {
this.traverseValue(id, value, state);
if (value.kind === "FunctionExpression" || value.kind === "ObjectMethod") {
if (value.kind === 'FunctionExpression' || value.kind === 'ObjectMethod') {
this.visitHirFunction(value.loweredFunc.func, state);
}
}
@@ -114,7 +114,7 @@ class Visitor extends ReactiveFunctionVisitor<Scopes> {
_id: InstructionId,
_dependencies: Array<Place>,
_fn: ReactiveFunction,
_state: Scopes
_state: Scopes,
): void {
renameVariablesImpl(_fn, this, _state);
}
@@ -180,7 +180,7 @@ class Scopes {
fn();
const last = this.#stack.pop();
CompilerError.invariant(last === next, {
reason: "Mismatch push/pop calls",
reason: 'Mismatch push/pop calls',
description: null,
loc: null,
suggestions: null,
@@ -4,9 +4,9 @@ import {
ReactiveScopeBlock,
ReactiveTerminalStatement,
makeBlockId,
} from "../HIR";
import { getOrInsertDefault } from "../Utils/utils";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
} from '../HIR';
import {getOrInsertDefault} from '../Utils/utils';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
export function stabilizeBlockIds(fn: ReactiveFunction): void {
const referenced: Set<BlockId> = new Set();
@@ -22,7 +22,7 @@ export function stabilizeBlockIds(fn: ReactiveFunction): void {
class CollectReferencedLabels extends ReactiveFunctionVisitor<Set<BlockId>> {
override visitScope(scope: ReactiveScopeBlock, state: Set<BlockId>): void {
const { earlyReturnValue } = scope.scope;
const {earlyReturnValue} = scope.scope;
if (earlyReturnValue != null) {
state.add(earlyReturnValue.label);
}
@@ -30,7 +30,7 @@ class CollectReferencedLabels extends ReactiveFunctionVisitor<Set<BlockId>> {
}
override visitTerminal(
stmt: ReactiveTerminalStatement,
state: Set<BlockId>
state: Set<BlockId>,
): void {
if (stmt.label != null) {
if (!stmt.label.implicit) {
@@ -44,14 +44,14 @@ class CollectReferencedLabels extends ReactiveFunctionVisitor<Set<BlockId>> {
class RewriteBlockIds extends ReactiveFunctionVisitor<Map<BlockId, BlockId>> {
override visitScope(
scope: ReactiveScopeBlock,
state: Map<BlockId, BlockId>
state: Map<BlockId, BlockId>,
): void {
const { earlyReturnValue } = scope.scope;
const {earlyReturnValue} = scope.scope;
if (earlyReturnValue != null) {
const rewrittenId = getOrInsertDefault(
state,
earlyReturnValue.label,
state.size
state.size,
);
earlyReturnValue.label = makeBlockId(rewrittenId);
}
@@ -59,7 +59,7 @@ class RewriteBlockIds extends ReactiveFunctionVisitor<Map<BlockId, BlockId>> {
}
override visitTerminal(
stmt: ReactiveTerminalStatement,
state: Map<BlockId, BlockId>
state: Map<BlockId, BlockId>,
): void {
if (stmt.label != null) {
const rewrittenId = getOrInsertDefault(state, stmt.label.id, state.size);
@@ -67,11 +67,11 @@ class RewriteBlockIds extends ReactiveFunctionVisitor<Map<BlockId, BlockId>> {
}
const terminal = stmt.terminal;
if (terminal.kind === "break" || terminal.kind === "continue") {
if (terminal.kind === 'break' || terminal.kind === 'continue') {
const rewrittenId = getOrInsertDefault(
state,
terminal.target,
state.size
state.size,
);
terminal.target = makeBlockId(rewrittenId);
}
@@ -5,39 +5,36 @@
* LICENSE file in the root directory of this source tree.
*/
export { alignObjectMethodScopes } from "./AlignObjectMethodScopes";
export { alignReactiveScopesToBlockScopes } from "./AlignReactiveScopesToBlockScopes";
export { assertScopeInstructionsWithinScopes } from "./AssertScopeInstructionsWithinScope";
export { assertWellFormedBreakTargets } from "./AssertWellFormedBreakTargets";
export { buildReactiveBlocks } from "./BuildReactiveBlocks";
export { buildReactiveFunction } from "./BuildReactiveFunction";
export {
codegenFunction,
type CodegenFunction,
} from "./CodegenReactiveFunction";
export { extractScopeDeclarationsFromDestructuring } from "./ExtractScopeDeclarationsFromDestructuring";
export { flattenReactiveLoops } from "./FlattenReactiveLoops";
export { flattenScopesWithHooksOrUse } from "./FlattenScopesWithHooksOrUse";
export { inferReactiveScopeVariables } from "./InferReactiveScopeVariables";
export { memoizeFbtAndMacroOperandsInSameScope as memoizeFbtOperandsInSameScope } from "./MemoizeFbtAndMacroOperandsInSameScope";
export { mergeOverlappingReactiveScopes } from "./MergeOverlappingReactiveScopes";
export { mergeReactiveScopesThatInvalidateTogether } from "./MergeReactiveScopesThatInvalidateTogether";
export { printReactiveFunction } from "./PrintReactiveFunction";
export { promoteUsedTemporaries } from "./PromoteUsedTemporaries";
export { propagateEarlyReturns } from "./PropagateEarlyReturns";
export { propagateScopeDependencies } from "./PropagateScopeDependencies";
export { pruneAllReactiveScopes } from "./PruneAllReactiveScopes";
export { pruneHoistedContexts } from "./PruneHoistedContexts";
export { pruneNonEscapingScopes } from "./PruneNonEscapingScopes";
export { pruneNonReactiveDependencies } from "./PruneNonReactiveDependencies";
export { pruneTemporaryLValues as pruneUnusedLValues } from "./PruneTemporaryLValues";
export { pruneUnusedLabels } from "./PruneUnusedLabels";
export { pruneUnusedScopes } from "./PruneUnusedScopes";
export { renameVariables } from "./RenameVariables";
export { stabilizeBlockIds } from "./StabilizeBlockIds";
export {alignObjectMethodScopes} from './AlignObjectMethodScopes';
export {alignReactiveScopesToBlockScopes} from './AlignReactiveScopesToBlockScopes';
export {assertScopeInstructionsWithinScopes} from './AssertScopeInstructionsWithinScope';
export {assertWellFormedBreakTargets} from './AssertWellFormedBreakTargets';
export {buildReactiveBlocks} from './BuildReactiveBlocks';
export {buildReactiveFunction} from './BuildReactiveFunction';
export {codegenFunction, type CodegenFunction} from './CodegenReactiveFunction';
export {extractScopeDeclarationsFromDestructuring} from './ExtractScopeDeclarationsFromDestructuring';
export {flattenReactiveLoops} from './FlattenReactiveLoops';
export {flattenScopesWithHooksOrUse} from './FlattenScopesWithHooksOrUse';
export {inferReactiveScopeVariables} from './InferReactiveScopeVariables';
export {memoizeFbtAndMacroOperandsInSameScope as memoizeFbtOperandsInSameScope} from './MemoizeFbtAndMacroOperandsInSameScope';
export {mergeOverlappingReactiveScopes} from './MergeOverlappingReactiveScopes';
export {mergeReactiveScopesThatInvalidateTogether} from './MergeReactiveScopesThatInvalidateTogether';
export {printReactiveFunction} from './PrintReactiveFunction';
export {promoteUsedTemporaries} from './PromoteUsedTemporaries';
export {propagateEarlyReturns} from './PropagateEarlyReturns';
export {propagateScopeDependencies} from './PropagateScopeDependencies';
export {pruneAllReactiveScopes} from './PruneAllReactiveScopes';
export {pruneHoistedContexts} from './PruneHoistedContexts';
export {pruneNonEscapingScopes} from './PruneNonEscapingScopes';
export {pruneNonReactiveDependencies} from './PruneNonReactiveDependencies';
export {pruneTemporaryLValues as pruneUnusedLValues} from './PruneTemporaryLValues';
export {pruneUnusedLabels} from './PruneUnusedLabels';
export {pruneUnusedScopes} from './PruneUnusedScopes';
export {renameVariables} from './RenameVariables';
export {stabilizeBlockIds} from './StabilizeBlockIds';
export {
ReactiveFunctionTransform,
eachReactiveValueOperand,
visitReactiveFunction,
type Transformed,
} from "./visitors";
} from './visitors';
@@ -18,18 +18,18 @@ import {
ReactiveTerminal,
ReactiveTerminalStatement,
ReactiveValue,
} from "../HIR/HIR";
} from '../HIR/HIR';
import {
eachInstructionLValue,
eachInstructionValueOperand,
eachTerminalOperand,
} from "../HIR/visitors";
import { assertExhaustive } from "../Utils/utils";
} from '../HIR/visitors';
import {assertExhaustive} from '../Utils/utils';
export function visitReactiveFunction<TState>(
fn: ReactiveFunction,
visitor: ReactiveFunctionVisitor<TState>,
state: TState
state: TState,
): void {
visitor.visitBlock(fn.body, state);
}
@@ -43,7 +43,7 @@ export class ReactiveFunctionVisitor<TState = void> {
_id: InstructionId,
_dependencies: Array<Place>,
_fn: ReactiveFunction,
_state: TState
_state: TState,
): void {}
visitValue(id: InstructionId, value: ReactiveValue, state: TState): void {
@@ -51,34 +51,34 @@ export class ReactiveFunctionVisitor<TState = void> {
}
traverseValue(id: InstructionId, value: ReactiveValue, state: TState): void {
switch (value.kind) {
case "OptionalExpression": {
case 'OptionalExpression': {
this.visitValue(id, value.value, state);
break;
}
case "LogicalExpression": {
case 'LogicalExpression': {
this.visitValue(id, value.left, state);
this.visitValue(id, value.right, state);
break;
}
case "ConditionalExpression": {
case 'ConditionalExpression': {
this.visitValue(id, value.test, state);
this.visitValue(id, value.consequent, state);
this.visitValue(id, value.alternate, state);
break;
}
case "SequenceExpression": {
case 'SequenceExpression': {
for (const instr of value.instructions) {
this.visitInstruction(instr, state);
}
this.visitValue(value.id, value.value, state);
break;
}
case "ReactiveFunctionValue": {
case 'ReactiveFunctionValue': {
this.visitReactiveFunctionValue(
id,
value.dependencies,
value.fn,
state
state,
);
break;
}
@@ -105,24 +105,24 @@ export class ReactiveFunctionVisitor<TState = void> {
this.traverseTerminal(stmt, state);
}
traverseTerminal(stmt: ReactiveTerminalStatement, state: TState): void {
const { terminal } = stmt;
const {terminal} = stmt;
if (terminal.id !== null) {
this.visitID(terminal.id, state);
}
switch (terminal.kind) {
case "break":
case "continue": {
case 'break':
case 'continue': {
break;
}
case "return": {
case 'return': {
this.visitPlace(terminal.id, terminal.value, state);
break;
}
case "throw": {
case 'throw': {
this.visitPlace(terminal.id, terminal.value, state);
break;
}
case "for": {
case 'for': {
this.visitValue(terminal.id, terminal.init, state);
this.visitValue(terminal.id, terminal.test, state);
this.visitBlock(terminal.loop, state);
@@ -131,28 +131,28 @@ export class ReactiveFunctionVisitor<TState = void> {
}
break;
}
case "for-of": {
case 'for-of': {
this.visitValue(terminal.id, terminal.init, state);
this.visitValue(terminal.id, terminal.test, state);
this.visitBlock(terminal.loop, state);
break;
}
case "for-in": {
case 'for-in': {
this.visitValue(terminal.id, terminal.init, state);
this.visitBlock(terminal.loop, state);
break;
}
case "do-while": {
case 'do-while': {
this.visitBlock(terminal.loop, state);
this.visitValue(terminal.id, terminal.test, state);
break;
}
case "while": {
case 'while': {
this.visitValue(terminal.id, terminal.test, state);
this.visitBlock(terminal.loop, state);
break;
}
case "if": {
case 'if': {
this.visitPlace(terminal.id, terminal.test, state);
this.visitBlock(terminal.consequent, state);
if (terminal.alternate !== null) {
@@ -160,7 +160,7 @@ export class ReactiveFunctionVisitor<TState = void> {
}
break;
}
case "switch": {
case 'switch': {
this.visitPlace(terminal.id, terminal.test, state);
for (const case_ of terminal.cases) {
if (case_.test !== null) {
@@ -172,11 +172,11 @@ export class ReactiveFunctionVisitor<TState = void> {
}
break;
}
case "label": {
case 'label': {
this.visitBlock(terminal.block, state);
break;
}
case "try": {
case 'try': {
this.visitBlock(terminal.block, state);
this.visitBlock(terminal.handler, state);
break;
@@ -184,7 +184,7 @@ export class ReactiveFunctionVisitor<TState = void> {
default: {
assertExhaustive(
terminal,
`Unexpected terminal kind \`${(terminal as any).kind}\``
`Unexpected terminal kind \`${(terminal as any).kind}\``,
);
}
}
@@ -202,7 +202,7 @@ export class ReactiveFunctionVisitor<TState = void> {
}
traversePrunedScope(
scopeBlock: PrunedReactiveScopeBlock,
state: TState
state: TState,
): void {
this.visitBlock(scopeBlock.instructions, state);
}
@@ -213,26 +213,26 @@ export class ReactiveFunctionVisitor<TState = void> {
traverseBlock(block: ReactiveBlock, state: TState): void {
for (const instr of block) {
switch (instr.kind) {
case "instruction": {
case 'instruction': {
this.visitInstruction(instr.instruction, state);
break;
}
case "scope": {
case 'scope': {
this.visitScope(instr, state);
break;
}
case "pruned-scope": {
case 'pruned-scope': {
this.visitPrunedScope(instr, state);
break;
}
case "terminal": {
case 'terminal': {
this.visitTerminal(instr, state);
break;
}
default: {
assertExhaustive(
instr,
`Unexpected instruction kind \`${(instr as any).kind}\``
`Unexpected instruction kind \`${(instr as any).kind}\``,
);
}
}
@@ -241,15 +241,15 @@ export class ReactiveFunctionVisitor<TState = void> {
visitHirFunction(fn: HIRFunction, state: TState): void {
for (const param of fn.params) {
const place = param.kind === "Identifier" ? param : param.place;
const place = param.kind === 'Identifier' ? param : param.place;
this.visitParam(place, state);
}
for (const [, block] of fn.body.blocks) {
for (const instr of block.instructions) {
this.visitInstruction(instr, state);
if (
instr.value.kind === "FunctionExpression" ||
instr.value.kind === "ObjectMethod"
instr.value.kind === 'FunctionExpression' ||
instr.value.kind === 'ObjectMethod'
) {
this.visitHirFunction(instr.value.loweredFunc.func, state);
}
@@ -262,14 +262,14 @@ export class ReactiveFunctionVisitor<TState = void> {
}
export type TransformedValue =
| { kind: "keep" }
| { kind: "replace"; value: ReactiveValue };
| {kind: 'keep'}
| {kind: 'replace'; value: ReactiveValue};
export type Transformed<T> =
| { kind: "remove" }
| { kind: "keep" }
| { kind: "replace"; value: T }
| { kind: "replace-many"; value: Array<T> };
| {kind: 'remove'}
| {kind: 'keep'}
| {kind: 'replace'; value: T}
| {kind: 'replace-many'; value: Array<T>};
export class ReactiveFunctionTransform<
TState = void,
@@ -280,48 +280,48 @@ export class ReactiveFunctionTransform<
const instr = block[i]!;
let transformed: Transformed<ReactiveStatement>;
switch (instr.kind) {
case "instruction": {
case 'instruction': {
transformed = this.transformInstruction(instr.instruction, state);
break;
}
case "scope": {
case 'scope': {
transformed = this.transformScope(instr, state);
break;
}
case "pruned-scope": {
case 'pruned-scope': {
transformed = this.transformPrunedScope(instr, state);
break;
}
case "terminal": {
case 'terminal': {
transformed = this.transformTerminal(instr, state);
break;
}
default: {
assertExhaustive(
instr,
`Unexpected instruction kind \`${(instr as any).kind}\``
`Unexpected instruction kind \`${(instr as any).kind}\``,
);
}
}
switch (transformed.kind) {
case "keep": {
case 'keep': {
if (nextBlock !== null) {
nextBlock.push(instr);
}
break;
}
case "remove": {
case 'remove': {
if (nextBlock === null) {
nextBlock = block.slice(0, i);
}
break;
}
case "replace": {
case 'replace': {
nextBlock ??= block.slice(0, i);
nextBlock.push(transformed.value);
break;
}
case "replace-many": {
case 'replace-many': {
nextBlock ??= block.slice(0, i);
nextBlock.push(...transformed.value);
break;
@@ -336,112 +336,112 @@ export class ReactiveFunctionTransform<
transformInstruction(
instruction: ReactiveInstruction,
state: TState
state: TState,
): Transformed<ReactiveStatement> {
this.visitInstruction(instruction, state);
return { kind: "keep" };
return {kind: 'keep'};
}
transformTerminal(
stmt: ReactiveTerminalStatement,
state: TState
state: TState,
): Transformed<ReactiveStatement> {
this.visitTerminal(stmt, state);
return { kind: "keep" };
return {kind: 'keep'};
}
transformScope(
scope: ReactiveScopeBlock,
state: TState
state: TState,
): Transformed<ReactiveStatement> {
this.visitScope(scope, state);
return { kind: "keep" };
return {kind: 'keep'};
}
transformPrunedScope(
scope: PrunedReactiveScopeBlock,
state: TState
state: TState,
): Transformed<ReactiveStatement> {
this.visitPrunedScope(scope, state);
return { kind: "keep" };
return {kind: 'keep'};
}
transformValue(
id: InstructionId,
value: ReactiveValue,
state: TState
state: TState,
): TransformedValue {
this.visitValue(id, value, state);
return { kind: "keep" };
return {kind: 'keep'};
}
transformReactiveFunctionValue(
id: InstructionId,
dependencies: Array<Place>,
fn: ReactiveFunction,
state: TState
): { kind: "keep" } | { kind: "replace"; value: ReactiveFunction } {
state: TState,
): {kind: 'keep'} | {kind: 'replace'; value: ReactiveFunction} {
this.visitReactiveFunctionValue(id, dependencies, fn, state);
return { kind: "keep" };
return {kind: 'keep'};
}
override traverseValue(
id: InstructionId,
value: ReactiveValue,
state: TState
state: TState,
): void {
switch (value.kind) {
case "OptionalExpression": {
case 'OptionalExpression': {
const nextValue = this.transformValue(id, value.value, state);
if (nextValue.kind === "replace") {
if (nextValue.kind === 'replace') {
value.value = nextValue.value;
}
break;
}
case "LogicalExpression": {
case 'LogicalExpression': {
const left = this.transformValue(id, value.left, state);
if (left.kind === "replace") {
if (left.kind === 'replace') {
value.left = left.value;
}
const right = this.transformValue(id, value.right, state);
if (right.kind === "replace") {
if (right.kind === 'replace') {
value.right = right.value;
}
break;
}
case "ConditionalExpression": {
case 'ConditionalExpression': {
const test = this.transformValue(id, value.test, state);
if (test.kind === "replace") {
if (test.kind === 'replace') {
value.test = test.value;
}
const consequent = this.transformValue(id, value.consequent, state);
if (consequent.kind === "replace") {
if (consequent.kind === 'replace') {
value.consequent = consequent.value;
}
const alternate = this.transformValue(id, value.alternate, state);
if (alternate.kind === "replace") {
if (alternate.kind === 'replace') {
value.alternate = alternate.value;
}
break;
}
case "SequenceExpression": {
case 'SequenceExpression': {
for (const instr of value.instructions) {
this.visitInstruction(instr, state);
}
const nextValue = this.transformValue(value.id, value.value, state);
if (nextValue.kind === "replace") {
if (nextValue.kind === 'replace') {
value.value = nextValue.value;
}
break;
}
case "ReactiveFunctionValue": {
case 'ReactiveFunctionValue': {
const nextValue = this.transformReactiveFunctionValue(
id,
value.dependencies,
value.fn,
state
state,
);
if (nextValue.kind === "replace") {
if (nextValue.kind === 'replace') {
value.fn = nextValue.value;
}
break;
@@ -456,7 +456,7 @@ export class ReactiveFunctionTransform<
override traverseInstruction(
instruction: ReactiveInstruction,
state: TState
state: TState,
): void {
this.visitID(instruction.id, state);
for (const operand of eachInstructionLValue(instruction)) {
@@ -465,93 +465,93 @@ export class ReactiveFunctionTransform<
const nextValue = this.transformValue(
instruction.id,
instruction.value,
state
state,
);
if (nextValue.kind === "replace") {
if (nextValue.kind === 'replace') {
instruction.value = nextValue.value;
}
}
override traverseTerminal(
stmt: ReactiveTerminalStatement,
state: TState
state: TState,
): void {
const { terminal } = stmt;
const {terminal} = stmt;
if (terminal.id !== null) {
this.visitID(terminal.id, state);
}
switch (terminal.kind) {
case "break":
case "continue": {
case 'break':
case 'continue': {
break;
}
case "return": {
case 'return': {
this.visitPlace(terminal.id, terminal.value, state);
break;
}
case "throw": {
case 'throw': {
this.visitPlace(terminal.id, terminal.value, state);
break;
}
case "for": {
case 'for': {
const init = this.transformValue(terminal.id, terminal.init, state);
if (init.kind === "replace") {
if (init.kind === 'replace') {
terminal.init = init.value;
}
const test = this.transformValue(terminal.id, terminal.test, state);
if (test.kind === "replace") {
if (test.kind === 'replace') {
terminal.test = test.value;
}
if (terminal.update !== null) {
const update = this.transformValue(
terminal.id,
terminal.update,
state
state,
);
if (update.kind === "replace") {
if (update.kind === 'replace') {
terminal.update = update.value;
}
}
this.visitBlock(terminal.loop, state);
break;
}
case "for-of": {
case 'for-of': {
const init = this.transformValue(terminal.id, terminal.init, state);
if (init.kind === "replace") {
if (init.kind === 'replace') {
terminal.init = init.value;
}
const test = this.transformValue(terminal.id, terminal.test, state);
if (test.kind === "replace") {
if (test.kind === 'replace') {
terminal.test = test.value;
}
this.visitBlock(terminal.loop, state);
break;
}
case "for-in": {
case 'for-in': {
const init = this.transformValue(terminal.id, terminal.init, state);
if (init.kind === "replace") {
if (init.kind === 'replace') {
terminal.init = init.value;
}
this.visitBlock(terminal.loop, state);
break;
}
case "do-while": {
case 'do-while': {
this.visitBlock(terminal.loop, state);
const test = this.transformValue(terminal.id, terminal.test, state);
if (test.kind === "replace") {
if (test.kind === 'replace') {
terminal.test = test.value;
}
break;
}
case "while": {
case 'while': {
const test = this.transformValue(terminal.id, terminal.test, state);
if (test.kind === "replace") {
if (test.kind === 'replace') {
terminal.test = test.value;
}
this.visitBlock(terminal.loop, state);
break;
}
case "if": {
case 'if': {
this.visitPlace(terminal.id, terminal.test, state);
this.visitBlock(terminal.consequent, state);
if (terminal.alternate !== null) {
@@ -559,7 +559,7 @@ export class ReactiveFunctionTransform<
}
break;
}
case "switch": {
case 'switch': {
this.visitPlace(terminal.id, terminal.test, state);
for (const case_ of terminal.cases) {
if (case_.test !== null) {
@@ -571,11 +571,11 @@ export class ReactiveFunctionTransform<
}
break;
}
case "label": {
case 'label': {
this.visitBlock(terminal.block, state);
break;
}
case "try": {
case 'try': {
this.visitBlock(terminal.block, state);
if (terminal.handlerBinding !== null) {
this.visitPlace(terminal.id, terminal.handlerBinding, state);
@@ -586,7 +586,7 @@ export class ReactiveFunctionTransform<
default: {
assertExhaustive(
terminal,
`Unexpected terminal kind \`${(terminal as any).kind}\``
`Unexpected terminal kind \`${(terminal as any).kind}\``,
);
}
}
@@ -594,32 +594,32 @@ export class ReactiveFunctionTransform<
}
export function* eachReactiveValueOperand(
instrValue: ReactiveValue
instrValue: ReactiveValue,
): Iterable<Place> {
switch (instrValue.kind) {
case "OptionalExpression": {
case 'OptionalExpression': {
yield* eachReactiveValueOperand(instrValue.value);
break;
}
case "LogicalExpression": {
case 'LogicalExpression': {
yield* eachReactiveValueOperand(instrValue.left);
yield* eachReactiveValueOperand(instrValue.right);
break;
}
case "SequenceExpression": {
case 'SequenceExpression': {
for (const instr of instrValue.instructions) {
yield* eachReactiveValueOperand(instr.value);
}
yield* eachReactiveValueOperand(instrValue.value);
break;
}
case "ConditionalExpression": {
case 'ConditionalExpression': {
yield* eachReactiveValueOperand(instrValue.test);
yield* eachReactiveValueOperand(instrValue.consequent);
yield* eachReactiveValueOperand(instrValue.alternate);
break;
}
case "ReactiveFunctionValue": {
case 'ReactiveFunctionValue': {
yield* instrValue.dependencies;
break;
}
@@ -631,40 +631,40 @@ export function* eachReactiveValueOperand(
export function mapTerminalBlocks(
terminal: ReactiveTerminal,
fn: (block: ReactiveBlock) => ReactiveBlock
fn: (block: ReactiveBlock) => ReactiveBlock,
): void {
switch (terminal.kind) {
case "break":
case "continue":
case "return":
case "throw": {
case 'break':
case 'continue':
case 'return':
case 'throw': {
break;
}
case "for": {
case 'for': {
terminal.loop = fn(terminal.loop);
break;
}
case "for-of": {
case 'for-of': {
terminal.loop = fn(terminal.loop);
break;
}
case "for-in": {
case 'for-in': {
terminal.loop = fn(terminal.loop);
break;
}
case "do-while":
case "while": {
case 'do-while':
case 'while': {
terminal.loop = fn(terminal.loop);
break;
}
case "if": {
case 'if': {
terminal.consequent = fn(terminal.consequent);
if (terminal.alternate !== null) {
terminal.alternate = fn(terminal.alternate);
}
break;
}
case "switch": {
case 'switch': {
for (const case_ of terminal.cases) {
if (case_.block !== undefined) {
case_.block = fn(case_.block);
@@ -672,11 +672,11 @@ export function mapTerminalBlocks(
}
break;
}
case "label": {
case 'label': {
terminal.block = fn(terminal.block);
break;
}
case "try": {
case 'try': {
terminal.block = fn(terminal.block);
terminal.handler = fn(terminal.handler);
break;
@@ -684,7 +684,7 @@ export function mapTerminalBlocks(
default: {
assertExhaustive(
terminal,
`Unexpected terminal kind \`${(terminal as any).kind}\``
`Unexpected terminal kind \`${(terminal as any).kind}\``,
);
}
}