import {ReactiveFunctionTransform, Transformed, visitReactiveFunction} from '.';
import {
Identifier,
ReactiveFunction,
ReactiveInstruction,
ReactiveScopeBlock,
ReactiveStatement,
} from '../HIR';
export function pruneAlwaysInvalidatingScopes(fn: ReactiveFunction): void {
visitReactiveFunction(fn, new Transform(), false);
}
class Transform extends ReactiveFunctionTransform<boolean> {
alwaysInvalidatingValues: Set<Identifier> = new Set();
unmemoizedValues: Set<Identifier> = new Set();
override transformInstruction(
instruction: ReactiveInstruction,
withinScope: boolean,
): Transformed<ReactiveStatement> {
this.visitInstruction(instruction, withinScope);
const {lvalue, value} = instruction;
switch (value.kind) {
case 'ArrayExpression':
case 'ObjectExpression':
case 'JsxExpression':
case 'JsxFragment':
case 'NewExpression': {
if (lvalue !== null) {
this.alwaysInvalidatingValues.add(lvalue.identifier);
if (!withinScope) {
this.unmemoizedValues.add(lvalue.identifier);
}
}
break;
}
case 'StoreLocal': {
if (this.alwaysInvalidatingValues.has(value.value.identifier)) {
this.alwaysInvalidatingValues.add(value.lvalue.place.identifier);
}
if (this.unmemoizedValues.has(value.value.identifier)) {
this.unmemoizedValues.add(value.lvalue.place.identifier);
}
break;
}
case 'LoadLocal': {
if (
lvalue !== null &&
this.alwaysInvalidatingValues.has(value.place.identifier)
) {
this.alwaysInvalidatingValues.add(lvalue.identifier);
}
if (
lvalue !== null &&
this.unmemoizedValues.has(value.place.identifier)
) {
this.unmemoizedValues.add(lvalue.identifier);
}
break;
}
}
return {kind: 'keep'};
}
override transformScope(
scopeBlock: ReactiveScopeBlock,
_withinScope: boolean,
): Transformed<ReactiveStatement> {
this.visitScope(scopeBlock, true);
for (const dep of scopeBlock.scope.dependencies) {
if (this.unmemoizedValues.has(dep.identifier)) {
for (const [_, decl] of scopeBlock.scope.declarations) {
if (this.alwaysInvalidatingValues.has(decl.identifier)) {
this.unmemoizedValues.add(decl.identifier);
}
}
for (const identifier of scopeBlock.scope.reassignments) {
if (this.alwaysInvalidatingValues.has(identifier)) {
this.unmemoizedValues.add(identifier);
}
}
return {
kind: 'replace',
value: {
kind: 'pruned-scope',
scope: scopeBlock.scope,
instructions: scopeBlock.instructions,
},
};
}
}
return {kind: 'keep'};
}
}