import {CompilerError} from '..';
import {
GeneratedSource,
HIRFunction,
Identifier,
ReactiveScope,
makeInstructionId,
} from '../HIR';
import {eachInstructionValueOperand} from '../HIR/visitors';
import DisjointSet from '../Utils/DisjointSet';
function findScopesToMerge(fn: HIRFunction): DisjointSet<ReactiveScope> {
const objectMethodDecls: Set<Identifier> = new Set();
const mergeScopesBuilder = new DisjointSet<ReactiveScope>();
for (const [_, block] of fn.body.blocks) {
for (const {lvalue, value} of block.instructions) {
if (value.kind === 'ObjectMethod') {
objectMethodDecls.add(lvalue.identifier);
} else if (value.kind === 'ObjectExpression') {
for (const operand of eachInstructionValueOperand(value)) {
if (objectMethodDecls.has(operand.identifier)) {
const operandScope = operand.identifier.scope;
const lvalueScope = lvalue.identifier.scope;
CompilerError.invariant(
operandScope != null && lvalueScope != null,
{
reason:
'Internal error: Expected all ObjectExpressions and ObjectMethods to have non-null scope.',
suggestions: null,
loc: GeneratedSource,
},
);
mergeScopesBuilder.union([operandScope, lvalueScope]);
}
}
}
}
}
return mergeScopesBuilder;
}
export function alignObjectMethodScopes(fn: HIRFunction): void {
for (const [_, block] of fn.body.blocks) {
for (const {value} of block.instructions) {
if (
value.kind === 'ObjectMethod' ||
value.kind === 'FunctionExpression'
) {
alignObjectMethodScopes(value.loweredFunc.func);
}
}
}
const scopeGroupsMap = findScopesToMerge(fn).canonicalize();
for (const [scope, root] of scopeGroupsMap) {
if (scope !== root) {
root.range.start = makeInstructionId(
Math.min(scope.range.start, root.range.start),
);
root.range.end = makeInstructionId(
Math.max(scope.range.end, root.range.end),
);
}
}
for (const [_, block] of fn.body.blocks) {
for (const {
lvalue: {identifier},
} of block.instructions) {
if (identifier.scope != null) {
const root = scopeGroupsMap.get(identifier.scope);
if (root != null) {
identifier.scope = root;
}
}
}
}
}