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;
}
}
}
}
}