import {
IdentifierId,
ReactiveFunction,
ReactiveInstruction,
ReactiveScopeBlock,
isDispatcherType,
isSetStateType,
} from "../HIR";
import { eachPatternOperand } from "../HIR/visitors";
import { collectReactiveIdentifiers } from "./CollectReactiveIdentifiers";
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
export function pruneNonReactiveDependencies(fn: ReactiveFunction): void {
const reactiveIdentifiers = collectReactiveIdentifiers(fn);
visitReactiveFunction(fn, new Visitor(), reactiveIdentifiers);
}
type ReactiveIdentifiers = Set<IdentifierId>;
class Visitor extends ReactiveFunctionVisitor<ReactiveIdentifiers> {
override visitInstruction(
instruction: ReactiveInstruction,
state: ReactiveIdentifiers
): void {
this.traverseInstruction(instruction, state);
const { lvalue, value } = instruction;
switch (value.kind) {
case "LoadLocal": {
if (lvalue !== null && state.has(value.place.identifier.id)) {
state.add(lvalue.identifier.id);
}
break;
}
case "StoreLocal": {
if (state.has(value.value.identifier.id)) {
state.add(value.lvalue.place.identifier.id);
if (lvalue !== null) {
state.add(lvalue.identifier.id);
}
}
break;
}
case "Destructure": {
if (state.has(value.value.identifier.id)) {
for (const lvalue of eachPatternOperand(value.lvalue.pattern)) {
if (
isSetStateType(lvalue.identifier) ||
isDispatcherType(lvalue.identifier)
) {
continue;
}
state.add(lvalue.identifier.id);
}
if (lvalue !== null) {
state.add(lvalue.identifier.id);
}
}
break;
}
case "PropertyLoad": {
if (
lvalue !== null &&
state.has(value.object.identifier.id) &&
!isSetStateType(lvalue.identifier)
) {
state.add(lvalue.identifier.id);
}
break;
}
case "ComputedLoad": {
if (
lvalue !== null &&
(state.has(value.object.identifier.id) ||
state.has(value.property.identifier.id))
) {
state.add(lvalue.identifier.id);
}
break;
}
}
}
override visitScope(
scopeBlock: ReactiveScopeBlock,
state: ReactiveIdentifiers
): void {
this.traverseScope(scopeBlock, state);
for (const dep of scopeBlock.scope.dependencies) {
const isReactive = state.has(dep.identifier.id);
if (!isReactive) {
scopeBlock.scope.dependencies.delete(dep);
}
}
if (scopeBlock.scope.dependencies.size !== 0) {
for (const [, declaration] of scopeBlock.scope.declarations) {
state.add(declaration.identifier.id);
}
for (const reassignment of scopeBlock.scope.reassignments) {
state.add(reassignment.id);
}
}
}
}